-->

Using profiles in multimodule project

2019-09-15 17:49发布

问题:

I have a multimodule maven project. Project layout is described below:

PARENT
  |-CHILD1
  |-CHILD2

Parent project has pom packaging type and declares CHILD1 and CHILD2 projects as modules. Also PARENT project declares profile dev which declares some property. CHILD1 project has jar packaging type and "overrides" PARENT dev profile by adding some dependency(dependency on commons-collections for example). CHILD2 project has war packaging type and has dependency on CHILD1 project. Also CHILD2 "overrides" parent dev profile by adding another dependency(dependency on commons-io for example, I mean dependency that is not related with that one in project CHILD1). Then when I run mvn clean install -Pdev maven doesn't put commons-collections.jar(dependency that is declared in CHILD1 project) to WEB-INF/lib of CHILD2 project, but commons-io.jar is there.

So, the question is: Why does not maven put dependencies from profiles that are declared in dependent projects of target project if target project declares another set of dependencies in that profile?

Actually I have much more projects and much more dependencies that varies in different profiles. And I want to declare project specific dependencies in that project pom.xml(supposing that declaring profile in project will "override" parent profile declaration)

回答1:

I am assuming that you want to be able to test locally when developing, test your changes against a staging environment and finally deploy to production.

The critical thing that you need to keep in mind is that when an artifact gets deployed to the local/remote repository, the active profiles is not part of what gets deployed, so when you add dependencies via profiles things become very dangerous as you have no way of knowing if the webapp was built with the DEV profile active or the PROD profile active, and then when that built artifact gets deployed into production you could be royally screwed over.

So the short of this is that you ensure that your artifacts are independent of deployment environment.

This means that, for example, you will pick up configuration from:

  • files on the classpath
  • system properties
  • jndi entries

So for example, if deploying to Tomcat, you might put a configuration.properties into $CATALINA_HOME/lib

Your webapp on startup will use getClass().getResource('/configuration.properties') to resolve the properties file and fail to start-up if the file is missing (fail-fast)

you can let your unit/integration tests use a different config by putting a test version of configuration.properties in src/test/resources.

You use the same principle for the <scope>provided</scope> style dependencies of your application. In otherwords a dependency that the container is contracted with providing should be provided by the container. So you might build the production version of tomcat/jetty for yourself using Maven also and add in the required dependencies into that assembly. This would be things like the production version uses a MySQL database, so you need to add the mysql-jdbc driver into to $CATALINA_HOME/lib. It is relatively easy to do this with the assembly plugin as you are really just repacking a zip with some bits included and others excluded.

When testing locally you will want to make use of the helper plugins' run goals such as jetty:run and tomcat:run. The solution here is that there is nothing wrong with giving these plugins dependencies via profiles because you are not affecting the dependencies of the artifact you are only affecting the plugin's classpath.

e.g.

<project>
  <!-- ... some stuff .. -->
  <profiles>
     <profile>
       <id>DEV</id>
       <build>
         <plugins>
           <plugin>
             <groupId>org.mortbay.jetty</groupId>
             <artifactId>jetty-maven-plugin</artifactId>
             <dependencies>
               <dependency>
                 <groupId>commons-dbcp</groupId>
                 <artifactId>commons-dbcp</artifactId>
                 <version>1.4</version>
               </dependency>
               <dependency>
                 <groupId>mysql</groupId>
                 <artifactId>mysql-connector-java</artifactId>
                 <version>5.1.18</version>
               </dependency>
             </dependencies>
           </plugin>
         </plugins>
       </build>
     </profile>
   </profiles>
 </project>

You can also configure system properties or classpath additions to pull in the required configuration file.

The net result of all this is that the artifact remains environment independent and you can test easily against the various environments

Hope this answers your question (even if sideways)