Reduce Legacy from Java EE 5 to 7

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.

How?

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.

CDI

Remember the tedious work to get an EJB in a different context, like a Servlet:

getLocalBean public static <T> T getLocalBean(final Class<T> klass) { try { LocalBinding localBinding = klass.getAnnotation(LocalBinding.class); if (localBinding == null) { throw new BeanNotFoundException(“…”); } return (T) initialContext.lookup(localBinding.jndiBinding()); } catch (Exception e) { throw new BeanNotFoundException(“…”); } } 1 2 3 4 5 6 7 8 9 10 11 public static <T> T getLocalBean ( final Class <T> klass ) { try { LocalBinding localBinding = klass . getAnnotation ( LocalBinding . class ) ; if ( localBinding == null ) { throw new BeanNotFoundException ( “…” ) ; } return ( T ) initialContext . lookup ( localBinding . jndiBinding ( ) ) ; } catch ( Exception e ) { throw new BeanNotFoundException ( “…” ) ; } }

Most of these can be simply replaced with @Inject .

No more Local Interfaces

Tedious to always have to define an Interface for your Beans, especially if they were only used locally:

Local EJB @Stateless @Local(UserBusiness.class) public class UserBusinessBean implements UserBusiness { ... } 1 2 3 4 5 @Stateless @Local ( UserBusiness . class ) public class UserBusinessBean implements UserBusiness { . . . }

Just Replace by:

Local EJB No Interface @Stateless public class UserBusinessBean { ... } 1 2 3 4 @Stateless public class UserBusinessBean { . . . }

Singletons

Old fashioned Singleton (maybe not the most correct way to do it):

Old Singleton public class ModuleListener { private static ModuleListener moduleListener; private static ModuleBusiness moduleBusiness; private ModuleListener() { moduleBusiness = BeanFactory.getLocalBean(ModuleBusinessBean.class); } public static ModuleListener getInstance() { if (moduleListener == null) { moduleListener = new ModuleListener(); } return moduleListener; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ModuleListener { private static ModuleListener moduleListener ; private static ModuleBusiness moduleBusiness ; private ModuleListener ( ) { moduleBusiness = BeanFactory . getLocalBean ( ModuleBusinessBean . class ) ; } public static ModuleListener getInstance ( ) { if ( moduleListener == null ) { moduleListener = new ModuleListener ( ) ; } return moduleListener ; } }

You just change it to:

Singleton @Singleton @Lock(LockType.READ) public class ModuleListener { @EJB private ModuleBusiness moduleBusiness; } 1 2 3 4 5 6 @Singleton @Lock ( LockType . READ ) public class ModuleListener { @EJB private ModuleBusiness moduleBusiness ; }

Validations

Since you didn’t have Bean Validation available in Java EE 5, sometimes you had to resort in doing things like this:

Hard Coded Validation public static int fieldEntityMaxLenght(Class clazz, String field) throws Exception { int maxLength = 0; if (field != null) { Column annotation = clazz.getDeclaredField(field).getAnnotation(Column.class); maxLength = annotation.length(); } return maxLength; } public static void rejectIfMaxLengthExceeded(String field, int maxLength) { if (field != null && field.length() > maxLength) { … } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static int fieldEntityMaxLenght ( Class clazz , String field ) throws Exception { int maxLength = 0 ; if ( field != null ) { Column annotation = clazz . getDeclaredField ( field ) . getAnnotation ( Column . class ) ; maxLength = annotation . length ( ) ; } return maxLength ; } public static void rejectIfMaxLengthExceeded ( String field , int maxLength ) { if ( field != null && field . length ( ) > maxLength ) { … } }

Now we can just use @NotNull and @Max annotations into the field that we want to validate.

JMS

It’s a pain to use JMS in Java EE 5:

JMS 1.0 @Resource(mappedName = "java:/JmsXA") private ConnectionFactory connectionFactory; @Resource(mappedName = "java:/jms/queue/EmailQueue") private Destination destination; public void sendAlertsByEmail(Map<Long, String> toSend, List<AlertAttachment> files) { try { Connection connection = connectionFactory.createConnection(); Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(destination); HashMap<String, Alert> dataToSend = new HashMap<>(); for (Long alertId : toSend.keySet()) { log.info(String.format("Sending alert %d to %s", alertId, toSend.get(alertId))); Alert alert = findAlert(alertId); alert.getAlertContent() .setBodyMail(undecorateHTMLLinks( TemplateContextUtils.createMailMessage(alert, Configuration.getInstance()))); dataToSend.put(toSend.get(alertId), alert); } ObjectMessage messageToSend = session.createObjectMessage(); messageToSend.setObject(dataToSend); producer.send(messageToSend); // send message and then clean up session.close(); connection.close(); } catch (Exception e) { log.error("Unexpected error occured", e); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Resource ( mappedName = "java:/JmsXA" ) private ConnectionFactory connectionFactory ; @Resource ( mappedName = "java:/jms/queue/EmailQueue" ) private Destination destination ; public void sendAlertsByEmail ( Map < Long , String > toSend , List <AlertAttachment> files ) { try { Connection connection = connectionFactory . createConnection ( ) ; Session session = connection . createSession ( true , Session . AUTO_ACKNOWLEDGE ) ; MessageProducer producer = session . createProducer ( destination ) ; HashMap < String , Alert > dataToSend = new HashMap <> ( ) ; for ( Long alertId : toSend . keySet ( ) ) { log . info ( String . format ( "Sending alert %d to %s" , alertId , toSend . get ( alertId ) ) ) ; Alert alert = findAlert ( alertId ) ; alert . getAlertContent ( ) . setBodyMail ( undecorateHTMLLinks ( TemplateContextUtils . createMailMessage ( alert , Configuration . getInstance ( ) ) ) ) ; dataToSend . put ( toSend . get ( alertId ) , alert ) ; } ObjectMessage messageToSend = session . createObjectMessage ( ) ; messageToSend . setObject ( dataToSend ) ; producer . send ( messageToSend ) ; // send message and then clean up session . close ( ) ; connection . close ( ) ; } catch ( Exception e ) { log . error ( "Unexpected error occured" , e ) ; } }

With JMS 2.0 and Java EE 7 you can drastically reduce the code and use chaining calls:

JMS 2.0 @Inject private JMSContext context; @Resource(mappedName = "java:/jms/queue/EmailQueue") private Queue inboundQueue; public void sendMessage (Map<Long, String> toSend, List<AlertAttachment> files) { HashMap<String, Alert> dataToSend = new HashMap<>(); for (Long alertId : toSend.keySet()) { log.info(String.format("Sending alert %d to %s", alertId, toSend.get(alertId))); Alert alert = findAlert(alertId); alert.getAlertContent() .setBodyMail(undecorateHTMLLinks( TemplateContextUtils.createMailMessage(alert, Configuration.getInstance()))); dataToSend.put(toSend.get(alertId), alert); } context.createProducer() .setPriority(1)! .setTimeToLive(1000)! .setDeliveryMode(NON_PERSISTENT)! .send(inboundQueue, dataToSend); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Inject private JMSContext context ; @Resource ( mappedName = "java:/jms/queue/EmailQueue" ) private Queue inboundQueue ; public void sendMessage ( Map < Long , String > toSend , List <AlertAttachment> files ) { HashMap < String , Alert > dataToSend = new HashMap <> ( ) ; for ( Long alertId : toSend . keySet ( ) ) { log . info ( String . format ( "Sending alert %d to %s" , alertId , toSend . get ( alertId ) ) ) ; Alert alert = findAlert ( alertId ) ; alert . getAlertContent ( ) . setBodyMail ( undecorateHTMLLinks ( TemplateContextUtils . createMailMessage ( alert , Configuration . getInstance ( ) ) ) ) ; dataToSend . put ( toSend . get ( alertId ) , alert ) ; } context . createProducer ( ) . setPriority ( 1 ) ! . setTimeToLive ( 1000 ) ! . setDeliveryMode ( NON_PERSISTENT ) ! . send ( inboundQueue , dataToSend ) ; }

Moving Forward

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.

Slides:

Video: