views:

324

answers:

3

Scenario is such: I have a webapp that I'd like to run dynamically with the tomcat-maven-plugin's tomcat:run goal. The wrinkle is that I have numerous classpath resources that need to differ between the packaged artifact and the one run off a local workstation.

Failed Attempts:

1.) My first attempt was to use the builder-helper-maven-plugin, but it won't work because the target configuration files will (inconsistently!) work their way into the packaged WAR archive.

<plugin>    
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>build-helper-maven-plugin</artifactId>
 <version>1.3</version>
 <executions>

  <execution>     
   <id>add-resource</id>
   <phase>generate-resources</phase>
   <goals>
    <goal>add-resource</goal>
   </goals>
   <configuration>
    <resources>
     <resource>
      <directory>${basedir}/src/main/resources-env/${testEnv}</directory>
      <targetPath>${basedir}/target/classes</targetPath>
     </resource>
    </resources>
   </configuration>
  </execution>
 </executions>
</plugin>

2.) My second attempt was to add the folder (since the files-to-be-deployed aren't present in Tomcat's classpath yet either) to -Djava.ext.dirs, but it has no effect (I actually suspect that this systemProperties element is misconfigured or otherwise not working at all). See:

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>tomcat-maven-plugin</artifactId>
 <version>1.0-beta-1</version>
 <configuration>
  <tomcatWebXml>${basedir}/src/main/mock/web.xml</tomcatWebXml>

  <systemProperties>
   <property>
    <name>java.ext.dirs</name>
    <value>${basedir}/src/main/resources-env/${testEnv}</value>
   </property>      
  </systemProperties>      
  <path>/licensing</path>
 </configuration>
</plugin>

I'm not sure what to attempt next. The heart of the problem seems to be that missing in this plugin is something like Surefire's <additionalClasspathElement> element.

Would the next step be to create a custom catalina.properties and add it to a <configurationDir>? If so, what would catalina.properties need to look like?

Edit: More thorough explanation follows

I understand this question reads somewhat vaguely, so I'll try to elaborate a bit.

My POM uses the webResources functionality of the WAR plugin to copy some environment-specific config files and without using a profile to do it, by copying in a resource named /src/main/resources-env/${env} like so:

...
<plugin>    
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
  ...
    <configuration>
   ...
     <webResources>
      <!-- Copy Environment-specific resources to classes -->
      <resource>
       <directory>${basedir}/src/main/resources-env/${env}</directory>
       <targetPath>WEB-INF/classes</targetPath>
      </resource>
     </webResources>
    </configuration>
</plugin>

This will copy the (default, DEV) environment resources into the package and currently works fine. Note also that b/c these occur as part of packaging, the tomcat:run goal is never privy to them (which is desired, as the environments differ).

So the problem is this: when the dynamic tomcat:run is executed, the application will fail because its classpath (it looks at target/classes) will lack the needed local workstation environmental config files. All I need to do is get those on the path for tomcat, but would like to do so without adding anything to the command line and definitely without breaking the build's integrity if someone follows up with a mvn package and doesn't clean first.

I hope this is more clear.

+1  A: 

I may be missing something but why don't you declare the required dependencies in a profile and use this profile when running Tomcat? I don't get why you would need to put these resources at Tomcat's classpath level.

UPDATE: I'm editing my answer to cover the comment from the OP itself answering my question above.

You're correct, the files really need to be in the webapp classpath, not tomcat's. So how could I make a profile that activate automatically for tomcat:run but without additional cmd line args?

I don't know how to do this without declaring the profile as <activeByDefault> or listing it under the <activeProfiles> (but this is not what I had in mind, I would rather use property activation and call something like mvn tomcat:run -Denv=test, not sure to understand why this is a problem).

And how should I "declare the dependencies" in the profile while ensuring that subsequent invocations never let them into the package WAR via a vanilla mvn package

If the previously mentioned profile is active by default, then you'll need to exclude it if you don't want it, by calling something like mvn package -P !profile-1. A profile can't be magically deactivated for one particular goal (at least, not to my knowledge).

Actually, my understanding is that you really have two different context here: the "testing" context (where you want to include more things in the WAR) and the "normal" context (where you don't want these things to be included). To be honest, I don't know how you could distinguish these two situations without specifying any additional parameter (either to activate a profile or to deactivate it depending on the context). You must have valid reasons but, as I said, I don't really understand why this is a problem. So maybe profiles are not a solution for your situation. But I'd really like to understand why because this seems to be a typical use case for profiles :)

UPDATE2: Having read your comment to the other answer and your update, I realize that my initial understanding was wrong (I though you were talking about dependencies in the maven sense). But, I still think that profiles could help you, for example to customize the <resources> as in this blog post (this is just one way to do, using a property like src/main/resources/${env} in the path is another way to go). But this won't solve all your concerns (like not specifying additional command line params or automagically cleaning the target directory). I don't have any solutions for that.

Pascal Thivent
You're correct, the files really need to be in the webapp classpath, not tomcat's. So how could I make a profile that activate automatically for tomcat:run but without additional cmd line args? And how should I "declare the dependencies" in the profile while ensuring that subsequent invocations never let them into the package WAR via a vanilla `mvn package`.
Justin Searls
Thanks for the update! I'm fine with adding a profile to do this conditionally just as you described (was hoping to keep this hidden from developer users, but I relent). I'll give that a try now.
Justin Searls
A: 

Add the dependencies element directly to the plugin element.

Here is an example of doing the same with the Jetty plugin from the (still in development) Maven Handbook: http://www.sonatype.com/books/mhandbook-stage/reference/ch06s03.html

tobrien
Apologies, as I used the word dependency generally, and not to describe a Maven Dependency proper. Since what I need added to the classpath is actually a folder of configuration files (to be loaded as class path resources), will this approach (or something like it) work?
Justin Searls
A: 

Here's the solution I have in place at the moment.

Special thanks to Pascal's diligent conversation here, but I ultimately decided to make a change to how I was loading my environment-specific config files throughout the goals and now I believe I'm getting most of what I initially wanted.

I removed the config files from <webResources> from the WAR plugin and the test config from <testResources> and am now manually managing the resource-copying with the the maven-resources-plugin to copy them directly into target/classes at the goal they're needed. This way Tomcat can see the config, but the tests aren't broken by having duplicate or differing config files on the path.

It's definitely a mess, but it works. Listing:

org.apache.maven.plugins maven-resources-plugin 2.4.1 copy-env-resources process-resources copy-resources ${basedir}/src/main/resources-env/${env} true ${basedir}/target/classes copy-testEnv-resources process-test-resources copy-resources ${basedir}/src/main/resources-env/${testEnv} true ${basedir}/target/classes copy-env-resources-again prepare-package copy-resources ${basedir}/src/main/resources-env/${env} true ${basedir}/target/classes

So a mvn clean install will build and test with ${env} and ${testEnv} appropriately. A mvn -Denv=someLocalConfig tomcat:run (which in my case is identical to my default ${testEnv} will make sure the src/main/resources-env/someLocalConfig gets loaded for Tomcat's dynamic execution, but without requiring that I do a clean before successfully rebuilding.

Like I said, messy that I'm rewriting the same cluster of files to the same target location at each phase, but it accomplishes what I'd meant to.

Justin Searls