The Maven Application Server
After spending more time than I care to indulge trying to get commercial application servers to behave, I finally decided to Do the Simplest Thing that Could Possibly Work, and create a new application server from scratch. Well, not really a full application server. For 90 % of the Java applications out there, all that you really need is Servlets, so I limit myself to that. And not really from scratch. The servlet functionality is provided by jetty, the application is built using Maven, the startup script is generated with the Maven Appassembler Plugin, and the final package is constructed using Maven Assembly Plugin.
The result is a project that lets me construct and release zip-files that can be copied onto an server host, extracted, and started up with a single script. With some help from my friends, I’ve added a few bells and whistles to make it as complete as possible. However, this article describes the simplest possible “Maven application server” that you can start with. You can find the application code in my subversion repository.
The life-cycle of application development
My main rationale for creating the Maven Application Server was to take control of my whole application development life-cycle. Most application server vendors consider the whole lifespan from test to production too naïvely. Here is what I want to do:
- I find a new requirement or bug, that requires me to change the code.
- First, I implement a test for the change in my IDE. This is done by checking out the main trunk from subversion and executing
mvn eclipse:eclipse -DdownloadSources=true
in the top level directory. I can then import the files and work with them as normal. - Second, I want to make sure that the web module runs correctly. As per my article on the subject, I have created a unit tests that fires up an Jetty instance and tests that it can access the web application. I have also created a Main Class, which can be started under the IDE debugger. This way, I can quickly debug the problem if my test fails.
- When I’m satisfied, I run
mvn deploy
on the top level directory. This pushes out a new snapshot version of the whole application to a maven repository (the example uses a local disk for illustration purposes). This ZIP-file can be downloaded to the workstation of a tester. The tester extracts the file and double clicks the start script that was generated by the appassembly plugin. The tester can then check the functionality more throughly (try downloading the zip file, unzip it and double-click the reference-app.bat script, and go to http://localhost:9090/reference-web/). We have also created scripts that automatically pull down the latest version to integration test servers as part of our automated build. - When we’re happy with the version, we can use
mvn release:prepare
andmvn release:perform
to construct a released version. This tags the code in svn and pushes out a final version of the zip file to the Maven repository. - We then typically use scripts on the staging environment to pull down the released version from the Maven repository. If everything works there, we pull down and start the server on the production environment.
- This completes one development cycle.
Some work left for the reader (or a later article)
The use of the appassembler plugin is currently a bit rough. However, Trygve and Kristian, the developers of the plugin have a version in the works that will fix my needs. Deploying and installing new versions of the ZIP on a server requires a little script. I might post the details on how we set this up in a later entry. Our deployment strategy separates the configuration settings from the software, so we can upgrade the application without having to mess around with the settings. We can also deploy several version on the same host, each having separate settings. The use of Jetty should be expanded to be more sophisticated than my current toy example. You will need to read configuration for things like port numbers and possibly database settings. You might also want to implement functionality for shutting the server down.
Conclusion
By taking responsibility of the application server, we have regained control over the whole software development life cycle. This enables us to eliminate a few of our configuration management issues, and close the loop from unit testing to production. As an added bonus, we have been able to construct the whole solution using only open source components. This both reduce the work we have to do, and our licensing expenses. I would especially like to thank Trygve and Kristian, who’ve created the critical piece to make the whole puzzle fit together.
Comments:
[Stefan Landrø] - Jun 8, 2007
Great stuff! I’ve been thinking about how one can close the gap between test and production for quite a while, and this is definately one of the best approaches I have heard of. Way to go - get in control!
[Jan-Olav Eide] - May 13, 2014
Late arrival to the party here. Is the svn repo you link to still meant to be available ?
Johannes Brodwall - May 13, 2014
Hi Jan-Olav
The article has been superseded by this: /2010/03/08/why-and-how-to-use-jetty-in-mission-critical-production/. (Even that article doesn’t have the full latest version of my thinking on the subject, though)
[Jan-Olav Eide] - May 13, 2014
Ok, I am doing almost exactly the same thing, and am having some issues with appassembler. (still rough around the edges) I was hoping some details on your maven configuration might help me a bit.
Johannes Brodwall - May 13, 2014
The approach in the other article (which has a link to my github repository) is extract the dependencies into the executable war file. It’s not elegant, but it’s the approach I’ve found to be the most practical.
I use zipTree with gradle. I think you can use dependency:unpack-dependencies with maven.
I’ve also heard people report good experience with the shade plugin.