简体   繁体   中英

Is it possible to get `mvn tomcat6:run` to work correctly with a filtered web.xml?

I have a project that needs to have ${} placeholder substitution applied to web.xml that I want to run for development mode with mvn tomcat6:run . Is it possible to set this up?


This is a legacy codebase where I'm trying to introduce both deployment environments to get rid of the previous process that was "copy web.xml variants around and edit a config file and keep this out of source control." I also want to avoid having to maintain a Tomcat installation and setting up an IDE to deploy to it, and just have the application execute using tomcat6-maven-plugin .

What I have so far is this project layout:

foo
│   pom.xml
│
└───src
    └───main
        ├───environments
        │   ├───BAR
        │   │       filter.properties
        │   │
        │   └───FOO
        │           filter.properties
        │
        ├───java
        │   └───se
        │       └───millimoo
        │               IndexServlet.java
        │
        └───webapp
            └───WEB-INF
                    web.xml

pom.xml

...
<build>
    <filters>
        <filter>src/main/environments/${environment}/filter.properties</filter>
    </filters>
    <resources>
        <resource>
            <directory>src/main/environments/${environment}</directory>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.0</version>
            <configuration>
                <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat6-maven-plugin</artifactId>
            <version>2.2</version>
        </plugin>
    </plugins>
</build>

<profiles>
    <profile>
        <id>env-foo</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <environment>FOO</environment>
        </properties>
    </profile>
    <profile>
        <id>env-bar</id>
        <properties>
            <environment>BAR</environment>
        </properties>
    </profile>
</profiles>
...

That is: I add the directory for an environment as a resource; tell Maven to load placeholder values from filter.properties in that directory; and tell the WAR plugin to filter the deployment descriptors. I also set up two profiles to switch between the environments.

FOO\\filter.properties

filter.environment=foofoo

BAR\\filter.properties

filter.environment=barbar

web.xml

...
<servlet>
    <servlet-name>Index</servlet-name>
    <servlet-class>se.millimoo.IndexServlet</servlet-class>
    <init-param>
        <param-name>environment</param-name>
        <param-value>${filter.environment}</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>Index</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
...

IndexServlet.java

Prints the value of the environment init param.


What happens is that when I run this using mvn tomcat6:run , it uses the unfiltered web.xml , which is obviously not what I want. The filtered version is used when I use mvn tomcat6:run-war , but I'm not a fan of that: it's slower, and has other issues in my real code that generally slow down turnaround, which is the point of the exercise. (It uses the JAR files of dependent projects where the backend code is from the Maven repository to build the JAR, which can be either outdated or not available at all if I haven't installed them after a version number bump; rather than using the current code.)

Is there a way to get the best of both worlds here? Be able to run the app server through maven or in another easily embedded way, while also having filtering applied to web.xml? Preferrably without significantly changing the app itself, ie I don't want to load the configuration values without using servlet init params. (In the real code, the servlet is a third-party one.)

I think you need to add this in your pom.xml so Maven knows he needs to filter this directory too :

<build>
    <resources>
        <resource>
            <directory>src/main/webapp/WEB-INF</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
...
</build>

EDIT

As discused in the commentaries, this approach might not be a good solution.

Another solution :

Overriding the default destination directory

By default web resources are copied to the root of the WAR, as shown in the previous example. To override the default destination directory, specify the target path.

  ... <configuration> <webResources> <resource> ... </resource> <resource> <directory>configurations</directory> <!-- override the destination directory for this resource --> <targetPath>WEB-INF</targetPath> <!-- enable filtering --> <filtering>true</filtering> <excludes> <exclude>**/properties</exclude> </excludes> </resource> </webResources> </configuration> ... 

In this example, we override a directory but maybe it's possible to override a single file.

It seems that the goal I want is tomcat6:run-war-only , which starts a Tomcat over the assembled webapp in target/ . However, this goal doesn't invoke the rest of the lifecycle when run on its own, meaning the app might not be assembled or up-to-date. I resolved this by first replacing the war:war goal with war:war-exploded in the package phase of the lifecycle, and binding tomcat6:run-war-only to verify , the next lifecycle phase. (Which, conveniently enough, doesn't have any goals bound for war projects, and seems appropriate semantically.) To keep this contained, I put it into a profile:

<profile>
    <id>embedded</id>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <!-- Replace running war:war with war:exploded in the lifecycle -->
                    <execution>
                        <id>embedded-war</id>
                        <goals>
                            <goal>exploded</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                    <execution>
                        <id>default-war</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat6-maven-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <!-- Run Tomcat over the assembled webapp -->
                    <execution>
                        <id>embedded-verify</id>
                        <goals>
                            <goal>run-war-only</goal>
                        </goals>
                        <phase>verify</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

Combined with what's already given in the question, to run the foo environment, you use:

mvn -P embedded,env-foo verify

One flaw this has is that it won't reflect live changes to views or such, but running mvn war:exploded to reassemble the app while the Tomcat server is running should take care of that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM