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,
mvn clean install and pushed the new
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
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
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
decompiled byte code matched what I expected from the source code. So let
me get this straight.
target/main/classes contained my corrected
.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
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
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]
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
.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
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
mvn clean task will clean up the leftover parts of
mvn war:inplace each time it's run.