Thursday, November 4, 2010

Hot-deploying from Eclipse

Article note: This article is in dire need of example screenshots. I'll add them as soon as I've made them.


Today we have fancy IDEs like Eclipse and Netbeans that can do many mundane tasks for us. One such task is building and packaging a JEE application (although I still prefer to do that through Maven).

The one thing where any IDE fails, in my honest opinion, is development deployment of a JEE application. Netbeans gets far when deploying a simple jsp/servlet based web application, but when taking the next step and trying to deploy a full JEE application, things just don't work like I want them to. Either certain resources are not deployed, they are deployed in packaged form, they contain the wrong names or they end up in a place I don't want them to be.

Now you can try to get specialized plugins like Eclipse's JBossTools to do the deployment for you, or you do it my way - which is to take full control over application deployment. What I will describe to you now is a way to setup your development environment, using Eclipse, to hot-deploy files to the server as you modify them (automatically of course), making it quite easy to debug and fix your web applications without endless restart cycles.

What is hot-deployment?
Simply to "swap in" classes and web resources in a running server. Most modern servers can instantly swap in servlets, EJBs, MDBs and even configuration resources such as JSF configuration files and datasources without having to restart either the server or the web application (you can force a webapp to reload by touching the web.xml file; JBoss 5.1 even does it when you modify other resource files, such as the application.xml or a JSF configuration file).

The only rule is that there must not be a change in method signature, such as a change in parameters or added/removed fields or methods. When this happens the server needs a reboot unfortunately to load in the changes.

Also, it is not possible (yet) to "swap in" resources that are discovered during server startup, such as entities. You also won't see changes to static members until you reboot (a good reason to avoid statics when you can IMO).


The project setup

Since I'm a big fan of Maven, lets take the average Maven project as an example. When we create a Maven project with an EJB, WAR and EAR module, we'll end up with the following set of directories after a clean install:

Eclipse resources:
example/exampe-ejb/src/main/java
example/example-ejb/src/main/resources
example/example-ejb/target/classes
example/example-web/src/main/java
example/example-web/src/main/webapp
example/example-web/src/main/resources
example/example-web/target/classes

Maven specific resources:
example/example-ear/target/example/lib
example/example-ear/target/example/META-INF
example/example-web/target/example-web/WEB-INF/lib

Of course most of these names are dependent on settings in the pom files. Note that to let Maven create consistent directory names, you should give all your modules finalName entries to make sure no version numbers end up in the filenames (as described in my maven article).

The last three entries are specific to Maven and are quite important to us - Maven is responsible for not only collecting all the dependencies of the EAR and the WAR module, but also for generating the application.xml file. We'll need these resources if we want to succesfully hot-deploy the application. All the other resources come from the Eclipse project and are those that are actively developed.

Don't use Maven?
Well you still need the libraries and application.xml file. One option open to you is to simply collect these resources yourself - put them in a directory 'hot-deploy' and in this directory create subdirectories for the EAR and the WAR, in which you put the libraries and application.xml file. This is no problem as these resources should be mostly static.


Eclipse project setup

Note that on the Eclipse side, you don't need more than a mavenized Java project; no need for fancy JEE, EJB or whatever projects. Maven will do all our stuff, Eclipse shouldn't. (but you may want to install the JBossTools plugin set and add JSF capabilities to your project).

There is one requirement: build automatically must be activated for this to work!


Enter the Eclipse filesync plugin

The one plugin that makes the whole setup possible is the excellent filesync plugin for Eclipse, which you can download here:

filesync plugin

Or simply use the Eclipse Marketplace, where you can also find it.

This plugin allows you to manually define which parts of your project you want to sync (and which you specifically do not) and where to put them. Not only that, but synced files will be monitored, and when you modify them in the source location, they are automatically synced to the destination location (again: if build automatically is turned on so the .class file is automatically recreated).

Configure the hot-deploy settings

Lets create a basic configuration. Lets say we have a JBoss 5.1 server with an instance 'myapp' where we want to hot-deploy our phantom mavenized JEE application. Open up the project properties; now that the filesync plugin is installed, there should be a menu item file synchronization present. Select it.


The first thing you will want to do in the resulting window, is add all the directories of your project that you want to sync to the server. This will be the following in our maven project:

WhatWhy
example-ejb/target/classesEJB classes and resources (Eclipse)
example-web/target/classesWAR classes and resources (Eclipse)
example-web/src/main/webappWAR web resources (Eclipse)
example-ear/target/example/libEAR library dependences (Maven)
example-ear/target/example/META-INFgenerated application.xml (Maven)
example-web/target/example-war/libWAR libraries (Maven)

These folders contain all the resources we need to construct our full EAR application. Note that things that are dynamic are taken from the directories where Eclipse does its thing; this makes it so the hot-deployment will work on save. If you would take the Maven generated classes the hot-deploy would only work after you do a mvn clean install and refresh the project view.

Now before we continue, there are two more configurations to be done. First of all, check the checkboxes allow different target folders and use current date for destination files.

Next, see the input field default target directory. This is where we want to put our files; we make this point to our deployed EAR directory.


EAR... directory?

Yes, not an EAR file, but an EAR directory. JBoss, like many other JEE environments, supports what can be called exploded deployment. This means that you don't package up your application in an EJB jar, a web WAR and all that inside an application EAR; in stead, you create a folder structure that pretends to be these files.

Thus, go to your JBoss instance's deploy directory and create there the following directory structure:

deploy\example.ear
deploy\example.ear\META-INF
deploy\example.ear\example-ejb.jar
deploy\example.ear\example-web.war
deploy\example.ear\example-web.war\WEB-INF
deploy\example.ear\example-web.war\WEB-INF\classes

Making this directory structure by hand makes it easy for us to configure the next steps of filesync as it will allow the plugin to open up the existing directory by default in stead of starting at the root.

Now, make the default target directory point to the myapp.ear directory you just created. You are now ready for the final steps.

Configure destinations

With our list of source directories input into filesync and the allow different target directories checkbox checked, you will now see an option target folder under each source item. Here we can now configure where the content of the source items should be hot-deployed to. The following table illustrates the mapping.

FromTo
example-ejb/target/classesexample.ear/example-ejb.jar
example-web/target/classesexample.ear/example-war.war/WEB-INF/classes
example-web/src/main/webappexample.ear/example-war.war
example-ear/target/example/libexample.ear/lib
example-ear/target/example/META-INFexample.ear/META-INF
example-web/target/example-web/libexample.ear/example-web.war/WEB-INF/lib

Testing it out

With the configuration part done, save settings and close the project properties. Now, right-click on the project node in the project explorer and you should see an item force file synchronization. This does exactly what it describes; it will synchronize all modified files from the source locations to the destination locations.

Tip: with the server running, the force file synchronization will most certainly cause the application to be redeployed.

Now open up your deploy directory and see where the files ended up in the server; if everything is correct you should now have a flat directory structure matching exactly how the EAR file would be composed. If not, make modifications to the filesync settings until you get the correct setup.

The final step is simple: start JBoss and check the startup log for errors.

Oh and by the way: filesync is totally capable of recreating the directories. You can safely remove the hot-deployed application from the deploy directory, force file sync and have the complete application recreated fresh. This is sometimes necessary when certain items have been removed from the source; filesync does not automatically remove them in the destination environment. I tend to make it a habit to remove and completely redeploy an application whenever I make big changes.

Testing it out part 2: hot-deploy a modified file


You have a hot-deployed application now? Good. Now lets try out the real benefit of this plugin. Run the server and open up your web application, for example a specific page. Now modify the content of this page in Eclipse and save.

Reload the page in the browser - if everything is correct, you should now see your modified page. And all you had to do was save the file.

Try it out with EJBs and servlets if you want, you'll see the changes made to them (if they don't break the rules explained earlier) are automatically available in your running application.


Troubleshooting


Problem: large files (such as libraries) end up as 0 bytes in size after hot-deployment
Solution: increase the permgen space for Eclipse to 256mb. You can do this in the eclipse.ini file (XXMaxPermSize). If that doesn't help, you probably removed a file and forgot to refresh the project view.

Problem: 'module does not exist' error during startup
Solution: check the application.xml file that maven generated; the name of the EJB and WAR modules must be the same as the directories you created in the JBoss deploy directory.

Problem: library, class or resource is not hot-deployed while it exists
Solution: filesync only sees what Eclipse sees; did you refresh your project view?

Problem: files in the resources directories aren't synced
Solution: for some reason, sometimes Eclipse wants to exclude all files in the resources directories by default. Check this in the build path of your project settings.

Problem: filesync throws an error when I select its menu item in the project properties
Solution: this is an Eclipse thing; sometimes the project facets are screwed up. First try restarting Eclipse. If that doesn't work, open the .project file of your project, you should see something along the lines of:

<buildSpec>
  <buildCommand>
   <name>org.eclipse.jdt.core.javabuilder</name>
   <arguments>
   </arguments>
  </buildCommand>
  <buildCommand>
   <name>org.maven.ide.eclipse.maven2Builder</name>
   <arguments>
   </arguments>
  </buildCommand>
  <buildCommand>
   <name>de.loskutov.FileSync.FSBuilder</name>
   <arguments>
   </arguments>
  </buildCommand>
 </buildSpec>
 <natures>
  <nature>org.maven.ide.eclipse.maven2Nature</nature>
  <nature>org.eclipse.jdt.core.javanature</nature>
 </natures>

If it all looks fine, you can try removing/renaming the .settings/de.loskutov.FileSync.prefs file in your workspace. But this will destroy all settings you did for filesync.


Problem: JBoss doesn't see the changes I made
Solution: I don't really have a solution, but I'm pretty sure there are ways to specifically configure JBoss to not scan for resource changes. That will of course defeat the purpose of hot-deployment. A JBoss instance you download directly from the JBoss website should work flawlessly. If not: well it works on my machine!

4 comments:

  1. eclipse fileSync uses an eclise builder so will only work when you build; set your project to build automatically.

    ReplyDelete
  2. I quote from the article under Eclipse project setup:

    "There is one requirement: compile on save must be activated for this to work!"

    Even if you don't know about the builders it is already obvious; you hot-deploy the class files, so you will want those class files to be updated when you save your java source file.

    ReplyDelete
  3. ... only that is what it is called in Netbeans; changed in the article to "Build automatically".

    ReplyDelete