June 27, 2011

Maven's WAR Overlay: What are WAR Overlays?

What is a WAR overlay?

def: Overlays are used to share common resources across multiple web applications. The dependencies of a WAR project are collected in WEB-INF/lib, except for WAR artifacts which are overlayed on to the WAR project itself. http://maven.apache.org/plugins/maven-war-plugin/overlays.html

Why and where would you use a WAR overlay?

WAR overlays make up one more tool in your arsenal to combat repetitive code and/or resources. Overlays allow you to combine many similar resources, for example, images, CSS and JavaScript files all into one project. At build time, the overlay will be predictably merged into a master WAR project. A great example of a WAR overlay is a branded website, combined with Sitemesh decorators. Imagine you have a branded company website which consists of multiple web applications. An industry standard approach would be to utilize Sitemesh decorators to decorate the application with brand specific styles and allow each of your applications to focus on the specific purpose of that application. At this time, this blog post will not go into depth on Sitemesh decorators, however, more details are available at http://www.sitemesh.org/.

Leading on with our example, each of the decorators will need a set of CSS, images and possibly JavaScript files to define the application's branded style. If your applications are to be deployed to production to look and act as one cohesive branded site, you will need to make sure that the images, CSS, JavaScript, decorator files and JSP files are all the same in each of your applications in order to keep a consistant brand across your applications. Well, this is duplicated code (currently ranked in my top 5 biggest "code avoid-at-all-cost" list).

How do you solve code duplication using WAR Overlays?

One approach to reducing the amount of duplicated code is to create a separate maven project with the packaging target type of WAR. Then in each of your application's pom.xml file, include the overlay project as a dependency and construct it as an overlay using the maven-war-plugin. This is known as a Fat WAR Overlay. (NOTE: In the next post I will cover other types of WAR overlays to help mitigate transient dependency problems)

More specifics please!

First, let's create a new maven WAR project and name it "branded-templates". (NOTE: This post is not intended to be a comprehensive guide on creating a maven project, only adding to an already working maven project. If you need more assistance, please refer to some of my other post or maven's website for more guidance.)

In our new branded-templates application, we will need to fill this with our resources. Here is my recommended locations to place web-related resources within your project:
  • /src/main/webapp/styles – used to hold your CSS files, (example: brand.css, reset.css, etc)
  • /src/main/webapp/scripts/ – used to hold brand-wide JavaScript files, (example: jquery.1.x.x-min.js, jquery-my-company-validation.js, etc
  • /src/main/webapp/images/ – used to hold brand-wide images, (example: company-logo.png, facebook-icon.png, company-background.jpg, etc)

Naming for now will not be important, but you will have to make decisions here because you have two naming options here that will have development implications. You can name your resources and/or folders the same in both of your applications or you can name them different to get different desired results. How is this going to impact my design and why do you need to think about this? (Answer coming after we add to the containing webapp project, keep following!)

Let's continue. We can now install your branded-templates WAR overlay to your local maven repository:

mvn clean install

If all is good, you should see a "Successful build" along with a 1.0-SNAPSHOT for your WAR's version.

Next, let's crate a new main application called "main-webapp". If you have an existing webapp, feel free to use that and work along with this example. Inside of the main-webapp's pom.xml file, we need to add a plugin to build the two WARs into one WAR and add the overlay as a dependency to the project.

Add to your dependencies section:
<dependency>
   <groupId>com.yourcompany</groupId>
   <artifactId>branded-templates</artifactId>
   <type>war</type>
</dependency>

Note that we are adding this as a WAR type, do not leave the type blank.

Add to your plugins section:
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-war-plugin</artifactId>
 <version>2.1.1</version>
 <configuration>
   <overlays>
     <overlay>
       <groupId>com.yourcompany</groupId>
       <artifactId>branded-templates</artifactId>
     </overlay>
   </overlays>
 </configuration>
</plugin>

Next, we've added the war-plugin which will take our application and build a WAR. If you're familiar with maven's artifact types, you'll already know that your project builds a WAR already, so why the plugin? The plugin is where the "magic" of the overlay happens. This is where the overlay is placed into the main-webapp's WAR file.

How many WAR files will I have?

When using a WAR overlay, you will start with two or more WAR files (presumably one or more overlays and a main-webapp), but will ultimately end up with one WAR artifact (your main-webapp) in which to deploy to your environment. Though it is completely possible to run your overlay as a standalone application, I recommended you read my next post (coming soon) on dependency management within overlay projects to avoid dependency problems in your main-webapp project.

So what happens when there is a conflicting resource?

As you can imagine, there can be conflicts between overlay's resource names and the main project's resources. This is what I asked you to think about from the question posed above. If you have the same location and named file in your overlay application as you do in your main application, the overlay version will be ignored for the main application's version. This can work for or against you, so think about how you might use both. How would you want to leverage the customization abilities in the main webapp.

Note, if you plan on utilizing Sitemesh decorators, it's not a bad idea to put your decorators.xml and sitemesh.xml file in your WEB-INF folder and the corresponding JSPs in the WEB-INF folder (suggest placing them in /WEB-INF/decorators folder). Remember to add the sitemesh web.xml information into your containing main-webapp's web.xml!

One more gotcha that might arise out of this would be dealing with the web.xml. The overlay's web.xml file is NOT copied over nor is it merged in any way, so if your overlay has specific web.xml file information you can either manually add it to the web.xml or use a <include file="/WEB-INF/web-overlay.xml"> tag where you've added the contents to a file named web-overlay.xml to the overlay war project.

Tips for rapid development

At this point you should have an overlay and a working master webapp leveraging that WAR overlay. Since you have your resources in the overlay and you are probably working in your main-webapp, and it is probably becoming a pain to keep deploying a new version of your overlay project, you can simply refer to the SNAPSHOT version of your overlay inside your main-webapp pom.xml file and do a local install of your overlay.

Leaving notes

Follow my blog and get the next blog post dealing with dependency management with overlays.

Feel free to leave comments or questions and I'll do my best to get back to you in a reasonable time frame. I would like to thank Dynacron Group (my consulting firm) for allowing me some time to work on this blog post!

2 comments:

future said...

Boris Waguia : Great post!
I have this error while processing the overlay :
2011-10-04 08:26:46,914 [main] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Duplicate element detected
Offending resource: file [/home/adorsys/dev/training-workspace/cinema/target/tmp/webinf/WEB-INF/classes/META-INF/spring/applicationContext-security.

can you do somthing for that ?

Mike! said...

Hey Boris,

Though this isn't related to overlays or this post, it is something I might be able to provide some assistance with. Based on the stacktrace you posted here, it looks like you have multiple beans defined in your spring context files. Look for <bean id="NAME_OF_BEAN"... where the "NAME_OF_BEAN" is the same in probably your applicationContext.xml and applicationContext-security.xml files

Mike!