Now I'm so excited to start using them! I can see JDK 8 being released and have started migrating projects on a jdk8 branch which will be ready to be snapped into production as soon as the JDK is officially released, I strongly advise everyone to do the same, you will love the new support.
What I'll be Covering
There are several new features coming with JDK 8, of which I'm going to cover a small subset of what I believe will be the most widely used by consumer developers, see the full JDK 8 feature list for more.- JSR-335 - Lambda Expressions and associated library improvements such as bulk data operations for collections.
- JSR-310 - Date and Time API.
- NetBeans tooling to help migrate to JDK 8
- Maven based project
Environment
For the conversion we'll be using NetBeans, quite simply because IMHO it is the best IDE for Java development, if you haven't tried it in a while you're in for a heck of a treat (sorry eclipse). I've been using it since 2002 of which the team has always responded quickly to change and bug submissions. The upcoming 8.0 version has full support for JDK 8 and an awesome tool that will analyse your project and convert (with your permission) blocks of code to JDK 8 equivalents all in one bulk migration operation, this is what we're going to use. Download the Beta 8 version or the nightly build.NetBeans comes with all you will need to get the job done, but you will also need a copy of the latest JDK 8 EA which can be downloaded here.
I converted two distinct projects, one was a super project containing 20 child projects that represent domains within our business such as sales, product, scheduling... The second project is a spring/mvc 3 project for our in house FSA/CRM/portal system used by all members of the company that relies on the 18 libraries in the first project.
I use maven/git from the command line a lot, but that's a personal preference. NetBeans has great support for both, but I just type faster than I can click.
At the time I performed the conversion I was using the nightly build of NetBeans (Build 201401260001) and JDK8 (build 1.8.0-ea-b124).
Configure NetBeans IDE
While NetBeans does work well with JDK 8, I have had some rendering issues, it could be due to being on a mac, I don't know. But for me I always point it to JDK 7. I recommend doing this until the final release of both JDK 8 and NB 8. To do this you will need to locate theetc/netbeans.conf
file within your NetBeans installation. On a mac this is slightly different. Refer to the following default locations where your netbeans.conf
may reside:- Windows:
c:\program files\netbeans\etc\netbeans.conf
- Mac:
/Applications/NetBeans/NetBeans\ 8.0\ Beta.app/Contents/Resources/NetBeans/etc/netbeans.conf
- Linux:
/opt/netbeans/NetBeans\ 8.0\ Beta/etc/netbeans.conf
netbeans_jdkhome="/Library/Java/JavaVirtualMachines/jdk1.7.0_51.jdk/Contents/Home"NOTE: If you have previously used a dev build of NetBeans, it's best to delete your dev userdir, this can be found in
~/netbeans/dev
on linux, ~/Library/Application\ Support/NetBeans/dev/
for mac, I'm not sure where in windows.Now start NetBeans, we will need to then add the Java 8 JDK to NetBeans. Go to [Tools > Java Platforms], from here add your installed Java 8 JDK.
Change JDK Version for Servlet Container(s)
One thing I actually forgot to do when first writing this post was to update the JDK on my tomcat container. I found this out after exceptions started appearing when debugging locally. All servlet containers are configured similarly, thought he JDK setting is in slightly different places. All servers can by configured by either right clicking the server in the Services (CMD+5 on Mac) within the servers node or by accessing the Tools > Servers menu option.I have only covered Glassfish and Tomcat as they are the standard containers that come with NetBeans, if you have another container try poking about until you find the setting.
Tomcat
Tomcat's configuration is found under the "Platform" tab. The "Java Platform" configuration option controls which JDK is used.Glassfish
Glassfishes configuration is found under the "Java" tab. The "Java Platform" configuration option controls which JDK is used.Upgrade Project JDK
First thing to do is for each project update the JDK and source version. This can be done by right clicking the project and clicking "Properties".You will need to first set the JDK under "Build > Compile", set the "Java Platform" to "JDK 1.8".
Next go to "Sources" and set the "Source/Binary Format" to 1.8.
NetBeans would do the following:
- Add or update the
maven-compiler-plugin
version to 1.8 - Create or modify the
nb-configuration.xml
file settingnetbeans.hint.jdkPlatform
to JDK_1.8
pom.xml
file directly, for me I actually have a property version-java-target
in my pom.xml
properties
section that defines the 1.8
java version. The use of the nb-configuration.xml
file gives the IDE a hint as to which JDK it should use, maven from the command line will use the setting of JAVA_HOME
, but NetBeans may have more than one JDK configured so has to somehow figure out which one to use.NetBeans first looks at
nb-configuration.xml
for the JDK hint (netbeans.hint.jdkPlatform
), if it can't find it there it will look at the pom.xml
file for the property of the same name. If you do want to control this yourself you may edit the pom.xml
directly but remember to reload the project (right click > Reload POM) to reload the new configuration.TIP: If later you find that you are creating JDK 8 specific code but the IDE tells you your source level does not permit this, yet checking the project configuration tells you it's all set for JDK 8 and source level 1.8, chances are you have a
nb-configuration.xml
file and pom.xml
conflict with netbeans.hint.jdkPlatform
, in this case the nb-configuration.xml
file wins. This caught me out where a child project was involved. Basically NetBeans was telling me the configuration from the parent pom.xml
file, yet the child projects had a nb-configuration.xml
file that was conflicting.At this point you will need to update the project JDK on your CI/build server before pushing your changes if you wish to avoid build failures.
Repeat this for your other projects you will be upgrading.
TIP: If you are working with a super pom and child projects, I suggest the following:
- Remove all
nb-configuration.xml
files (look at them first), i.e.find . -type f -name nb-configuration.xml -delete
. - If any child project contains a
maven-compiler-plugin
build plugin, remove it. - In your parent pom add the plugin management with default settings which will get picked up by child projects, refer below.
- Set the JDK version in your properties section.
<properties> <version-java-target>1.8</version-java-target> <netbeans.hint.jdkPlatform>JDK_1.8</netbeans.hint.jdkPlatform> </properties> <build> <pluginmanagement> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupId> <artifactid>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${version-java-target}</source> <target>${version-java-target}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupid>org.codehaus.mojo</groupId> <artifactid>jaxb2-maven-plugin</artifactId> <version>1.5</version> </plugin> </plugins> </pluginManagement> </build>
Build your project within the IDE (SHIFT+F11), if all goes well, send it off to your DEV CI server. You should be right to use new language features.
Test Project 1.8 Support
Now that you've configured all projects I advise testing that the project is in fact being picked up by the IDE as a JDK 1.8 project, as noted above I had issues where this was not. It's simple to test, just open a class and add the following:Runnable r = () -> System.out.println("I love lambdas");
If your editor shows an error like the following, the project isn't picking up the Java 8 JDK sometimes it's necessary to close the IDE and open again to get it to recognise the new JDK settings for the project. If it isn't showing an error, then it's time to get excited!
Configure Formatting Preferences
NetBeans has a highly customisable configuration for the way code is formatted, which can be specified globally and per project. To configure settings globally Go to NetBeans Preferences (Mac: NetBeans > Preferences, Windows/Linux: Tools > Options) > Editor > Formatting > Java. Browse through all the available settings and get these the way you like them. I advise doing this now as later we will be converting a lot of source files automatically at once which will use these settings.Inspect and Transform to JDK 8
This is by far the most super-coolTM feature that will hunt down potential JDK 8 conversions that can be applied to your source files. It will present a list of all found changes with diff's allowing you to accept each individual change. This is truly cool.TIP: Note that this can do more than just convert to JDK 8, have a look at the configurations and see what else you might like it to find/fix for you.
Start by selecting a project node, you can right click and select "Inspect and Transform..." or go to [Refactor > Inspect and Transform...]. Select the "Configuration" radio button and change the selection to "Migrate to JDK 8".
At this point you can customise how the migration assistant works by hitting the "Manage..." button, I highly recommend you do this; my tip is to turn OFF the "Static Imports" setting. There's a good reason for this which is illustrated here.
Consider this fairly useless piece of code, though it's functional that it shows how something can go awry.
import java.util.Calendar; import java.text.DateFormat; import java.util.GregorianCalendar; import java.text.SimpleDateFormat; public class StaticUsage { public void foo() { Calendar cal = GregorianCalendar.getInstance(); DateFormat df = SimpleDateFormat.getInstance(); } }
With static imports on the migration tool will produce the following invalid class file.
import java.util.Calendar; import java.text.DateFormat; import java.util.GregorianCalendar; import java.text.SimpleDateFormat; import static java.text.DateFormat.getInstance; import static java.util.Calendar.getInstance; public class StaticUsage { public void foo() { Calendar cal = getInstance(); DateFormat df = getInstance(); } }
Notice here that the code produced now has a conflict between the two static methods.
Back to our migration assistant, hit Inspect. The inspector may take some time depending on the size of your project. Once complete you may have a warning where more than one fix can be applied, if this is the case take note of where and just hit "Inspect".
You should now have a diff open up within the output window area titled "Refactoring". From here you can review all refactorings that have been identified by the IDE. Give each one a review and un-tick if you do not want that particular action to be taken. Once satisfied hit the "Do Refactoring" button.
All your selected refactorings will have now been applied. First before continuing just ensure the project compiles by performing another clean and build (SHIFT+F11).
Clean up Source Files
While the JDK 8 migration tool is completely awesome, you may want to review all files and give them a polish. There's a few things that I found that I wanted to do when reviewing my sources:- Lambdas now removed a lot of anonymous classes, therefore their
import
statements can now also be removed. To fix this, open each file and simply hit [Source > Fix Imports...]. - Lambdas in most cases can type infer the arguments. I personally prefer this as IMHO less is more when it comes to readability. Simply find any lambda arguments and remove the type. Note that sometimes this can't be done as the compiler may not know which method is being called for example.
- Lambdas do not require a body, which NetBeans prefers. However I found that in some (minor) cases it's actually more readable when there is a body.
- The refactoring sometimes ends up on very long lines, especially where the lambda has no body. I found it far easier to read by going through every lambda and breaking them out a little.
Check out Some Hints
NetBeans has more to offer with migrating to JDK 8. For example it can convert a for loop to a map/reduce stream. Sometimes this may not be ideal though, consider the following:Set<String> ids = new HashSet<>(); for (Widget w : getAllWidgets()) { ids.add(w.getId()); }
If we were to accept the hints suggestion and convert, it would be more of a horizontal problem:
Set<String> ids = new HashSet<>(); getAllWidgets().stream().forEach((w) -> { ids.add(w.getId()); });
Which if fully converted should actually look like the following:
HashSet<String> ids = getAllWidgets().stream() .map(Widget::getId) .collect(Collectors.toCollection(HashSet::new));
To stop this hint appearing all the time, configure it to only appear on current line.
Java Time
Java now comes with a great date/time library out of the box, so you don't need to worry about JodaTime, which served a need for many years. Fortunately Java Time (JSR-310) uses similar class names to that of JodaTime, which will make your life slightly easier in the fact that some source files will just need the references changed fromorg.joda.time.*
to java.time.*
. Choosing to upgrade to java-time or continue using Joda or java.util.Date
/java.util.Calendar
is up to you, but I advise doing it sooner than later. Updating is a matter of going through and performing manual conversion unfortunately.What I will describe is what you might need to do to get jsr-310 working with Jackson and spring.
As with joda-time, it comes as a separate jackson module. Add the following to your
pom.xml
<dependency> <groupid>com.fasterxml.jackson.datatype</groupId> <artifactid>jackson-datatype-jsr310</artifactId> <version>2.3.0-rc1</version> </dependency>
As for spring there are a number of ways this is done, if you are using joda already, use that same class/configuration, but also add
com.fasterxml.jackson.datatype.jsr310.JSR310Module
with the registerModule
method just as you would with joda. For example, this is the configuration I use:Custom object mapper class
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.joda.JodaModule; import com.fasterxml.jackson.datatype.jsr310.JSR310Module; import javax.annotation.PostConstruct; public final class CustomObjectMapper extends ObjectMapper { public CustomObjectMapper() { super(); registerModule(new JodaModule()); registerModule(new JSR310Module()); } @PostConstruct public void afterPropertiesSet() { disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); } }
Spring MVC servlet context configuration
<annotation-driven> <message-converters register-defaults="true"> <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <beans:property name="objectMapper"> <beans:bean class="com.johnsands.spring.http.converter.json.CustomObjectMapper"/> </beans:property> </beans:bean> </message-converters> </annotation-driven>
Conclusion
Here I've shown you how you can quickly migrate your project to JDK 8 fairly painlessly with the aid of the awesome tools that come with the NetBeans IDE that help to perform this migration. I really tip my hat off to the team as this was a nice surprise that I thought I was going to have to convert everything on a per case.Have a look at my second article The Power of the Arrow for some of my more interesting lambdas.
I've added a section for configuring the JDK within your app-servers. Here I cover Tomcat and Glassfish
ReplyDelete