Featured FREE Whitepapers

What's New Here?

software-development-2-logo

An unambiguous software version scheme

When people talk about software versioning schemes they often refer to the commonly used X.Y.Z numerical scheme for versioning. This is often referred to major.minor.build, but these abstract terms are not useful as they don’t explicitly impart any meaning to each numerical component. This can lead to the simplest usage, we just increment the last number for each release, so I’ve seen versions such as 1.0.35. Alternatively, versions become a time consuming point of debate. This is a shame as we could impart some clear and useful information with versions. I’m going to suggest that rather than thinking ‘X.Y.Z’ we think ‘api.feature.bug’. What do I mean by this? You increment the appropriate number for what your release contains. For example, if you have only fixed bugs, you increment the last number. If you introduce even one new feature, then you increment the middle number. If you change a published or documented API, be that the interface of a package, a SOAP or other XML API, or possibly the user interface (in a loose sense of the term ‘API’) then the first number. This system is unambiguous, no need for discussions about the numbering. You zero digits to the right of any you increment, so if you fix a bug and introduce a new feature after version 5.3.6 then the new version is 5.4.0. Unstated digits are assumed to be zero, so 5.4.0 is the same as 5.4.0.0 and 5.4.0.0.0.0.0.0.0… The version is not a number, and it does not have digits. The version 5.261.67 is pretty unusual, but not invalid. Don’t let it put you off. You might need to change an API due to bug fix, but you’ll need to be diligent, and cold to any politicking by increasing the API digit. Otherwise the scheme looses value and you might as well just use a single number for versioning. What if you’re on version 5 of the product and the product lead has told everyone version 6 will be something special, but you need to fix a bug that means an API change? You need a hybrid version system, which consists of the external ‘product version’ and the internal ‘software version’. What about branching for production support? Technically no features, but quite possibly one branch per customer. CVS has a suitable system, take the version of the release, append two digits, the first to indicate the branch, the second for the fix number. For example, if you branch from 5.4.0 then the first release will be 5.4.1.0, the next branch’s second release would be 5.4.2.1. Reference: An unambiguous software version scheme from our JCG partner Alex Collins at the Alex Collins ‘s blog blog....
android-logo

Android: Level Two Image Cache

In the mobile world, it’s very common to have scrollable lists of items that contain information and an image or two. To make these lists performance well, most apps follow a lazy loading approach, which simply grabs and displays images in these types of lists. This approach works great for getting images into the system initially. However, there are still a few problems with this approach. The app must re-download each image every time the images need to appear to the user in the list. This creates a pretty bad experience for the user. The first time the user sees the list, s/he has to wait several seconds (or minutes with a bad network connection) before s/he sees the complete list with images. But the real pain comes when the user scrolls to a different part of the list and then scrolls back. This action causes the entire image download process to restart!We can remove the negative user experience by using a image cache.An image cache allows us to store images on the device that have been recently downloaded. By storing them on the device, we can grab them from memory instead of asking the server for them again. This can save us performance in several different ways, most notably:Images that have already been downloaded appear almost instantly, which makes the UI much more snappier Battery Life is saved by not having to go to the network for the imagesThere are some design considerations when using a cache. Since the cache is using memory on the device, it is fairly limited on space. This means we can only have a certain number of images in the cache, so it’s really important to make sure we keep the correct images stored there. “Correct” is a very relative term, which can mean several different things based on what problem is at hand. As you can see here, there are several different types of caching algorithms that attempt to define “correct” for different problems. In our case, “correct” means we want the images that are used the most in the cache. Luckily for us, the type of cache we need is simple and one of the most commonly used. The LRU Cache keeps the most frequently used images in memory, while discarding the least used images. And even luckier, the Android SDK has a LRUCache implementation, which was added in Honey Comb (its also available on the Support Library if you need to support older versions as well). Using a LRUCache that is Stored on the Disk A LRU Cache allows you to save images in memory instead of going to a server every time. This allows your app to respond much quicker to changes and save some battery life. One of the limits of the cache is the amount of memory you can use to actually store the images. This space is very constrained, especially on mobile devices. However, you do have access to one data store that has considerably more space: the disk.The disk on a mobile device is usually much larger than the main memory. Although disk access is much slower than main memory, it is still much faster than going to the network for an image, and you still get the battery life savings by not going to the network. For an excellent Disk LRU Cache implementation that works great with Android, check out Jake Wharton’s DiskLRUCache on GitHub. Combining Memory and Disk Caches Although both of the previous caches (memory LRUCache and disk LRUCache) work well independently, they work every better when combined. By using both caches at the same time, you get the best of both worlds:increased loading speed of the main memory cache increased cache size with the disk cacheCombining the two caches is fairly straight forward.Google provides some excellent example code for both the memory and disk caches here. All you have to do now is take the two different cache implementations and use the above flow chart to put them together to create a Level 2 image cache in Android! Reference: Level Two Image Cache in Android from our JCG partner Isaac Taylor at the Programming Mobile blog....
oracle-weblogic-logo

Running RichFaces on WebLogic 12c

I initially thought I could write this post months back already. But I ended up being overwhelmed by different things. One among them was, that it wasn’t able to simply fire up the RichFaces showcase like I did it for the 4.0 release. With all the JMS magic and the different provider checks in the showcase this has become some kind of a challenge to simply build and deploy it. Anyway, I was willing to give this a try and here we go. If you want to get started with any of the JBoss technologies it is a good idea to check with the JBoss Developer Framework first. That is a nice collection of different examples and quickstarts to get you started on Java EE and it’s technologies. One of them is the RichFaces-Validation example which demonstrates how to use JSF 2.0, RichFaces 4.2, CDI 1.0, JPA 2.0 and Bean Validation 1.0 together. The ExampleThe example consists of a Member entity which has some JSR-303 (Bean Validation) constraints on it. Usually those are checked in several places, beginning with the Database, on to the persistence layer and finally the view layer in close interaction with the client. Even if this quickguide doesn’t contain a persistence layer it starts with the Enity which reflects the real life situation quite good. The application contains a view layer written using JSF and RichFaces and includes an AJAX wizard for new member registration. A newly registered member needs to provide a couple of information before he is actually ‘registered’. This includes e-mail a name and his phone number. Getting Started I’m not going to repeat what the excellent and detailed quickstart is already showing you. So, if you want to run this on JBoss AS7 .. go there. We’re starting with a blank Maven web-project. And the best and easiest way to do this is to fire up NetBeans 7.2 and create one. Lets name it ‘richwls-web’. Open your pom.xml and start changing some stuff there. First remove the endorsed stuff there. We don’t need it. Next is to add a little bit of dependencyManagement: <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.bom</groupId> <artifactId>jboss-javaee-6.0-with-tools</artifactId> <version>1.0.0.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.richfaces</groupId> <artifactId>richfaces-bom</artifactId> <version>4.2.0.Final</version> <scope>import</scope> <type>pom</type> </dependency> </dependencies> </dependencyManagement>This adds the Bill of Materials (BOM) for both Java EE 6 and RichFaces to your project. A BOM specifies the versions of a ‘stack’ (or a collection) of artifacts. You find it with anything from the RedHat guys and it is considered ‘best practice’ to have one. At the end this makes your life easier because it manages versions and dependencies for you. On to the lengthy list of true dependencies: <!-- Import the CDI API --> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <scope>provided</scope> </dependency> <!-- Import the JPA API --> <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0.2</version> <scope>provided</scope> </dependency> <!-- JSR-303 (Bean Validation) Implementation --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.3.0.Final</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </exclusion> </exclusions> </dependency> <!-- Import the JSF API --> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <!-- Import RichFaces runtime dependencies - these will be included as libraries in the WAR --> <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-components-ui</artifactId> </dependency> <dependency> <groupId>org.richfaces.core</groupId> <artifactId>richfaces-core-impl</artifactId> </dependency>Except the RichFaces dependencies all others are provided by the runtime. In this case it will be GlassFish 3.1.2.2. In case you haven’t defined it elsewhere (settings.xml) you should also add the JBoss repository to your build section: <repository> <id>jboss-public-repository-group</id> <name>JBoss Public Maven Repository Group</name> <url>https://repository.jboss.org/nexus/content/groups/public-jboss/</url> </repository>Copy the contents of the richfaces-validation directory of the source-zip or check it out from github. Be a little bit careful and don’t mess up with the pom.xml we created ;) Build it and get that stuff deployed. Issues First thing you are greeted with is a nice little weld message: WELD-000054 Producers cannot produce non-serializable instances for injection into non-transient fields of passivating beans [...] Producer Method [Logger] with qualifiersWe obviously have an issue here and need to declare the Logger field as transient. @Inject private transient Logger logger;Don’t know why this works on AS7 but might be I find out someday :) Next iteration: Change it, build, deploy. java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableSet;That doesn’t look better. Fire up the WLS CAT at http://localhost:7001/wls-cat/ and try to find out about it.Seems as if Oracle is using Google magic inside the server. Ok, fine. We have no way to deploy RichFaces as a standalone war on WebLogic because we need to resolve some classloading issues here. And the recommended way is to add a so-called Filtering Classloader. You do this by adding a weblogic-application.xml to your ear. Yeah: Lets repackage everything and put the war into an empty ear and add the magic to the weblogic-application.xml: <prefer-application-packages> <package-name>com.google.common.*</package-name> </prefer-application-packages>Done? Another deployment and you finally see your application. Basically RichFaces run on WebLogic but you have to package it into an ear and turn the classloader around for the com.google.common.* classes. That is way easier with PrimeFaces but … anyway, there are reasons why I tried this. One is, that I do like the idea of being able to trigger the Bean Validation on the client side. If you take a look at the example you see, that the <rich:validator event=’blur’ /> adds client side validation for both bean validation constraints and standard jsf validators to the client. Without having to mess around with anything in JavaScript or duplicate logic. Happy coding and don’t forget to share! Reference: Running RichFaces on WebLogic 12c from our JCG partner Markus Eisele at the Enterprise Software Development with Java blog....
career-logo

How To Disrupt Technical Recruiting – Hire an Agent

A recent anti-recruiter rant posted to a news group and a subsequent commentary on HackerNews got me thinking about the many ways that tech recruiting and the relationship between recruiters and the tech community is broken. I saw a few comments referencing that the community always says how broken it is, but no one tries to fix it. Here are some ideas on how we got here and directions we can go. Why is the recruiting industry the way it is?The high demand and low supply for tech talent creates a very lucrative market for recruiters. Many technologists might not be aware of this, but successful recruiters probably all make over 100K (some earn much more) and as a commission-based business your compensation has no maximum. Recruiting is an easy field to enter. No formal training is required, although you will need some sales training and tech knowledge to truly make an impact. One can easily start with a computer, a phone line, and a basic website.So we have an industry that can be very lucrative (for some much more lucrative than the tech industry itself) with almost no barriers to entry. Of course an industry with these characteristics will draw both talented, ethical professionals as well as carpetbaggers and bottom-feeders just as the gold rush did. What are the biggest complaints about recruiters (and how can we solve them)? First, complaints from candidates (tech pros):Too many cold calls. POSSIBLE SOLUTION: Without some widespread changes from all three parties (candidates, hiring firms, and recruiters) in the industry, this one is probably impossible to solve. Simply mentioning that you do not wish to hear from recruiters is no guarantee that they won’t contact you, but if I see on a LinkedIn page that someone specifically doesn’t want to hear from recruiters I won’t contact them as it is clear they do not value the services I provide. Dishonesty about the job description or salary. POSSIBLE SOLUTION: What if companies gave recruiters some form of ‘verified job spec‘ to share with candidates? Salary range, job description, location, whatever else might be helpful. A candidate could request this from the recruiter before agreeing to an interview. Being marketed/used without their knowledge. POSSIBLE SOLUTION: Companies could require a ‘right to represent‘ email giving a recruiter permission to submit his/her resume for any or all positions, which would at least eliminate some of this. Of course, recruiters will still send blinded resumes (contact info removed) to client prospects. A better idea may be for candidates to have a document that they ask recruiters to sign – a contract where the recruiter agrees not to send their resume in any form to any company without the express written consent (the ‘right to represent’) of the candidate. I’m not a lawyer, but I assume there could be some financial penalties/restitution allowed if you were to break that trust, as you may damage the candidate’s career. As a rule, if I want to market a candidate to a client, I always get their permission first. No feedback or follow-up. POSSIBLE SOLUTION: Unfortunately there is little value that a company gets by providing specific feedback about a candidate, and it actually exposes them to substantial risk (ageism, racism, etc.). Likewise, taking time to give rejected candidates details provides nothing to the recruiter except goodwill with the candidate. This one is difficult to solve, but probably not as big an issue as the other problems.And complaints from hiring firms:Too many resumes. POSSIBLE SOLUTION: If you provide a very good requirement to a good recruiter, he/she should be able and very willing to limit the resumes. Telling your recruiter that you want to see the best five available candidates should encourage them to limit submissions. Unqualified candidates. POSSIBLE SOLUTION: Same as above. Misrepresenting a candidate’s background. POSSIBLE SOLUTION: Well for starters, stop working with the recruiter and that agency entirely. If you want to make a positive change for the recruiting industry, contact the recruiter’s manager and tell your side of the story. Having liars in an industry is bad for everyone except the liars and those that profit off of them. Marketing cold calls. POSSIBLE SOLUTION: If you truly will not use recruiters for your searches, list that on your job specifications both on your website and the jobs you post publicly. I would rather not waste my time if a company has a policy against using recruiters, and if your policy changes perhaps you will be calling me. I will not call a company that specifically lists that they do not want to hear from recruiters, as it is clear they do not value the service I provide. Price gouging. POSSIBLE SOLUTION: This could be when recruiting agencies mark-up their candidates’ hourly rates well beyond what is a reasonable margin, or when recruiters who receive permanent placement fees tied to salary will stretch every penny from the hiring company. Flat transparent fees work very well for both of these problems (a flat hourly mark-up on contractors and a flat fee for permanent placements), although recruiters would particularly hate a flat fee structure for contractors. The recruiter’s ‘sale’ to a contractor is, “If I can get you $300/hr, do you care if I make $2/hr or $100/hr?“. The answer is usually ‘no’, which is all fine until the contractor finds out that you are billing your client $300/hr and only paying the him/her maybe $50/hr. That is rare, but that is when things get ugly. Flat and transparent rates exposed to all three parties involved will solve that problem, but don’t expect recruiters to go for it.To all the technology pros who claim they really want to disrupt the industry, I have one simple question. Would you be willing to hire, and pay for, an agent? I’ve heard the argument from some engineers that they would like recruiters to care more about the engineer’s career and not treat them like a commodity. Recruiters are traditionally paid for by the hiring companies, but only if they can both find the proper talent and get that talent to take the job (contingency recruiting). This can lead to a recruiter treating candidates like some homogenized commodity that all have similar value. If engineers want true representation of their best interests, having representation from a sole agent would be one obvious choice. As your agent, I could provide career advice at various times during the year, making suggestions on technologies that you may want to explore or giving inside information on which companies might have interest in you. You might come to me to discuss any thoughts on changing jobs, how to apply for promotion, or how to ask for a salary increase (which I could negotiate for you directly with your manager). When you do decide to explore new opportunities, the agent would help put together your resume, set a job search strategy, and possibly market your background to some hiring companies. As the agent is making his living by charging a fee to the candidates, the agent could charge a much smaller fee (or potentially even no fee) to the hiring company, which would make hiring you much less expensive than hiring through a traditional recruiter. If you were contacted by a recruiter from an agency or a hiring company, you would simply refer them to me for the first discussions and I would share the information with you (if appropriate) at a convenient time. You could even list my name on your LinkedIn, GitHub, and Twitter accounts. “If you are interested in hiring me, contact Dave at Fecak.com“ How good would that feel? How good would it feel to tell your friends that you have an agent? All of this assumes your agent would have some high degree of knowledge about the industry, the players, market rates, and a host of other things. Many recruiters don’t have this expertise, but some certainly do. An agent could probably represent and manage the career of perhaps 50-100 candidates very comfortably and make a good living doing it. Would you be willing to pay a percentage of your salary or a flat annual rate to an agent who provides you with these services? If the answer is ‘yes’, look me up and I’d be happy to discuss it with you further. But I’m guessing for many the answer is ‘no’ (or ‘yes, depending on the price’). My business model Most recruiters are contingency based, which means they only get a fee if they make a placement. If they search for months and don’t find a proper candidate, they just wasted months for no payment. This places 100% of the risk of a search on the recruiter and 100% of the control with the hiring company. Even if the recruiter finds a great fit, the company can walk away without making a hire. Contingency recruiting is cut-throat and causes desperation to make a placement, and this is where most of the problems arise for candidates. This is the ‘numbers game’ that tech pros talk about, where the recruiter’s main incentive is to get resumes and bodies in front of clients and see what sticks. Some recruiters are retained search, which means that basically all their fees are guaranteed up front regardless of their results. This is great for the recruiter but places 100% of the risk on the hiring company. The recruiter is working this search to save his/her reputation, which is obviously very important in getting future searches. This is not cut-throat, because it is not a competitive industry – recruiters have exclusive deals with a retained client for that particular job. The model I use combines contingency and retained search. I charge clients a relatively small flat fee upfront to initiate the search, which is non-refundable. When a placement is made, I charge my clients another flat fee (not tied to salary). When you combine the two fees, the percentage of salary is often about half what contingency recruiters would get for the same placement. So you think I’m an idiot for charging much less than my competition. Perhaps. I see it as creating a true partnership with companies that continue to come back with additional searches and repeat business, often referring me to their friends and partners. When a company gives you a fee upfront, they are putting their money where their mouth is and you can be sure they are serious about hiring. It takes some degree of trust on behalf of the hiring company, but once you have been in the business for a while the references are there and chances are we have some business connections in common. So far this model has worked well, with happy clients and lots of repeat business. I have already met my goal for 2012, and I’m hoping to double it in the coming months. What else do I do differently?I give it away (sometimes) – information, resume and interview advice, and any other kind of help you can think of are requested of me, and I rarely refuse a reasonable request. If I can’t help you find a job, I can at least take a look at your resume or evaluate how your applications look. I have known some engineers for over ten years without ever having made .05 in fees, and have helped them make career decisions for free. I’ll often introduce candidates to start-ups or one-man firms with limited budgets who may end up hiring without using my services, with the hopes that they will use me for future searches. I run a users’ group – I’ve run the local Java Users’ Group for almost 13 years. It is a volunteer job with no compensation, but it helps me stay in touch with the tech community and it also adds some credibility to my name. It is a lot of effort at times, but the success of the group is something that I’m quite proud of. I don’t recruit out of the group, but most of the group are aware of my services and come to me for my services when necessary. I specialize – Historically I focused both geographically and on one technology (Philadelphia Java market). I’ve opened that up a bit as many of those Java pros are now doing mobile, RoR, and alternative JVM lang work, and I’m a bit more regional now. Staying specialized in one geography and one technology forces a recruiter to be very careful about his/her reputation, as the degrees of Kevin Bacon are always low. Flat fees – A flat fee lets the company know that my goal is to fill the position and how much you pay the candidate is irrelevant. I inform candidates of this relationship so they are aware that my goal is to get them an offer that they will accept, and my client companies know that if I say I need $5K more in salary to close the deal that I am not trying to line my pockets.CONCLUSION Don’t expect my model to be adopted by any other firms, but I wanted to share it with readers as at least one alternative to the traditional contingency model that seems to be the biggest complaint for both candidates and hiring firms. And I believe the agent model would work quite nicely for all parties involved if anyone would like to inquire. If you truly want to disrupt the industry, let’s talk. Reference: How To Disrupt Technical Recruiting – Hire an Agent from our JCG partner Dave Fecak at the Job Tips For Geeks blog....
zk-logo

Advanced ZK: Asynchronous UI updates and background processing – part 1

Asynchronous UI updates are very useful, because they typically improve the responsiveness, usability and the general feel of user interfaces. I’ll be focusing here on the ZK framework, but generally the same principles apply for desktop UIs too (Swing, SWT). Long-running processing Sometimes you might have a database query, or an external web service call that takes a long time. Typically these jobs are synchronous, so basically there is a specific point in the code where the system will have to wait for a result and will block the thread that runs the code. If you end up running code like that in an UI thread, it will usually block the UI completely.Real-time updates Sometimes you don’t know in advance the exact time when something in the UI should be updated. For example, you could have a visual meter that shows the amount of users in an application. When a new user enters the application, the UIs of the current users should be updated as soon as possible to reflect the new user count. You could use a timer-based mechanism to continuously check if the amount of users has changed, but if there’s too many simultaneous users, the continuous checking will cause a very heavy load even if there is nothing to actually update in the UIs.Basic concepts Let’s first digest the title of this blog post: “Asyncronous UI updates and background processing” Background processing In the long-running processing use case the most obvious way to reduce UI blocking is to move expensive processing from the UI threads to some background threads. It’s very important to be able to understand what kind of thread will run the code in different parts of your application. For example, in ZK applications, most code is executed by servlet threads which are basically servlet world equivalents to UI threads. In order to execute code in some background thread, we’ll need a thread pool. The easiest way is to use java.util.concurrent.ExecutorService that was introduced in JDK5. We can push Runnable objects to the ExecutorService, so we are basically asking the ExecutorService to run a specific block of code in some background thread. It is absolutely crucial to understand that frameworks that use ThreadLocals will have problems with this approach because the ThreadLocals that are set in the servlet thread will not be visible in the background thread. An example is Spring Security which by default uses a ThreadLocal to store the security context (= user identity + other things).Asynchronous UI updates What does an asynchronous UI update mean in this context? Basically the idea is that once we have some information that we’d like to present in the UI, we’ll inform the UI of the new data (= asynchronous) instead of directly updating the UI in the background thread (= synchronous). We cannot know in advance when the new information is available, so we cannot ask for the information from the client side (unless we use polling which is expensive).Server push in ZK With ZK we have basically two different approaches we can use to update the UI once a background thread has new information. The name “server push” comes from the fact that the server has some new data that has to be pushed to the client instead of the typical workflow (client asks the server for information). Firstly, you can do synchronous updates by grabbing exclusive access to a desktop by using Executions.activate/deactivate. Personally I discourage this because once you have exclusive access, UI threads will have to wait until you deactivate the desktop. That’s why I won’t cover this method at all in this blog post. On the other hand, asynchronous updates are done by using Executions.schedule, which conforms to the Event/EventListener model of normal event processing. The idea is that we can push normal ZK Event objects to EventListeners, and the client side will be informed of these events. After that ZK does a normal AJAX request using Javascript and the Events will be processed by the EventListeners. This means that if we use asynchronous updates, all actual event processing will be done by servlet threads and all ThreadLocals are available as usual. This makes the programming model very simple, because you need to only have normal event listener methods without complex concurrent programming. Here’s a small example: public class TestComposer extends GenericForwardComposer { private Textbox search;public void onClick$startButton() { if (desktop.isServerPushEnabled()) { desktop.enableServerPush(true); }final String searchString = search.getValue(); final EventListener el = this; // All GenericForwardComposers are also EventListeners// Don't do this in a real-world application. Use thread pools instead. Thread backgroundThread = new Thread() { public void run() { // In this part of code the ThreadLocals ARE NOT available // You must NOT touch any ZK related things (e.g. components, desktops) // If you need some information from ZK, you need to get them before this code // For example here I've read searchString from a textbox, so I can use the searchString variable without problems String result = ... // Retrieve the result from somewhere Executions.schedule(desktop, el, new Event('onNewData', null, result)); } };backgroundThread.start(); } public void onNewData(Event event) { // In this part of code the ThreadLocals ARE available String result = (String) event.getData(); // Do something with result. You can touch any ZK stuff freely, just like when a normal event is posted. } } In the next part I’ll show you how to use JDK5 ExecutorServices to run tasks without manually creating threads. If you truly want to understand ZK server push, you should also read the relevant ZK documentation. Happy coding and don’t forget to share! Reference: Advanced ZK: Asynchronous UI updates and background processing – part 1 from our JCG partner Joonas Javanainen at the Jawsy Solutions technical blog blog....
software-development-2-logo

Provocateurs Gather the Best Requirements

Ask someone what they want, and they’ll tell you they want a faster horse. Provoke them, and they’ll tell you they have a ‘get there faster’ problem, an ‘equine waste disposal’ problem, and issues with total cost of ownership. Thought Provoking If your requirements elicitation session looks like the photo above, you’re doing it wrong. However, just asking people what they want and confirming that you heard what they said is also not enough. Active listening is important, but to capture great requirements, you also have to provoke thought about why someone is expressing a “requirement.” Adrian Reed wrote a great article this week (hat tip to Kevin Brennan) on asking provoking questions that leverage lateral thinking techniques to get better insight into the true requirements. Adrian presents eight questions, such as “Imagine if we fast-forward to 2 years after the implementation of this project, what will the organisation look like?” Some of his questions remind me a lot of the ideas behind Enthiosys’ Innovation Games (and Luke Hohmann’s Innovation Games book). The remember the future, and product box games immediately come to mind.Unprovoked Thoughts Most good subject matter experts I’ve met, when asked about the important problems to be solved, try and be really helpful and incorporate elements of solutions in their descriptions of problems. They will say things like “the system must integrate with [other system] to do X.” They may even ultimately be right, that this particular system integration is a constraint, and that “X” is the only acceptable (by policy) way to achieve “Y.” But usually, neither constraint is a requirement – it is a solution approach. Subject matter experts who are not as good at having and sharing insights about their domain often confuse problem manifestations with their underlying problems. By analogy, it is requesting treatment for a runny nose, when the problem the you have is the flu. You can dry up your nose and still feel horrible.Provoking Questions Reveal Real Problems Adrian’s questions are designed to help you understand that you’re treating the flu, and not a runny nose. Requirements gathering is a lot like diagnosis of a medical malady. You have to discover the real problems. The problems that people are willing to pay to solve. You have to uncover the latent problems that are “hidden” behind problem manifestations. In a (rare for me) American football analogy – the problem manifestation is that your quarterback is completing 1 of 20 forward passes. Replacing the quarterback and receivers will not solve the problem. The problem is that your offensive line is not able to give the quarterback sufficient time to throw higher-probability-of-success passes. Asking questions that force people to describe their objectives differently is a good way to bypass solution-design answers. It also creates chinks in the armor of problem manifestations. Completing more passes is not the future you’re looking for, winning more games is the goal. When you’re treating your flu, your goal is not to be sick – but with a dry nose. Your goal is to be well. When you ask someone to remember the future, they will will describe being not sick, not being dry-nosed. The product box will be a description of a winning team. Check out Adrian’s list of questions, and ask yourself, how do you get to the root causes? Ishikawa diagrams (also known as cause and effect or fishbone diagrams) provide a great visualization tool if you’re a spatial thinker or a whiteboard-talker. In the example below, you can quickly see that spending too much on fuel is part of the real problem – that the cost of operation is too high. You can likewise see that under-inflated tires are a source of poor fuel economy. Check out the Ishikawa article for an explanation, or this article on providing context (with Ishikawa diagrams), and this article on buyer and user personas for more examples of problem decomposition.If you’ve got any examples of problem-statement-turned-problem, chime in below… Reference: Provocateurs Gather the Best Requirements from our JCG partner Scott Sehlhorst at the Business Analysis | Product Management | Software Requirements blog....
career-logo

TALK! It’s An Interview, Not An Interrogation

Several times a year I will get a call or email from a hiring manager telling me that an interview never really ‘went anywhere’ because the candidate seemed either unwilling or unable to dive very deep into technical topics. It can be impossible for an interviewer to accurately gauge whether the cause was a lack of tech skills or just an inability to communicate those skills, but it really makes no difference as the end result is a rejection. This observation is probably a bit more prevalent during phone screens, where the parties do not have the advantage of proximity and body language to assist. Whenever I hear feedback like this from clients, I imagine what the interview may have sounded like: INTERVIEWER: Have you worked with Ruby on Rails before? CANDIDATE: Yes, I have. INTERVIEWER: Could you elaborate on that a little bit? CANDIDATE: Yes, I could. INTERVIEWER: (VISIBLY ANNOYED) It brings to mind the old joke (which of course reinforces gender and programmer stereotypes): A wife asks her computer programmer husband, “Go to the store and get a gallon of milk, and if they have eggs get a dozen.” A short time later the programmer returns with twelve gallons of milk. She asks, “Why did you get twelve gallons of milk?” and he responds, “They had eggs.” In the short two question interview scenario above, the candidate is answering the questions being asked, and the candidate is providing all the information that was being requested. If you look at the questions in a literal manner (as many engineers tend to do), this candidate may not feel he/she is being dodgy at all. Just as the programmer in the joke did what his wife asked (by his literal interpretation). Let’s now look at a brief example of a police interrogation: INTERROGATOR: Where were you on the night of the 8th? SUSPECT: At home. INTERROGATOR: Were you by yourself? SUSPECT: No INTERROGATOR: Who was with you? SUSPECT: My husband. INTERROGATOR: WHO WAS WITH YOU??!! SUSPECT: My lover! (CUE SCARY MUSIC) These answers provide the minimum amount of information the interrogator requested, and a lawyer will probably advise you to keep your answers short and precise during questioning. The interrogator’s job is to get a suspect to say something that he/she does not want to reveal. A job interview is not an interrogation, and it is important for candidates to be sure they are not treating it as such. Keep in mind that interviews, particularly in technology settings, can be awkward situations for participants on both sides of the table. Most interviewers are at least slightly uncomfortable being placed in a room with a complete stranger for the sole purpose of judging him/her in order to reach some conclusion about whether he/she should be hired, a decision which could greatly impact the life of at least one party (the candidate) or even both parties (if the hire is made). If an interviewer gives even a small consideration that this stranger may have a family that depends on the income that the job would provide, the potential for an awkward exchange is even more likely. Most interviewers want to get a dialogue flowing, where the discussion will allow them to evaluate your technical and interpersonal skills. They want to control and moderate the conversation, but they would like to listen more than they speak. The candidate’s ability to communicate his/her thoughts will be apparent after a few questions. At some point most interviewers have to ask themselves, “Would I want to work next to this person every day for several years?” If the candidate answered their questions in a fashion resembling the Candidate or the Suspect in the samples above, the answer is always “No”. Some candidates may argue that they have answered the questions as asked, and that if the interviewer wanted more details he/she should have inquired for them specifically (“Tell me about Ruby on Rails experience.”). This is a valid observation, but it doesn’t solve the problem. It’s purely a function of conversational English, and it is one reason that chatbots with artificial intelligence may give answers like our Candidate did in the example. So, I’ll just keep talking and talking to solve the problem? NOPE. There is also the possibility that if a candidate answers every question with a long-winded response, he/she may be rejected for not being able to provide succinct answers. Managers are often unwilling to hire someone who they feel is unable to express themselves efficiently, and in the case of software engineers you may hear, “Being that chatty, I can’t imagine what his/her code (comments) looks like!‘ How do I appear to be open, honest, and transparent without sounding chatty? How do you avoid being labeled as unable or unwilling to answer interview questions?Don’t take every question literally – Remember that a good interviewer is trying to engage you in a dialogue and an exchange of ideas. Pause before answering – Some candidates seem programmed to immediately start talking after a question is asked, and then find that they haven’t really answered the question. Taking a moment to reflect on the question and to organize your thoughts gives the appearance that you are making an effort to supply a strong answer and that you are not impulsive. A little white space does not have to be an uncomfortable silence. Pause after answering – If the interviewer does not respond with a follow-up after a few seconds, he/she may be waiting for you to go deeper. Ask if he/she is satisfied with your answer and offer to continue with more information if necessary. “Would you like some more details on that?“ Ask questions to clarify what type of answer is expected – If you are asked a question where there could be both an acceptable short answer (yes/no) or a longer answer (details), give the short response and offer the interviewer more. “Yes, I have used Ruby on Rails. Would you like me to discuss my experience further?“ At the end of the interview, ask the interviewer if they have any other questions that will help them make their decision. Invite them to contact you in the coming days if any additional information is required or if they would like any clarification on the answers you provided.Go into the interview with the goal of having a conversation which should put the interviewer at ease. Be willing to follow the interviewer into any direction the discussion may take, and ask questions so you know that you are on the same page. Reference: TALK! It’s An Interview, Not An Interrogation from our JCG partner Dave Fecak at the Job Tips For Geeks blog....
android-logo

Android AlarmManager tutorial

While writing an application, need arises to schedule execution of code in future. You may require AlarmManager to schedule your work at a specified time. AlarmManager accesses to system alarm and schedules the execution of code even when the application is not running. Project Information: Meta-information about the project. Platform Version : Android API Level 10. IDE : Eclipse Helios Service Release 2 Emulator: Android 4.1 Prerequisite: Preliminary knowledge of Android application framework, and Intent Broadcast receiver. AlarmManager: AlarmManager has access to the system alarm services. With the help of AlarmManager you can schedule execution of code in future. AlarmManager object can’t instantiate directly however it can be retrieved by calling Context.getSystemService(Context.ALARM_SERVICE). AlarmManager is always registered with intent. When an alarm goes off, the Intent which has been registered with AlarmManager, is broadcasted by the system automatically. This intent starts the target application if it is not running. It is recommended to use AlarmManager when you want your application code to be run at a specific time, even if your application is not currently running. For other timing operation handler should be used because it is easy to use. Handler is covered in other tutorial.Method Descriptionset() Schedules an alarm for one time.setInexactRepeating() Schedules an alarm with inexact repeating. Trigger time doesn’t follow any strict restriction.setRepeating() Schedules an alarm with exact repeating time.setTime() Sets the system’s wall clock time.setTimeZone() Sets the system’s default time zone.Check out the AlarmManager documention for more info. In this tutorial let’s learn to create one-time timer and the repeating timer, and also to cancel the repeating timer. Here timer and alarm have been used interchangeably, but in this tutorial context both of them have the same meaning.Example Code: Let’s create three buttons start repeating timer, cancel repeating timer and one-time timer in the layout file. These buttons are attached with methods i.e startRepeatingTimer, cancelRepeatingTimer and onetimeTimer respecitively. These methods will be defined in the Activity class. The layout file is shown below(activity_alarm_manager.xml). <linearlayout android:layout_height='match_parent' android:layout_width='match_parent' android:orientation='vertical' xmlns:android='http://schemas.android.com/apk/res/android' xmlns:tools='http://schemas.android.com/tools'><button android:id='@+id/btStart' android:layout_height='wrap_content' android:layout_width='match_parent' android:onclick='startRepeatingTimer' android:padding='@dimen/padding_medium' android:text='@string/btStart' tools:context='.WidgetAlarmManagerActivity'/> <button android:id='@+id/btCancel' android:layout_height='wrap_content' android:layout_width='match_parent' android:onclick='cancelRepeatingTimer' android:padding='@dimen/padding_medium' android:text='@string/btCancel' tools:context='.WidgetAlarmManagerActivity'/> <button android:id='@+id/btOneTime' android:layout_height='wrap_content' android:layout_width='match_parent' android:onclick='onetimeTimer' android:padding='@dimen/padding_medium' android:text='@string/btOneTime' tools:context='.WidgetAlarmManagerActivity'/> </linearlayout> We are going to define the BroadcastReciever which handles the intent registered with AlarmManager. In the given class onReceive() method has been defined. This method gets invoked as soon as intent is received. Once we receive the intent we try to get the extra parameter associated with this intent. This extra parameter is user-defined i.e ONE_TIME, basically indicates whether this intent was associated with one-time timer or the repeating one. Once the ONE_TIME parameter value has been extracted, Toast message is displayed accordingly. Helper methods have also been defined, which can be used from other places with the help of objects i.e setAlarm(), cancelAlarm() and onetimeTimer() methods. These methods can also be defined somewhere else to do operation on the timer i.e set, cancel, etc. To keep this tutorial simple, we have defined it in BroadcastReceiver. setAlarm(): This method sets the repeating alarm by use of setRepeating() method. setRepeating() method needs four arguments:type of alarm, trigger time: set it to the current time interval in milliseconds: in this example we are passing 5 seconds ( 1000 * 5 milliseconds) pending intent: It will get registered with this alarm. When the alarm gets triggered the pendingIntent will be broadcasted.cancelAlarm(): This method cancels the previously registered alarm by calling cancel() method. cancel() method takes pendingIntent as an argument. The pendingIntent should be matching one, only then the cancel() method can remove the alarm from the system. onetimeTimer(): This method creates an one-time alarm. This can be achieved by calling set() method. set() method takes three arguments:type of alarm trigger time pending intentpackage com.rakesh.alarmmanagerexample;import java.text.Format; import java.text.SimpleDateFormat; import java.util.Date;import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.PowerManager; import android.widget.Toast;public class AlarmManagerBroadcastReceiver extends BroadcastReceiver {final public static String ONE_TIME = 'onetime';@Override public void onReceive(Context context, Intent intent) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 'YOUR TAG'); //Acquire the lock wl.acquire();//You can do the processing here. Bundle extras = intent.getExtras(); StringBuilder msgStr = new StringBuilder(); if(extras != null && extras.getBoolean(ONE_TIME, Boolean.FALSE)){ //Make sure this intent has been sent by the one-time timer button. msgStr.append('One time Timer : '); } Format formatter = new SimpleDateFormat('hh:mm:ss a'); msgStr.append(formatter.format(new Date()));Toast.makeText(context, msgStr, Toast.LENGTH_LONG).show(); //Release the lock wl.release(); }public void SetAlarm(Context context) { AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class); intent.putExtra(ONE_TIME, Boolean.FALSE); PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); //After after 5 seconds am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 5 , pi); }public void CancelAlarm(Context context) { Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class); PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmManager.cancel(sender); }public void setOnetimeTimer(Context context){ AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class); intent.putExtra(ONE_TIME, Boolean.TRUE); PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pi); } }Given below is the manifest file. Here, WAKE_LOCK permission is required because the wake lock is being used while processing in onReceive() method present in AlarmManagerBroadcastReceiver class. AlarmManagerBroadcastReceiver has been registered as broadcast receiver. <manifest android:versioncode='1' android:versionname='1.0' package='com.rakesh.alarmmanagerexample' xmlns:android='http://schemas.android.com/apk/res/android'><uses-sdk android:minsdkversion='10' android:targetsdkversion='15'/> <uses-permission android:name='android.permission.WAKE_LOCK'/> <application android:icon='@drawable/ic_launcher' android:label='@string/app_name' android:theme='@style/AppTheme'> <activity android:label='@string/title_activity_alarm_manager' android:name='com.rakesh.alarmmanagerexample.AlarmManagerActivity'> <intent-filter> <action android:name='android.intent.action.MAIN'/> <category android:name='android.intent.category.LAUNCHER' /> </intent-filter> </activity> <receiver android:name='com.rakesh.alarmmanagerexample.AlarmManagerBroadcastReceiver'> </receiver> </application> </manifest> Now let’s define the activity class which defines some methods. These methods are going to handle the button clicks. Here in this class we create an instance of AlarmManagerBroadcastReciever which will help us to access setAlarm(), cancelAlarm() and setOnetime(). Rest of the code is easy to understand. package com.rakesh.alarmmanagerexample;import com.rakesh.alarmmanagerexample.R; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import android.support.v4.app.NavUtils;public class AlarmManagerActivity extends Activity {private AlarmManagerBroadcastReceiver alarm; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_alarm_manager); alarm = new AlarmManagerBroadcastReceiver(); } @Override protected void onStart() { super.onStart(); }public void startRepeatingTimer(View view) { Context context = this.getApplicationContext(); if(alarm != null){ alarm.SetAlarm(context); }else{ Toast.makeText(context, 'Alarm is null', Toast.LENGTH_SHORT).show(); } } public void cancelRepeatingTimer(View view){ Context context = this.getApplicationContext(); if(alarm != null){ alarm.CancelAlarm(context); }else{ Toast.makeText(context, 'Alarm is null', Toast.LENGTH_SHORT).show(); } } public void onetimeTimer(View view){ Context context = this.getApplicationContext(); if(alarm != null){ alarm.setOnetimeTimer(context); }else{ Toast.makeText(context, 'Alarm is null', Toast.LENGTH_SHORT).show(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_widget_alarm_manager, menu); return true; } } Once you are done with the coding, just execute the project and you will find the similar kind of application running in your emulator. Please download AlarmManagerExample code, if you need reference code. Happy coding and don’t forget to share! Reference: Tutorial on Android AlarmManager from our JCG partner Rakesh Cusat at the Code4Reference blog....
software-development-2-logo

Technical Debt – when do you have to pay it off?

There are 2 times to think about technical debt:When you are building a system and making trade-off decisions between what can be done now and what will need to be done “sometime in the future”. “Sometime in the future”, when have to deal with those decisions, when you need to pay off that debt.What happens when “sometime in the future” is now? How much debt is too much to carry? When do you have to pay if off?How much debt is too much? Every system carries some debt. There is always code that isn’t as clean or clear as it should be. Methods and classes that are too big. Third party libraries that have fallen out of date. Changes that you started in order to solve problems that went away. Design and technology choices that you regret making and would do differently if you had the chance. But how much is this really slowing the team? How much is this really costing you? You can try to measure if technical debt is increasing over time by looking at your code base. Code complexity is one factor. There is a simple relationship between complexity and how hard it is to maintain code, looking at the chance of introducing a regression:Complexity % Chance of bad fix1-10 5%20-30 20%>50 40%100 60%Complexity by itself isn’t enough. Some code is essentially complex, or accidentally complex but it doesn’t need to be changed, so it doesn’t add to the real cost of development. Tools like Sonar look at complexity as well as other variables to assess the technical risk of a code base: Cost to fix duplications + cost to fix style violations + cost to comment public APIs + cost to fix uncovered complexity (complex code that has less than 80% automated code coverage) + cost to bring complexity below threshold (splitting methods and classes) This gives you some idea of technical debt costs that you can track over time or compare between systems.But when do you have to fix technical debt? When do you cross the line? Deciding on whether you need to pay off debt depends on two factors:Safety / risk. Is the code too difficult or too dangerous to change? Does it have too many bugs? Capers Jones says that every system, especially big systems, has a small number of routines where bugs concentrate (the 20% of code that has 80% of problems), and that cleaning up or rewriting this code is the most important thing that you can do to improve reliability as well as to reduce long the term costs of running a system. Cost – real evidence that it is getting more expensive to make changes over time, because you’ve taken on too much debt. Is it taking longer to make changes or to fix bugs because the code is too hard to understand, or because it is too hard to change, or too hard to test?While apparently for some teams it’s obvious that if you are slowing down it must be because of technical debt, I don’t believe it is that simple. There are lots of reasons for a team to slow down over time, as systems get bigger and older, reasons that don’t have anything to do with technical debt. As systems get bigger and are used by more customers in more ways, with more features and customization, the code will take longer to understand, changes will take longer to test, you will have more operational dependencies, more things to worry about and more things that could break, more constraints on what you can do and what risks you can take on. All of this has to slow you down.How do you know that it is technical risk that is slowing you down? A team will slow down when people have to spend too much time debugging and fixing things – especially fixing things in the same part of the system, or fixing the same things in different parts of the system. When you see the same bugs or the same kind of bugs happening over and over, you know that you have a debt problem. When you start to see more problems in production, especially problems caused by regressions or manual mistakes, you know that you are over your head in debt. When you see maintenance and support costs going up – when everyone is spending more time on upgrades and bug fixing and tuning than they are on adding new features, you’re running in circles.The 80:20 rule for paying off Technical Debt Debt builds up slowly and incrementally over time, until you reach a breaking point. Without careful attention, all code will get worse over time, but whatever problems you do have are going to be worse in some places than others. When it comes to paying back debt, what you care about most are the hot spots:Code that is complex and Code changes a lot and Code that is hard to test and Code that has a history of bugs and problems.You can identify these problem areas by reviewing check-in history, mining your version control system (the work that Michael Feathers is doing on this is really cool) and your bug database, through static analysis checks, and by talking with developers and testers. This is the code that you have to focus on. This is where you get your best return on investment from paying down technical debt. Everything else is good hygiene – it doesn’t hurt, but it won’t win the game either. If you’re going to pay down technical debt, pay it down smart. Reference: Technical Debt – when do you have to pay it off? from our JCG partner Jim Bird at the Building Real Software blog....
java-logo

Review: “Java EE 6 Pocket Guide” by Arun Gupta

This is a review I am very pleased to write. My friend Arun published the Java EE 6 pocket guide and it will be at your hands as early as you can order. I knew about the book quite early, because I had the pleasure to review it and I am thankful for the chance to contribute a little to it! The Kindle edition is already available and the printed books should be available until JavaOne. There is a good chance to have Arun sign it, if you bump into him there! Abstract The Java Enterprise Edition 6 platform provides capabilities that make it easier for Java programmers to develop and deploy enterprise and Web applications. This handy guide provides an overview of the main technologies in the Java EE 6 platform, including extensive easy-to-understand code samples that demonstrate many improvements. Whether you’re familiar with Java EE 5 or a Java programmer approaching the enterprise edition for the first time, this book will quickly get you up to speed on Java EE 6. Discover how Java EE 6 provides a simplified developer experience and improves on the developer productivity features introduced in Java EE 5. Delve into Java EE 6 profiles, including a comprehensive profile for lightweight, standards-based modern web applications. Explore how the platform enables extensibility with open source libraries and frameworks. Learn how specifications such as Contexts & Dependency Injection, Java API for RESTful Services, and Servlets 3 make the platform more powerful.Book: ‘ Java EE 6 Pocket Guide‘ Language : English Paperback: 204 pages Release Date : September 2012 ISBN-10: 144933668X ISBN-13: 978-1449336684 The Author Arun ( @arungupta) is the Java EE evangelist at Oracle. He has over 15 years of experience in the software industry working in the Java platform and several web-related technologies. In his current role, he works to create and foster the community around Java EE and GlassFish. He has been with the Java EE team since its inception and contributed to all releases. Arun has extensive world wide speaking experience on myriad of topics and loves to engage with the community, customers, partners, and Java User Groups everywhere to spread the goodness of Java. He is running a well know blog named ‘ Miles to go…‘ The Content 204 pages is a good size for a pocket guide. It covers the basics you need to know of Java EE 6 and gives good examples of all relevant parts. Chapter 1 introduces you to the Java Enterprise Edition in general. Chapter 2 follows with a brief introduction to Managed Beans and their overall life-cycle. Chapter 3 dives into Servlets and all relevant parts. Chapter 4 introduces the Java Persistence API beginning with Entities and the surrounding happenings. Chapter 5 dedicates content around the Enterprise JavaBeans specification. Thins includes Stateful, Stateless, Singleton and Message-Driven up to the Embeddable API and the EJBLite specification. Chapter 6 is about Contexts and Dependency Injection including the portable extensions. The Chapter 7 covers JavaServer Faces very briefly and introduces you to the main concepts. Chapters 8 and 9 are about SOAP-Based Web Services and RESTful Web Services with simple and understandable examples. Chapter 10 covers the Java Message Service with a message send example. The book closes with Chapter 11 about Bean Validation and how it integrates with JPA and JSF. Writing and Style This is a pocket guide which is comprehensively written. I could follow all examples and it was a good read overall. No complicated constructs and clear writing. Walking from chapter to chapter works, but it isn’t designed like that. It is more like a reference book to look up the most important topics in Java EE 6 at one point. It isn’t watered-down by complex examples and sticks to the most important characteristics of the covered specifications. If you ever have seen one of Arun’s presentations or tutorials you know that he is good in explaining things and this style found the way into the guide. Conclusion and recommendation I’m obviously not neutral here :) GO GET IT! It is the only book you probably will need about Java EE 6! It is comprehensive, wonderfully written and covers everything you need in your daily work. It is not a complete reference but provides a great shortcut to the things you need to know. To me it is a good beginners guide and also works as a companion for advanced users. Get it while it’s hot! Thanks Arun for sharing your knowledge! Reference: Review: “Java EE 6 Pocket Guide” by Arun Gupta from our JCG partner Markus Eisele at the Enterprise Software Development with Java blog....
Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close