Maven WAR Plugin
  1. Maven WAR Plugin
  2. MWAR-21

Need a way to include limited set of webapp's dependencies

    Details

    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.0
    • Component/s: None
    • Labels:
      None
    • Environment:
      M2.0.1
    • Number of attachments :
      3

      Description

      I need a way to pack a war that includes only a limited set of the webapp's dependencies. We're deploying in mainly two different environments: for testing, the webapp runs standalone and thus needs to include all its dependencies in the war. For production we deploy the webapp into a JBoss server that has all the dependencies already installed.

      I've modified AbstractWarMojo in the following way: 1) allow to specify dependencyIncludes an dependencyExcludes (as lists) 2) upon building the war, each dependency is checked against the excludes and the includes and will be added to the war accordingly.

      While this patch may not be the best way to to it, it clearly shows my requirements.

      1. AbstractWarMojo.diff
        3 kB
        Dirk Olmes
      2. AbstractWarMojo.patch
        11 kB
        Jason Melnick
      3. MWAR-21-workaround.patch
        4 kB
        Mark Chesney

        Issue Links

          Activity

          Hide
          Trygve Laugstøl added a comment -

          I think you can solve this more elegantly by having a "test" profile with the extra set of dependencies.

          Show
          Trygve Laugstøl added a comment - I think you can solve this more elegantly by having a "test" profile with the extra set of dependencies.
          Hide
          Dirk Olmes added a comment -

          Ok, I wasn't clear enough ...

          The structure looks more like the following:

          webapp
          struts
          our-ejb-facade
          ejb1-client
          ejb2-client
          ejb3-client
          ...

          The problem is not the dependencies of the webapp itself, it's all the transitive dependencies of the our-ejb-facade module that I need to package or skip.

          I've played around with <scope>provided</scope> on the dependencies for our-ejb-facade but this is not what I want: to run from the development machines, I need ejb-client*, only for deployment on the production server, those dependencies have to be omitted. So for me it's really a packaging thing more than a dependency management thing.

          And no, I don't want to duplicate all the dependencies of our-ejb-facade in the webapp's pom.

          Show
          Dirk Olmes added a comment - Ok, I wasn't clear enough ... The structure looks more like the following: webapp struts our-ejb-facade ejb1-client ejb2-client ejb3-client ... The problem is not the dependencies of the webapp itself, it's all the transitive dependencies of the our-ejb-facade module that I need to package or skip. I've played around with <scope>provided</scope> on the dependencies for our-ejb-facade but this is not what I want: to run from the development machines, I need ejb-client*, only for deployment on the production server, those dependencies have to be omitted. So for me it's really a packaging thing more than a dependency management thing. And no, I don't want to duplicate all the dependencies of our-ejb-facade in the webapp's pom.
          Jason van Zyl made changes -
          Field Original Value New Value
          Workflow jira [ 44581 ] Maven [ 45105 ]
          Hide
          David Boden added a comment -

          I'm packaging three .war files in a .ear. They share most of their dependencies.

          I'm therefore going to include the dependencies as .jar files in the .ear and have them loaded in my container by the EJB ClassLoader. I don't want the .jar files packed into both of the .war files.

          All I need is a switch to say "don't include any of the .jar dependencies".

          One option might be simply to remove the dependencies from my .war pom.xml files. This is not desirable. Why? Because, I want Maven to work out which dependencies to pack in the .ear file.

          Show
          David Boden added a comment - I'm packaging three .war files in a .ear. They share most of their dependencies. I'm therefore going to include the dependencies as .jar files in the .ear and have them loaded in my container by the EJB ClassLoader. I don't want the .jar files packed into both of the .war files. All I need is a switch to say "don't include any of the .jar dependencies". One option might be simply to remove the dependencies from my .war pom.xml files. This is not desirable. Why? Because, I want Maven to work out which dependencies to pack in the .ear file.
          Hide
          Eugene Kuleshov added a comment -

          It looks like there is a missing dependency scope. The one that is required for compilation but not in the runtime.

          Show
          Eugene Kuleshov added a comment - It looks like there is a missing dependency scope. The one that is required for compilation but not in the runtime.
          Hide
          David Boden added a comment -

          >It looks like there is a missing dependency scope. The one that is required for compilation but not in the runtime.

          I strongly agree with this comment. However, I fundamentaly don't understand why "provided" scope doesn't declare transitive dependencies (which would make it exactly fit the bill of a scope that was required for compilation but not for runtime)

          Reasoning:
          If a subproject is dependent at runtime on a particular product then surely the superproject will, at the end of the day, need that product on its runtime classpath. This is the normal use-case. On the odd occasion that we don't want this behaviour, the developer can "exclude" unwanted dependencies and then have a talk with the person who manages the subproject and say "do you really need this dependency at runtime?".

          Speaking of use cases, it worries me that Maven doesn't have a simple description of expected use cases for each plugin right at the top of the documentation. An example that relates to this Jira issue is:
          Use Case: Developer packages multiple war files in an ear and wants to consolidate the dependencies onto the ear classpath. This involves:
          1. Excluding the dependency jar files from the war files.
          2. Marking the war files in a way that the ear file generation process will know to include them.
          3. Changing the ear plugin behaviour so that when it sees that jars are excluded from the war they are ear.
          4. Create entries in the war manifest so that the dependent jars can be loaded from the ear classloader.

          Not an easy use case, by any means, but a very, very common one and one that Maven 2 does not currently support.

          Show
          David Boden added a comment - >It looks like there is a missing dependency scope. The one that is required for compilation but not in the runtime. I strongly agree with this comment. However, I fundamentaly don't understand why "provided" scope doesn't declare transitive dependencies (which would make it exactly fit the bill of a scope that was required for compilation but not for runtime) Reasoning: If a subproject is dependent at runtime on a particular product then surely the superproject will, at the end of the day, need that product on its runtime classpath. This is the normal use-case. On the odd occasion that we don't want this behaviour, the developer can "exclude" unwanted dependencies and then have a talk with the person who manages the subproject and say "do you really need this dependency at runtime?". Speaking of use cases, it worries me that Maven doesn't have a simple description of expected use cases for each plugin right at the top of the documentation. An example that relates to this Jira issue is: Use Case: Developer packages multiple war files in an ear and wants to consolidate the dependencies onto the ear classpath. This involves: 1. Excluding the dependency jar files from the war files. 2. Marking the war files in a way that the ear file generation process will know to include them. 3. Changing the ear plugin behaviour so that when it sees that jars are excluded from the war they are ear. 4. Create entries in the war manifest so that the dependent jars can be loaded from the ear classloader. Not an easy use case, by any means, but a very, very common one and one that Maven 2 does not currently support.
          Hide
          Brett Porter added a comment -

          is this really a blocker? Is anyone actively working on it?

          Show
          Brett Porter added a comment - is this really a blocker? Is anyone actively working on it?
          Hide
          Eugene Kuleshov added a comment -

          Brett, I can't say about subset, but there is no way to exclude certain dependencies from being packaged into war. Eg. servlet-api-2.4.jar is required for most of the sevlet or web framework but it should not be packaged into war because api is provided by container.

          In case when war is packaged within EAR it is even worse, because it may be necessary to put dependencies into EAR in order to be loaded by common classloader between war, ejb jar and rar. So, such common classes must NOT be packaged in war.

          Show
          Eugene Kuleshov added a comment - Brett, I can't say about subset, but there is no way to exclude certain dependencies from being packaged into war. Eg. servlet-api-2.4.jar is required for most of the sevlet or web framework but it should not be packaged into war because api is provided by container. In case when war is packaged within EAR it is even worse, because it may be necessary to put dependencies into EAR in order to be loaded by common classloader between war, ejb jar and rar. So, such common classes must NOT be packaged in war.
          Brett Porter made changes -
          Fix Version/s 2.0 [ 12160 ]
          Hide
          Eugene Kuleshov added a comment -

          BTW, I am not sure if proposed patch is the best idea. m1 allowed to mark artifacts that need to be packaged, but with m2 it looks like there stould be special scope or some additional metadata in for dependencies.

          Show
          Eugene Kuleshov added a comment - BTW, I am not sure if proposed patch is the best idea. m1 allowed to mark artifacts that need to be packaged, but with m2 it looks like there stould be special scope or some additional metadata in for dependencies.
          Hide
          Stéphane Nicoll added a comment -

          I think it's partly my fault. I took the assignement of this issue too early and I am NOT actually working on it.

          Show
          Stéphane Nicoll added a comment - I think it's partly my fault. I took the assignement of this issue too early and I am NOT actually working on it.
          Stéphane Nicoll made changes -
          Assignee Stephane Nicoll [ sni ]
          Hide
          Grzegorz Slowikowski added a comment -

          Eugene, provided scope is for dependencies like servlet-api, jsp-api, etc. (that are needed for compile, but will be present in the container)

          Show
          Grzegorz Slowikowski added a comment - Eugene, provided scope is for dependencies like servlet-api, jsp-api, etc. (that are needed for compile, but will be present in the container)
          Hide
          Eugene Kuleshov added a comment -

          Grzegorz , thats great. But dependencies with such scope currently still included into war.

          Show
          Eugene Kuleshov added a comment - Grzegorz , thats great. But dependencies with such scope currently still included into war.
          Hide
          Grzegorz Slowikowski added a comment -

          I made a test and they were included in the war. A deleted my local repository war plugin and
          a) made a test, 2.0-beta-2 version was used - everything ok
          b) built trunk version (2.0-beta-3-SNAPSHOT) - everything ok too.

          I don't know what version I had in my repository. I know only that is was a snapshot.
          Try to delete maven-war-plugin from your local repo and try again.

          Show
          Grzegorz Slowikowski added a comment - I made a test and they were included in the war. A deleted my local repository war plugin and a) made a test, 2.0-beta-2 version was used - everything ok b) built trunk version (2.0-beta-3-SNAPSHOT) - everything ok too. I don't know what version I had in my repository. I know only that is was a snapshot. Try to delete maven-war-plugin from your local repo and try again.
          Brett Porter made changes -
          Workflow Maven [ 45105 ] Maven New [ 52443 ]
          John Tolentino made changes -
          Assignee John Tolentino [ jtolentino ]
          John Tolentino made changes -
          Status Open [ 1 ] In Progress [ 3 ]
          Hide
          John Tolentino added a comment -

          DIRK OLMES:

          Usecase: Pack a war that includes only a limited set of the webapp's dependencies.

          Solution: Profiles

          Usecase: To run from the development machines, need ejb*-client, only. And for deployment on the production server, those dependencies have to be omitted.

          Solution: Still profiles. Create a "test" profile where the dependencies used only for the development machines (ejb*-client) are declared.

          Concern: Don't want to duplicate all the dependencies of our-ejb-facade in the webapp's pom.

          Answer: There will be no duplicates here because, like what Trygve said, you only need to declare the uncommon artifacts (in this case your ejb*-client dependencies).

          Related URL:

          --------------------------------

          DAVID BODEN:

          Usecase: Packaging three .war files in a .ear. They share most of their dependencies. Same as "Developer packages multiple war files in an ear and wants to consolidate the dependencies onto the ear classpath."

          EAR-PROJECT (parent)

          • WAR1 (child-module)
          • WAR2 (child-module)
          • WAR3 (child-module)

          Need to include the dependencies as .jar files in the .ear and have them loaded in my container by the EJB ClassLoader.

          But don't want the .jar files packed into both of the .war files.

          Solution:

          PARENT-POM

          • EAR-PROJECT
          • PARENT-POM2
            • WAR1
            • WAR2
            • WAR3

          Declare common dependencies in WAR1, WAR2 and WAR3 in PARENT-POM2 with provided scope.

          Also declare these dependencies (without the provided scope) in the EAR project.

          Add WAR1, WAR2 and WAR3 to EAR-PROJECT's dependencies as well.

          Verified this setup works in a similar project. WAR1, WAR2 and WAR3 builds but their common dependencies are not included in the wars. EAR-PROJECT includes all the WARs and their dependencies in its packaging.

          --------------------------------

          EUGENE KULESHOV:

          Need: Dependency scope that is required for compilation but not in the runtime. Same As "Way to exclude certain dependencies from being packaged into war. Eg. servlet-api-2.4.jar is required for most of the sevlet or web framework but it should not be packaged into war because api is provided by container."

          Answer: Provided scope, As was mentioned by Grzegorz Slowikowski. Also verified this and war builds but does not add jars with provided scope.

          Related URLs:

          Show
          John Tolentino added a comment - DIRK OLMES: Usecase: Pack a war that includes only a limited set of the webapp's dependencies. Solution: Profiles Usecase: To run from the development machines, need ejb*-client, only. And for deployment on the production server, those dependencies have to be omitted. Solution: Still profiles. Create a "test" profile where the dependencies used only for the development machines (ejb*-client) are declared. Concern: Don't want to duplicate all the dependencies of our-ejb-facade in the webapp's pom. Answer: There will be no duplicates here because, like what Trygve said, you only need to declare the uncommon artifacts (in this case your ejb*-client dependencies). Related URL: http://maven.apache.org/guides/introduction/introduction-to-profiles.html -------------------------------- DAVID BODEN: Usecase: Packaging three .war files in a .ear. They share most of their dependencies. Same as "Developer packages multiple war files in an ear and wants to consolidate the dependencies onto the ear classpath." EAR-PROJECT (parent) WAR1 (child-module) WAR2 (child-module) WAR3 (child-module) Need to include the dependencies as .jar files in the .ear and have them loaded in my container by the EJB ClassLoader. But don't want the .jar files packed into both of the .war files. Solution: PARENT-POM EAR-PROJECT PARENT-POM2 WAR1 WAR2 WAR3 Declare common dependencies in WAR1, WAR2 and WAR3 in PARENT-POM2 with provided scope. Also declare these dependencies (without the provided scope) in the EAR project. Add WAR1, WAR2 and WAR3 to EAR-PROJECT's dependencies as well. Verified this setup works in a similar project. WAR1, WAR2 and WAR3 builds but their common dependencies are not included in the wars. EAR-PROJECT includes all the WARs and their dependencies in its packaging. -------------------------------- EUGENE KULESHOV: Need: Dependency scope that is required for compilation but not in the runtime. Same As "Way to exclude certain dependencies from being packaged into war. Eg. servlet-api-2.4.jar is required for most of the sevlet or web framework but it should not be packaged into war because api is provided by container." Answer: Provided scope, As was mentioned by Grzegorz Slowikowski. Also verified this and war builds but does not add jars with provided scope. Related URLs: http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html http://maven.apache.org/ref/2.0.3-SNAPSHOT/maven-model/maven.html
          John Tolentino made changes -
          Status In Progress [ 3 ] Open [ 1 ]
          John Tolentino logged work - 24/Mar/06 2:52 AM
          • Time Spent:
            6 hours
             
            Understanding the usecases and concerns of those who posted and put comments on this issue; verified existing profiles and dependency mechanism behavior; and stepped through current version of maven-war-plugin to confirm artifacts included in the packaging.
          John Tolentino made changes -
          Remaining Estimate 0 minutes [ 0 ]
          Time Spent 6 hours [ 21600 ]
          John Tolentino made changes -
          Resolution Fixed [ 1 ]
          Status Open [ 1 ] Closed [ 6 ]
          Hide
          Mark Chesney added a comment -

          You're missing an important aspect of J2EE packaging. If a dependency is included in the EAR to be used by J2EE modules within the same EAR, it must be listed in the manifest of that module. To have these dependencies included in the manfiest of the WAR using existing mechanisms, the WAR plugin must be configured like like so:

          <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <configuration>
          <archive>
          <manifest>
          <addClasspath>true</addClasspath>
          </manifest>
          </archive>
          </configuration>
          </plugin>

          Provided scope dependencies will not be included in the WAR manifest. Compile and Runtime scope dependencies will be added to the WAR manifest. The WAR plugin also packages compile and runtime scope libraries in WEB-INF/lib. Not only is this redundant, but it can cause Class Cast Exceptions. The J2EE spec allows for any combination of WAR dependencies packaged in the EAR or within WEB-INF/lib, not just one or the other. Please see the J2EE 1.4 specification (http://java.sun.com/j2ee/j2ee-1_4-fr-spec.pdf) section 8.2, for valid J2EE packaging use cases.

          Show
          Mark Chesney added a comment - You're missing an important aspect of J2EE packaging. If a dependency is included in the EAR to be used by J2EE modules within the same EAR, it must be listed in the manifest of that module. To have these dependencies included in the manfiest of the WAR using existing mechanisms, the WAR plugin must be configured like like so: <plugin> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> Provided scope dependencies will not be included in the WAR manifest. Compile and Runtime scope dependencies will be added to the WAR manifest. The WAR plugin also packages compile and runtime scope libraries in WEB-INF/lib. Not only is this redundant, but it can cause Class Cast Exceptions. The J2EE spec allows for any combination of WAR dependencies packaged in the EAR or within WEB-INF/lib, not just one or the other. Please see the J2EE 1.4 specification ( http://java.sun.com/j2ee/j2ee-1_4-fr-spec.pdf ) section 8.2, for valid J2EE packaging use cases.
          Hide
          Mark Chesney added a comment -

          I am attaching the workaround I use which supports the use case where all webapp dependencies are in the EAR. The workaround is activated when the the archive.manifest.addClasspath configuration property is set to true like so:

          <plugins>
          <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <configuration>
          <archive>
          <manifest>
          <addClasspath>true</addClasspath>
          </manifest>
          </archive>
          </configuration>
          </plugin>
          </plugins>

          Not bundling runtime/compile dependencies for a WAR packaged within an EAR is necessary because of the J2EE spec application assembly requirement 8.3.1e: There must be only one version of each class in an application.

          This workaround does not support the use case of a combination of EAR and WEB-INF/lib dependencies which the J2EE spec supports.

          Show
          Mark Chesney added a comment - I am attaching the workaround I use which supports the use case where all webapp dependencies are in the EAR. The workaround is activated when the the archive.manifest.addClasspath configuration property is set to true like so: <plugins> <plugin> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> </plugins> Not bundling runtime/compile dependencies for a WAR packaged within an EAR is necessary because of the J2EE spec application assembly requirement 8.3.1e: There must be only one version of each class in an application. This workaround does not support the use case of a combination of EAR and WEB-INF/lib dependencies which the J2EE spec supports.
          Mark Chesney made changes -
          Attachment MWAR-21-workaround.patch [ 19814 ]
          Mark Chesney made changes -
          Link This issue is related to MWAR-9 [ MWAR-9 ]
          Mark Chesney made changes -
          Link This issue is related to MEAR-17 [ MEAR-17 ]
          Hide
          Al Robertson added a comment -

          I am surprised this is marked as fixed. I don't believe it is.

          Here is a structure that J2EE requires you to support
          ear
          jar1
          war ->manifest classpath:jar1
          jar2

          war depends on jar1 and jar2

          Currently, you can specify to add a manifest classpath entry. This adds an entry for each dependency that is not "Provided", because provided means "provided by the container". (CORRECT)
          In the above example, I can't configure maven to exclude jar1 from the war/WEB-INF/lib dir but include it in the classpath manifest.

          It appears to me that we need another dependency attribute to accompany scope "Provided", say <providedByManifestClasspath>true</providedByManifestClasspath> or a <providedBy>CONTAINER|MF-CP</providedBy> etc.
          In this solution, we wouldn't need the manifest->addClasspath plugin config. The war builder would only include the artifact in the war when the scope wasn't provided and the war archiver would know when to include the dependency in the manifest classpath.
          You would still require the manifest classpath prefix config.

          Does this make sense?
          Is there a way to accomplish this already?

          Show
          Al Robertson added a comment - I am surprised this is marked as fixed. I don't believe it is. Here is a structure that J2EE requires you to support ear jar1 war ->manifest classpath:jar1 jar2 war depends on jar1 and jar2 Currently, you can specify to add a manifest classpath entry. This adds an entry for each dependency that is not "Provided", because provided means "provided by the container". (CORRECT) In the above example, I can't configure maven to exclude jar1 from the war/WEB-INF/lib dir but include it in the classpath manifest. It appears to me that we need another dependency attribute to accompany scope "Provided", say <providedByManifestClasspath>true</providedByManifestClasspath> or a <providedBy>CONTAINER|MF-CP</providedBy> etc. In this solution, we wouldn't need the manifest->addClasspath plugin config. The war builder would only include the artifact in the war when the scope wasn't provided and the war archiver would know when to include the dependency in the manifest classpath. You would still require the manifest classpath prefix config. Does this make sense? Is there a way to accomplish this already?
          Hide
          Jason Melnick added a comment -

          No, this isn't fixed.

          Although the war plug-in's documentation has examples of how to do this it doesn't work properly due to a problem with M2's resolution of optional dependencies: see http://jira.codehaus.org/browse/MNG-3067

          I have a fix for it (the war plugin, not the dependency mechanism) of which I'll post the patch ASAP...

          The problem is that somehow optional dependencies are being resolved for optional dependencies arbitrarily.

          The fix involves removing ResolutionNodes at the parent-level w/ a depth > 1 from the ArtifactResolutionResult as well as removing artifacts that are optional.

          Show
          Jason Melnick added a comment - No, this isn't fixed. Although the war plug-in's documentation has examples of how to do this it doesn't work properly due to a problem with M2's resolution of optional dependencies: see http://jira.codehaus.org/browse/MNG-3067 I have a fix for it (the war plugin, not the dependency mechanism) of which I'll post the patch ASAP... The problem is that somehow optional dependencies are being resolved for optional dependencies arbitrarily. The fix involves removing ResolutionNodes at the parent-level w/ a depth > 1 from the ArtifactResolutionResult as well as removing artifacts that are optional.
          Hide
          Jason Melnick added a comment -

          Fixes optional dependency transitive inclusion and root-level depth > 1 dependencies

          Show
          Jason Melnick added a comment - Fixes optional dependency transitive inclusion and root-level depth > 1 dependencies
          Jason Melnick made changes -
          Attachment AbstractWarMojo.patch [ 28152 ]

            People

            • Assignee:
              John Tolentino
              Reporter:
              Dirk Olmes
            • Votes:
              1 Vote for this issue
              Watchers:
              10 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - Not Specified
                Not Specified
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 6 hours
                6h