Solving the Skinny Wars problem


Added by Barend Garvelink, last edited by Barend Garvelink on Apr 08, 2008  (view change) show comment

Labels

 
Why does this page exist?
Having run into the "skinny wars" issue, I've found there's a lot of info about this, but it's very fragmented. This page is an attempt at centralizing all that information to eventually work out a lasting solution to this problem. Please chime in with whatever you have to add. Thanks! --Barend Garvelink

Summary

There are a lot of issues in the Maven 2.x JIRA about EAR files containing "skinny" WAR files and the manifest class path. Some of these have been resolved, many are unresolved. Some of the solutions currently in place are work-arounds rather than real solutions. The key points:

  1. By default, all dependencies of a WAR module are contained in the WEB-INF/lib folder of a WAR file.
  2. The J2EE spec allows a WAR file (or in fact any other module) contained within an EAR to refer to other modules in that EAR file by mentioning them in the manifest Class-Path. These modules can then be removed from the WEB-INF/lib folder, creating what's informally known as a "skinny" WAR file. This can avoid duplication of library modules.
  3. The current procedure for creating a skinny WAR is explained in [SKIN]. The main problem with this approach is that it requires duplication of dependency data from the WAR project's file into the EAR project's pom. There are more subtle problems too, explained in the next section.

Notes:

  • wrt. #1: This is the correct behaviour for stand-alone WAR modules and should remain the default.
  • wrt. #2: In fact, the J2EE specification requires that duplication is eliminated in this way, as mentioned in [MC06].

Problems with the current approach

Here's an overview of the JIRA issues touching on this subject. If you spot any omissions, please add them to the table.

Key Title Status
MWAR-9 WAR plugin should support minimal WARs for inclusion within an EAR Open
MWAR-21 Need a way to include limited set of webapp's dependencies Closed fixed (with commenters protesting)
MWAR-61 Document how to set manifest classpath and exclude dependency from WEB-INF/lib Closed fixed
MWAR-81 Request enhancement to pattern matching for warSourceIncludes/warSourceExcludes functionality (regular expressions?) Open
MWAR-111 Transitive dependencies of optional dependencies are included in WEB-INF/lib Open
MWAR-135 Add an option to exclude content from the generated archive to implement the skinny war process Closed fixed
MWAR-146 Better support for skinny wars Closed won't fix
MNG-1991 Can't get transitive dependencies from a war pom when this war is added as a depdency of a project Open
MNG-3269 Different builds for ejb-client optional with parent Open
MEAR-17 Jar files packed in the EAR file should also be added to application.xml or manifest.mf Closed won't fix
MEAR-60 Improve support for skinny WAR files Open
MEAR-75 Incorrect file name in class path (in manifest) if specifying different bundleFileName for module Open

MNG-3269 is particularly insidious.

The warSourceExcludes parameter used in [SKIN] feels like a hack to me. The maven-war-plugin figures out what to include like it always does, puts it neatly into a working directory under target/, then when it zips up the final artifact the files listed magically disappear. This 'hackishness' is reflected in the fact that the warSourceExcludes argument affects the standard war:war goal, but is ignored in war:exploded and war:inplace. In my opinion, this parameter should be deprecated and disposed of as soon as a better solution is in place.

While [SKIN] gives you reasonable control over where the libraries end up (albeit awkwardly because of the duplication of the <dependency> elements), the amount of control you have over the manifest class-path is too little. You can configure the entire thing by hand, which is awkward in its own right, or you can influence it with the provided scope (subject to MNG-3269), but that's not really powerful. For example, if you want to use the <bundleFileName> and/or <defaultLibBundleDir> configuration elements of the maven-ear-plugin, you cannot automatically provide a correct manifest class-path.

Patches currently in JIRA

The following issues include patches: MWAR-9, MWAR-21, MWAR-61, MWAR-111, MWAR-146, MWAR-3269.

Note that all of these address the maven-war-plugin.

Whose problem is this, anyway?

This issue is reported against both the maven-war-plugin and the maven-ear-plugin. Most of the patches seem to be for the maven-war-plugin. However, in my opinion "skinny" is an issue that should be addressed in the maven-ear-plugin, not in the maven-war-plugin.

My arguments for this are:

  1. A war file is valid as a stand-alone deployment unit. This issue specifically affects war files (and any other modules) that are embedded into an ear file. A war project build need not (and arguably should not) be aware of whether its artifact is ultimately deployed as a fat stand-alone module or as a skinny ear component.
  2. An EAR file can contain internal directory structure (a lib folder is not uncommon) that must be reflected in the manifest class-path of the embedded modules.
  3. The Manifest class-path is a concern not just for WAR modules, but for other module types embedded into an EAR as well.
  4. Any module can become part of more than one EAR file.
  5. Any module that gets embedded into an EAR file ceases to exist as a stand-alone artifact in that scope, justifying the ear plugin's mucking about in a module's contents.

Note that the common way of addressing #1 and #2 (in the build spec of the embeddable artifact) surreptiously creates a circular dependency problem.

Requirements for a lasting solution

As mentioned before, I think this issue should be addressed in maven-ear-plugin, not in maven-war-plugin.

# Summary
1 The maven-ear-plugin must (be able to) (re)write the manifest class path of any module embedded into its artifact ear file.
2 When (re)writing a manifest class path, the effect of any <bundleFileName> configuration must be taken into account.
3 When (re)writing a manifest class path, the effect of any <defaultLibBundleDir> configuration must be taken into account.
4 The maven-ear-plugin must be able to (selectively) prune the WEB-INF/lib folder any WAR artifacts it embeds.
5 When (re)writing a manifest class path, take conflict resolution into account (Marcel Schutte)
6 Must be able to specify in the ear pom which libraries are affected by #1 and #4 (Bryan Loofbourrow)
  please contribute

References

id href
SKIN Creating skinny wars
MC06 Comment by Mark Chesney in MWAR-21 on 2006-03-26
  1. Apr 04

    snicoll says:

    The sourceExcludes setting is now excluding only files in the webapp source dire...

    The sourceExcludes setting is now excluding only files in the webapp source directory (src/main/webapp).

    I've added a packageExcludes that excludes files before packaging. What you're saying is that it should be taken into account with the war:war and war:exploded phase?

  2. Apr 04

    Barend Garvelink says:

    snickoll wrote: What you're saying is that it should be taken into account with...

    snickoll wrote:

    What you're saying is that it should be taken into account with the war:war and war:exploded phase?

    Probably; reason being that I think the contents of a project artifact shouldn't change depending on whether you zip it or not. The effective pom is the same, the contents should be too.

    Truth is, I've so far failed to see a use for the sourceExcludes and packageExcludes parameters outside the role they currently play in solving the skinny wars problem, which I think is the wrong pom to solve that problem in. They might have a reasonable use in some other scenario.

  3. Apr 04

    Marcel Schutte says:

    Thanks Barend for doing this work, support for skinny wars is important for me t...

    Thanks Barend for doing this work, support for skinny wars is important for me too. A few remarks:

    • When (re)writing a manifest class path, the effect of version conflict resolution should be taken into account
    • These requirements are applicable to every artifact that includes other artifacts, so for instance the war plugin should rewrite the manifest class path in case of a resolved version conflict
    • The use of 'optional' and 'provided' in the war plugin to select a combination of manifest class path and/or web-inf/lib inclusion is a hack
  4. Apr 04

    Barend Garvelink says:

    Good point, I've added it to the requirements table. Good point, I hadn't thoug...
    • Good point, I've added it to the requirements table.
    • Good point, I hadn't thought of that.
    • Agreed.
  5. Apr 07

    Bryan Loofbourrow says:

    Thanks for doing this page. I like your point of view that this is a problem tha...

    Thanks for doing this page. I like your point of view that this is a problem that should be solved in the ear if possible. Since the main motivation for my use of skinny wars is eliminating duplication of jars in memory, I could accomplish most of my purpose by simply turning on an "move duplicate war jars into the ear" flag on the ear plugin, if only such a thing existed.

    One thing you do not include in your summary of the issues is the use case of needing some dependencies for-sure packaged in the war. I've found that there are some things (tag libaries, for example) that don't work unless they're packaged in the war. I don't know exactly why, but I'm guessing it's because of resource loading by just path, not classpath.

    So it's important to be able to specify what you do and do not want in the war. For me this is one of the worst parts of the present system. Using combinations of includes and excludes, there is no elegant way to do this at all at present.  I expressed this in MWAR-81, which is basically yet another JIRA issue associated with skinny war travails.

  6. Apr 23

    Barend Garvelink says:

    /me regrets not having named this page "mavenenterprisepackaging" or something l...

    /me regrets not having named this page "maven-enterprise-packaging" or something like that.

    A related feature that would be very useful in maven-ear-plugin is automatic gathering of security-role elements from all embedded modules when generating the application deployment descriptor.