This December, the Coimbra JUG had it’s second birthday! To celebrate, we had two awesome meetings with well known international speakers: Heather VanCura and Christoph Engelbert. The attendance was great. We had around 40 persons on each event. Thank you to all attendees that showed up and even brought friends!
A few weeks ago, I’ve posted a blog about moving from Java EE 5 to 7. It was mostly about how you could improve your Java EE 5 code with the new Java EE 7 stuff. Now in this post, I’m going to look a little bit into the migration path on the Application Server side.
If you’re using Java EE 5, there is good chance that you are using one of these servers:
There are many other servers supporting Java EE 5, and you could check them out here.
I’ve ended up getting most of my experience with JBoss 4x, since the company I was working on at the time was already using it heavily in most of their projects. I hardly had any vote on the matter and just kept the company direction with JBoss.
When we decided to move one of our client critical applications from Java EE 5 to 7, we were faced with the dilemma of which application server to use. Since I was in a technical management position, I was now able to influence that decision. We end up picking Wildfly for the following reasons:
Implemented Java EE 7 Full profile
Powerful CLI to manage the server
Team already familiar with the Java EE implementations shipped with Wildfly
Even though this post looks into JBoss and Wildfly, some of the principles still apply for Application Servers in general. So I hope that this can be useful for other Application Servers users as well. We are currently using Wildfly 8.2.0, but the content discussed in this post should also work with the latest Wildfly version.
Performing an Application Server migration, especially one that involves servers so far apart, is never easy. The migration path is not exactly straightforward, because each application ends up using different features of the Application Server. Worse, the application might even be implementing business code supported in these features that might not be available in the target migration server.
Anyway, there are two strategies that you can follow when working on a migration project:
As the name implies, you freeze your project to perform the necessary adjustments to migrate the application. It’s probably easier to deal with complexity, but on the other hand it delays business features and creates a non negotiable deadline. It’s very hard to convince stakeholders to go with this strategy, but if you are able, go for it.
The other alternative is to keep development going and work the migration at the same time. It’s best for the business, but requires much more discipline and planning. You can always partition and split your application into modules and migrate it in small bits. This in the strategy I usually use.
You might need some time to completely migrate your application. During that time, you need to keep the old server running as well as the new. For this, you are required to update and duplicate your environments. It’s like branching the code, but in runtime.
Support tools that you use, might need updating as well. Maven plugins for the new server, Jenkins deployments, whatever interacts with the Application Server. It’s a daunting task, since the complexity to manage all these extra environment and branches is not easy.
Walking the Path
There are a couple of details that you need to worry about when thinking about the migration. This is not an extensive list, but are probably the most common topics that you are going to come across.
If you don’t run into ClassNotFoundException, NoClassDefFoundError or ClassCastException you might want to consider to play the lottery and win!
This is especially true with JBoss 4.x Classloader. At the time, class loading was (still is, but even more than) an expensive operation, so JBoss used something called the UnifiedClassLoader. This meant that there was no true isolation between applications. EAR archives could look into each other to load libraries. Of course, this was a major headache to manage. The worst part was when you had to deploy your application into a customer using a JBoss server. If you didn’t have control over it, the current deployment could clash with your own.
Wildfly introduced class loading based on modules instead of the usual hierarchical approach. Usually, an application deployed in Wildfly, doesn’t have access to the Application Server libraries unless is stated explicitly with a file descriptor. For Java EE Applications these modules are loaded automatically.
When changing servers, these are the most common issues related to class loading:
Missing libraries that were sitting on other applications.
Relaying on libraries sitting on the server that were either removed or updated.
Libraries used on the application that are now part of the new server.
To fix this you need to tune your project dependencies by adding your removing the required libraries. There is no step by step guide here. Each case needs to be analyzed and fixed accordingly. It’s kinda like trying to untangle a string full of knots.
This custom descriptor is adding dependencies from other deployments, namely app-client.jar and even a sub deployment of another EAR in app-ear.ear.app-entity.jar.
Finally, my advice here is to try to keep with the Standards and only introduce additional libraries if absolutely necessary. This will surely reduce your class loading problem and it would make it easier to move to new versions of the server or even change to another server in the future.
In JBoss 4.x, all the configuration was spread around different files: server.xml, jboss-service.xml, login-config.xml and many others. You had to manually edit the files to change the required configuration. This was a tedious work, especially when you didn’t have access to the server and had to document the set of changes for someone else to perform.
In Wildfly most of the configuration goes into configuration/standalone.xml, but I don’t ever edit the file. Wildfly ships with a very powerful Command Line Interface (CLI) that allows you to script pretty much every change that you need to perform on the server. Here is a sample of Undertow configuration:
This is setting up a virtual host called app, making it the default host, removes the default host that comes with Wildfly and activate Single Sign On.
With scripting and the CLI is very easy to spin up a new server from the ground up. You should always prefer this way of changing configuration on the server.
In JBoss 4.x, setting up a Datasource only require you to copy the database driver to the lib folder and create a *-ds.xml file with the Datasource connection details.
In Wildfly, is a little more tricky, but not a big deal. You set up the Datasource as a module and then you can use the CLI to add the Datasource connection details to the server configuration. I even wrote an entire blog post about this in the past: Configure JBoss / Wildfly Datasource with Maven.
It was common to use @LocalBinding in JBoss 4.x to define the exact JNDI name for your EJB. But Java EE 7 introduced standard JNDI names by scope, meaning that you should follow the convention to lookup EJB’s.
As stated, migrations never follow a direct path. Still, there are a couple of things that you can do to improve. Write tests, tests and tests. Did I tell you to write tests yet? Do it before working on any migration stuff. Even if everything with the migration seems fine, you might encounter slight behaviour changes between the different versions of the Java EE implementations.
Also, don’t underestimate the job. Keeping your application working with new features being developed, plus changing a server requires you to invest time and effort to make sure that nothing is going to break. Definitely it won’t take you 1 week, unless we are talking about a very tiny application. We took almost 2 years to migrate an application over 1 Million lines. But take these numbers lightly. These are very dependent on your team dynamics.
My final advice: if you are sitting in an old Java EE version, you should definitely migrate. Have a look in my blog about Reduce Legacy from Java EE 5 to 7. The jump is not easy, but with each new version of Java EE release and the concern about standardization, each upgrade should become less painful.
Last Wednesday, 4 November 2015, the tenth meeting of Coimbra JUG was held at the Department of Informatics Engineering of the University of Coimbra, in Portugal. The attendance was great. We had around 40 persons and a lot of them were on a Coimbra JUG meeting for the first time. We had the pleasure to listen Sérgio Ferreira talk about Maven. Sérgio is an old time member of Coimbra JUG and he volunteered to present the session for us for the first time. A big thanks to Sérgio! It’s not easy to do it.
Love it or hate it (and a lot of people seem to hate it), Maven is a widely used tool by 64% of Java developers (source – Java Tools and Technologies Landscape for 2014). Most experienced developers already got their share of Maven headaches. Usually in the hard way, banging with their head into a brick wall. Unfortunately, I feel that new developers are going through the same hard learning process. In a young JUG as ours, it makes perfect sense to have a dedicated session to Maven, since sooner or later everyone will have to use Maven.
As always, we had surprises for the attendees. IntelliJ sponsored our event, by offering a free license to raffle among the attendees. Congratulations to A. Ventura and Ana Filipa for winning the license. Develop with pleasure! We also handed a few Tomitribe and ZeroTurnaround t-shirts.
Also, we already have our 11th and 12th Meetings scheduled for 2 and 9 of December of 2015. These are going to celebrate our 2nd Anniversary and we are happy to have two international well know speakers: Heather VanCura and Christoph Engelbert. Please, check our Meetup website for more information.
Java EE 5 was first introduced in 2005, while Java EE 7 came out in 2013. There is a 7 year gap between both versions and in technology terms it’s like a century.
Many organizations are still stuck using Java EE 5 and there are many valid reasons why they choose not to upgrade. Still, these become irrelevant if you look into some of the reasons to move forward:
Benefit from the Latest Improvements
Java 6 EOL in Q1 2013
Increased Maintenance Costs
Hard to keep Developers interested
These reasons are somehow debatable and may not be enough to convince someone to upgrade.
Over the last few years, I’ve worked in an application with a considerable dimension and just recently it was migrated from Java EE 5 to 7.
Stop the Legacy
Every year, new features were introduced that increased the application code base. It even surpassed 1 Million lines of code! This fact alone is an indicator that it’s hard to navigate this huge code base. If the application keeps growing, this will only get worse with time. Since the beginning of the application inception, we can observe that the grow was steady with each year, until 2015, when the migration happened. Afterwards, the code still grew but at a slower pace.
In fact, by changing to Java EE 7, it was possible to produce the same results, but by writing less code. This may not seem a very big deal with small applications, but when we are talking about 1 Million, it makes a huge difference.
Not only you are being more productive, by consuming less time to implement the same feature, but also the chance to introduce bugs is smaller, since you also have less code to mess around.
No one really wants to change old code, especially if it’s working and even worst, you don’t know exactly why it’s used. But there are a few easy to use features from Java EE 7 (and 6), that you can use straight away when moving from Java EE 5.
Remember the tedious work to get an EJB in a different context, like a Servlet:
These examples are just the tip of the iceberg on how you can simplify your code. There are many more examples, but these are the main ones used in this project.
Please post your examples in the comments section.
Also, if you would like to learn more about check my session, Migration Tales from Java EE 5 to 7 which covers some of the solutions we had to implement to completely migrate an application. Each case is different and there is no right recipe, but it can give you a good idea on the path you need to walk to achieve your goal.
When building a real application you often find yourself having to deal with different stages of the software. The most common stages are development, testing and production, but you can have many more. This means that you need a different environment to deploy the application on each of the current stages. You use different environments to be able to perform versioning, different configurations, test bug fixes and so on. This also poses challenges on upgrading environments, changing shared configuration or keeping track of the servers. I will show you how to do it with Tomcat.
The easiest way to set up multiple Tomcat instances is to duplicate the entire Tomcat folder and change a few configurations. I don’t advise doing it this way, since it’s harder to maintain, harder to spin up new instances and harder to upgrade. Instead, we will set up the instances in a much more flexible way, by duplicating only a few things and keeping a shared base folder for all instances.
I’ve used version 7.0, but this should also work with other versions. I’m also doing the setup in a Unix like environment. This can also be accomplished in a Windows box, but the commands need to be adjusted.
Unzip the installation folder to a directory of your choice. I just recommend to do it in a parent folder and you can use a name like tomcat or server.
Now, instead of using the unzipped folder, we are going to create a link to it, like this: ln -s apache-tomcat-7.0.64/ current
If you wish, you can now remove these folders from the Tomcat install folder, but is not mandatory.
Home and Base
The idea here is to share the main Tomcat folders and each instance has a copy of their personal folders to not clash with each other. Tomcat defines two environment variables called CATALINA_HOME and CATALINA_BASE that allow us to do that.
Create a bin folder in the instances development. Add the following exec.sh script:
Note: If you are using MacOSX, you might need to install core-utils using brew and replace readlink by greadlink to achieve the proper behaviour.
This script is going to set up the proper configuration variables to point to our shared Tomcat and the specific instance folders. Note the properties http.port, https.port, ajp.port and shutdown.port are included in the CATALINA_OPTS environment variable. With these we can pass specific configuration to the server.xml file. Tomcat is smart enough to perform property replace substitution as long as you have the proper placeholders in place.
All these operations are performed in the development folder instance.
Edit the file conf/server.xml do the following changes;
Note: Unfortunately the only place where property replacement doesn’t work is the shutdown port. I think this is a bug in Tomcat and should be fixed. So for now, we need to keep it hardcoded.
On the bin folder, create links to exec.sh to the following files: catalina.sh, startup.sh, shutdown.sh.
This will allow you to call the original Tomcat, but by calling the exec.sh set up first. The magic is done by the line $CATALINA_HOME/bin/"$(basename "$0")" "$@" in the exec.sh script.
The instance should be ready to be executed. Just run it as you would do it normally by executing sh catalina.sh run or sh startup.sh from the development instance folder.
Just duplicate the development instance folder to a production one and edit the bin/exec.sh to update it with different ports. You can user 7080 for http, 7443 for https, 7009 for ajp and 7005 for the shutdown.
Since property replacement is not working properly for the shutdown port, we need to manually edit conf/server.xml from the production instance and replace 8005 by 7005. When this bug is fixed, and you actually use a property, you don’t have to worry about doing this.
Note: You might need to reestablish the proper links in the scripts catalina.sh, startup.sh and shutdown.sh stored in the bin folder.
After this, your second instance production is ready to run. If you need more, just repeat the last steps, making sure to pick ports that don’t conflict with the instances already set up.
With this set up you can now:
Create new instances easily with minimum changes. You can actually have one untouched unchanged instance, that you can use to copy from to create others.
Update the Tomcat version, just by installing a new distribution and updating the link to current.
If you place jars in the libs folder of the HOME installation, they become instantly available to all instances.
Instead of duplicating the conf folder, you can actually link to the one in HOME and also share the configuration between all environments. Or just link to the files you want to share.
Remove an instance, by just deleting its BASE folder.