Beware of mvn war:inplace
So hopefully I can save somebody else four hours of frustrating, hair-tearing
debugging... a few days ago I had to make a minor change to one of the webapps
I maintain and redeploy it. I hadn't touched it in a while, but since I'm of
course using Maven for build automation, I made the change,
ran a mvn clean install
and pushed the new .war
out to my Tomcat cluster. I went to verify the change and - nothing. Hm.
I must have overlooked something in the
deployment step. So I did it again, being very careful to ensure that I had
built from the correct directory, deployed what I had built... and still,
same error. How strange.
I stared at the source directory. Was I in the right source directory? Check.
Did I deploy the right .war? Check. I re-ran mvn clean
by itself
and verified that the target
directory got wiped out. I ran
mvn install
and verified that the target
directory
had been re-created with new artifacts. I deployed. I checked the timestamps
on the deployed artifact. Still, same error.
Now, this is getting frustrating. I copied the .war into /tmp
and ran javap -c
on the .class
file to see exactly
what was deployed. And, sure enough, what I had deployed did not
include my fix. So, I triple checked that the .war
that I had
uploaded was the same one I had built. The timestamps matched. The file sizes
matched. The MD5 checksum matched. It was the same file. So I decompiled
the byte code on the artifact I had built. And lo and behold, I had built the
old, unchanged code!
Well, now, the only way this could happen would be if I was building some
old branch or something. Nope, src/main/java
contained the code
I was building. I rebuilt
and decompiled the bytecode in target/main/classes
. The
decompiled byte code matched what I expected from the source code. So let
me get this straight. target/main/classes
contained my corrected
code. The .war
was being re-created on each build. But it
contained the old code.
What. The. Hell. I ran mvn clean
. I verified that the
"target" directory
was gone. Gone, gone, gone. I built and installed the package again. I
looked at the target directory. It was new. Brand new. I checked the new
.war, exploding and decompiling the contents again. It had built the old
code.
However, there was an overlooked clue in mvn's output (you know, that output
that you're supposed to be paying attention to...?)
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/joshuadavies/devl/prod/app/src/main/webapp]
[INFO] Webapp assembled in [1428 msecs]
Now, of course, it's supposed to do that - that's where it gets
web.xml
from. But it triggered a memory from a while back...
see, I had been working with a designer who needed to be able to style the
.jsp
artifacts in the project. Since he wasn't going to be
making any code changes, it seemed like a lot of overhead for him to have to
redeploy the project every time he wanted to make a change. So I did a little
bit of research and discovered mvn war:inplace
. This builds an
exploded .war
right in src/main/webapp
for you —
you can then point a local Tomcat instance to that directory and make
(versionable) changes that are reflected in your running instance right away.
This is nice, because you get the software engineering benefits of version
control and build automation with a sped-up development cycle.
Well, since I'm the paranoid sort — and most of the changes I need to
make are in servlet
classes anyway, I re-build and re-deploy after
each change, so once I had discovered mvn war:inplace
, I showed
him how to use it and went on with my development effort. What I didn't
realize is that mvn clean
doesn't remove that new
src/main/webapp/WEB-INF/classes
directory! So if you run
mvn war:inplace
make a change, and then run
mvn clean install
, Maven will dutifully compile all of your code,
generate a new target
directory, create an exploded .war
in there (with the new code!), and then create a deployable artifact that includes
the old build.
This seems like an oversight in the mvn war
goal to me.
As it turns out, there's a
fix, but it's sort of an obscure one.
If you add the following to your pom.xml
:
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<filesets>
<fileset>
<directory>src/main/webapp/WEB-INF/classes</directory>
</fileset>
<fileset>
<directory>src/main/webapp/WEB-INF/lib</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
Then the mvn clean
task will clean up the leftover parts of
mvn war:inplace
each time it's run.
Add a comment:
Completely off-topic or spam comments will be removed at the discretion of the moderator.
You may preserve formatting (e.g. a code sample) by indenting with four spaces preceding the formatted line(s)