17

When building an ear through the Maven ear plugin, I experienced that applying package without clean can lead to inconsistent results: If I change the version of a dependency, the dependency now appears twice in the ear, in the old and the new version. If I build the ear without version numbers in the jar names, I get just one jar, but old versions are not replaced properly.

I looked at the source code

http://svn.apache.org/viewvc/maven/plugins/tags/maven-ear-plugin-2.10.1/src/main/java/org/apache/maven/plugin/ear/EarMojo.java?view=markup

Especially the lines from 436 are interesting: There checks whether to update files in target, but apparently only checks for the absolute path and the lastmodified date.

Summarized: Am I right that changing the dependencies (or their versions) always requires to call clean before the build? Or is there some intelligence in package which I missed?

J Fabian Meier
  • 33,516
  • 10
  • 64
  • 142
  • It would be great to have an answer with some meat in it. Does somebody know something about the inner workings of the ear plugin? – J Fabian Meier Nov 30 '17 at 20:02

3 Answers3

11

Yes... most of the time you should call clean when changing your dependencies or any other build configuration, otherwise previous build elements may collide with a new build (as seems to be your case here). Generally speaking, to make sure previous build elements won't collide with a new build you should call clean before-hand - configuration changes or not.

But there are exceptions: if you know for sure that previous build elements won't impact your new build, you can skip clean. However most of the time you cannot know, or skipping clean is simply not possible without causing such collisions. I explain in details below.


When you build your project with Maven, it will generate files and other elements under the ${project.build.directory} folder (target by default). These elements are then used to create your artifact (EAR in your case) during the package phase.

Without calling clean after a build, all the elements under target will still be there during the next build and may impact it. How can Maven plugins tell the difference and know which elements are from old builds or new ones? Files timestamp are obviously not sufficient, and unless using some obscur mechanism to identify which files are from which build, it is hard to do.

If I change the version of a dependency, the dependency now appears twice in the ear, in the old and the new version.

That's probably because your build copy dependencies under target and your packaging plugin will include an entire directory into the generated artifact (in your case, the EAR). For example, if you run your build twice with different version:

  • First build: mydep-1.0.jar is generated in target/classes/mydep and your plugin includes all files from target/classes/mydep into the EAR. Everything's fine.
  • Second build: mydep-2.0.jar is generated but mydep-1.0.jar is still in target/classes/mydep. The entire target/classes/mydep directory is included and both versions end-up in the EAR.

If I build the ear without version numbers in the jar names, I get just one jar, but old versions are not replaced properly.

That's because some plugins - for optimization - won't re-compile or re-do actions if they see the results already exists. In your case, as you pointed out, the EAR plugin probably keeps the "old" version of the file because it already exists, where in fact a more recent version with the same name should replace it. That's probably because you did not specify a version:

  • In the first case, mydep-0.1.jar existed and a new mydep-0.2.jar was created but both were included in your EAR
  • Now, without version, you have mydep.jar which is not overriden by the most recent version because "it already exists", thus the "old" file stay in place and get included in the EAR.

Conclusion: Maven plugins are not able to differentiate with a 100% certainty if an element is from a previous build or a new one. When you are updating your build configuration, you should always call clean to ensure there won't be a collision between old and new build elements. Even without build configuration changes, it may be necessary to call clean - but that's another matter.

I would recommend to always call clean when running a build, unless no collision is possible and it provides a real value such as a time gain because rebuilding the entire project is too long or other reasons justifying it.

EDIT: as per @JF Meier request for sources, the Running Maven guide provides advice as per the clean goal and the related inconsistency risks:

Inconsistent output

Most plugins are optimized to know if they have to execute their task. In some cases the output can be polluted from a previous build and the end result is not what you expected. In such rare situations you can call the clean phase which means: remove the output directory. You can also call it as mvn clean verify which means: first clean up the output directory, next build the project and verify the outcome.

Pierre B.
  • 11,612
  • 1
  • 37
  • 58
  • That seems more precise than my answer: +1 – VonC Nov 26 '17 at 15:24
  • Very interesting answer. It sounds plausible to me and is already helpful, but would be even greater with some sources. – J Fabian Meier Nov 28 '17 at 08:23
  • Glad it helps. It's hard to give "sources" as such apart the plugins doc and source code itself. My answer is entirely based on logical deduction from a few simple facts: 1. when building, you'll generate files into `target` (or the configured build directory), and generated files may change if you update your build config (such as dependencies); 2. EAR Plugin includes entire directories and cannot know if a file if from current or previous build; 3. running multiple builds without clean, you'll risk having older unwanted files into your EAR as per 1 and 2. – Pierre B. Nov 28 '17 at 10:04
  • ... Still I found a little piece of literature that may provide a proper source, I edited the answer. – Pierre B. Nov 28 '17 at 10:18
  • @PierreB. Thanks. I just wonder: Your source speaks about "rare situations", but your deduction suggests that this is the usual case (at least for war/ear building). – J Fabian Meier Nov 28 '17 at 12:25
  • That's not what I meant. I try to say *it may happen*, especially given your context. But that's not a common situation either. (if you can point out where I give that impression I'll edit happily) However, I think "rare" may be counter intuitive, it depends on context. When running automatized build hundred of time a day with regular pom or dependency updates (which is quite common today), you are bound to encounter such inconsistency without cleaning your working space regularly. Even if these situations are indeed rare in your context, better run clean if you can without serious issues. – Pierre B. Nov 28 '17 at 13:27
  • I must say, I find this answer less helpful than I first thought because it is all "it may", "most of the times". My question was if a change in dependencies requires a clean, and I guess the answer is just "yes" (considering the source code and my experiments) but on the other hand, I find it strange that the ear plugin does not take precautions to avoid the most obvious inconsistencies (like two versions of the same artifact) if not called with clean. – J Fabian Meier Nov 29 '17 at 07:46
  • I understand, I tried to provide an answer regarding a generic situation because I do not know everything about your build. I used "it may", etc. because these situations will vary depending on your build configuration the plugins you are using, etc. It depends on context as I said ;) However, if you can provide details regarding your build such as your `pom.xml`and how exactly you proceed to create your package I'll be happy to provide "it is in fact" for what I said previously in your precise context! – Pierre B. Nov 29 '17 at 08:32
  • *" I guess the answer is just "yes" (considering the source code and my experiments)"* Exactly what I said. Yes, you should. As to why exactly, it *may* depends on various factors, but that does not change the initial answer. Adding to your question "[...] call clean before the build *when using the EAR plugin in such and such config with this precise context*" I would be happy to provide details ;-) – Pierre B. Nov 29 '17 at 13:40
  • @PierreB. Don't try to blame me for an imprecise question. I asked in the context of the maven-ear-plugin "Am I right that changing the dependencies (or their versions) always requires to call clean before the build?" You never really tried to answer that question, but you speculated about the general maven build mechanism. And no, you do not need to know my pom.xml to answer that. But you do need to know something about the maven-ear-plugin. – J Fabian Meier Nov 29 '17 at 19:28
  • I don't think further debate is necessary here. I don't try to blame but to help you and other people having similar issues with the data you'll provide. I won't be able to provide more help regarding your issue without knowing how you configured your EAR plugin and your build, all I can say is that some output was generated from the dependencies you defined and without clean it's being re-used. I can speculate even more with every way the EAR plugin can use and generate data, but that won't be helpful at that point. I hope it helped. – Pierre B. Nov 29 '17 at 21:27
4

See if the issue persists by compiling the latest version of maven-ear-plugin (3.0.0, more recent than 2.10.2, itself more recent than the official 2.12.1)

As you can see in the latest code EarMojo.java, the function copyModules() has changed from the 2.10.1 implementation, and might be more robust.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

The ear archive bundles all the project dependencies into a zip (following a structure specified by the ear format). The archive is built in two phases, first the ear-maven-plugin collects the project dependencies and then creates the archive. These dependencies are copied into the ${project.build.directory}.

So, clean should always be used with this plug-in, to prevent any inconsistencies caused by appending dependencies into a directory where a previous execution might have copied another snapshot of the dependencies. If clean phase is skipped, the ${project.build.directory} directory will not be truncated, which will most probably cause inconsistencies in the constructed package.