Spread config files to child poms (maven) - java

I have a parent pom which is inherited by multiple other poms
superpom
|--pokemon
|--|--app
|--|--infrastructure
|--yu-gi-oh
|--|--app
|--|--infrastructure
I have multiple config files like:
a cve-suppress.xml file for the good old dependency plugin (can be directly on pom level)
logback.xml (must be in test/resources)
...
Of course, I could have these files in every project in every module, but changes would lead to changes everywhere and therefore consume much time.
How can I effectively move/copy the files on runtime to the child poms?
Does files are mostly used for testing in gitlab pipelines
Possible ideas
1. Resource Plugin in superpom
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>copy-resource-one</id>
<phase>generate-sources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/destination-folder</outputDirectory>
<resources>
<resource>
<directory>source-files</directory>
<includes>
<include>foo.txt</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
superpom specifies path to resource
not working, because when e.g. pokemon executes plugin, in this scope, the file does not exist
2. Use Gitlab variables/files
copy files in gitlab pipelines to desired place
problem1 -> copy file to every test/resource folder in every module of project is hard and changes to path may lead to errors
problem2 -> file content is in gitlab therefore separeted from parent pom, overview might get blurry
3. Use mojo exec plugin
trigger script directly creating file
I am not sure how to do this exactly, I cannot find good examples so far :/
Do you have any other ideas? Is there a way I am missing or a dedicated plugon for this?

Related

Distribute plugin resource files to host project

I have a maven plugin that exposes a Mojo, with a goal that runs at the compile stage. The project was generated using mvn archetype:generate, and the POM contains all the standard stuff that comes with running that, very little deviation. The project includes a couple of resource files, e.g. filea.txt and fileb.txt, that are packaged up as part of the jar.
When the plugin is used in a project, I'd like the files that are included in the jar to be extracted and copied to the target\test-classes directory of the host project. I'm trying to use the plugin jar to both distribute some files + expose some functionality that can then use those files.
Is this a valid approach, and if so, are there settings I can add to the plugin POM to indicate that content from the plugin should be extracted and copied? I want to centralise this logic in the plugin, rather than having to do in the plugin host.
I feel like it's something with maven-dependency-plugin or maven-resources-plugin or build-helper-maven-plugin:attach-artifact, have tried a couple of different approaches but think I'm missing something obvious:
e.g. something like this in plugin POM?
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<outputDirectory>${basedir}/target/test-classes</outputDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>filea.txt</include>
<include>fileb.txt</include>
</includes>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
</plugin>
// etc etc
Google fu has let me down, keep ending up on maven resources page. Can post directory structure / more information if needed.
Cheers
First I would suggest to put resources which needs to be distributed into src/main/resources which looks like you have done ...but remove the configuration for the maven-resources-plugin and let maven do it's work. This is automatically copied into target/classes/ which in result is packaged into the resulting jar later.
If your plugin needs to get those files those can accessed as a usual resource via this.getClass().getResourcesAsStream("/...") and reading and writing them into a new location preferable into target/...

How to get same properties from different parent modules in maven?

I have a maven project with this structure.
I want to use same jar in different EARs with different properties.
When I run "mvn install", it creates 2 EARs but it cannot do filtering with this config. How can I solve this issue?
With Maven, you cannot filter a file belonging to a JAR module which is already built.
Each module is built and packaged separately. So, if you JAR needs to have a property which the value is populated dynamically, I may propose you two solutions :
Put the properties file which contains the key-value for conf.path in each EARs and in the JAR side, you could use a helper class to lookup in the classpath the value associated to the conf.path key.
You can perform this processing when the value is required or as soon as the application has started up if you need it very soon.
Create two flavors of your JAR whose the property differs. In this case, the property information is not stored any longer in the EAR but directly in the JAR
Look at the classifier element :
The classifier allows to distinguish artifacts that were built from
the same POM but differ in their content. It is some optional and
arbitrary string that - if present - is appended to the artifact name
just after the version number. As a motivation for this element,
consider for example a project that offers an artifact targeting JRE
1.5 but at the same time also an artifact that still supports JRE 1.4. The first artifact could be equipped with the classifier jdk15 and the
second one with jdk14 such that clients can choose which one to use.
Another common use case for classifiers is the need to attach
secondary artifacts to the project's main artifact. If you browse the
Maven central repository, you will notice that the classifiers sources
and javadoc are used to deploy the project source code and API docs
along with the packaged class files.
To produce multiple jars, configure the maven-jar-plugin plugin in your build in this way :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>jarVersion2</classifier>
<includes>
<include>**/jarVersion2.properties</include>
</includes>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>jarVersion1</classifier>
<includes>
<include>**/jarVersion1.properties</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
After you can reference in your WAR the jarVersion artifact like that for the war 1:
<dependency>
<groupId>groupIdOfTheJar</groupId>
<artifactId>artifactIdOfTheJar</artifactId>
<version>versionOfTheJar</version>
<classifier>jarVersion1</classifier>
</dependency>
And like that for the war 2 :
<dependency>
<groupId>groupIdOfTheJar</groupId>
<artifactId>artifactIdOfTheJar</artifactId>
<version>versionOfTheJar</version>
<classifier>jarVersion2</classifier>
</dependency>

Unzip and re zip a file using Maven?

Question: is there any way in Maven (without resorting to an ant plugin) to unzip a file, cd into the directory, remove the file, and the rezip it, all as part of the build?
This is necessary as it is a complex build and also do not want to have to use gradle to accomplish this task.
The requirement of unzipping, removing file and zipping again can also be met in one single step by the truezip-maven-plugin and its remove goal which:
Remove a set of files from an existing archive.
The official examples also cover this scenario.
Given the following snippet:
<properties>
<archive>${project.basedir}/sample.zip</archive>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>truezip-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<id>remove-a-file</id>
<goals>
<goal>remove</goal>
</goals>
<phase>package</phase>
<configuration>
<fileset>
<!-- note how the archive is treated as a normal file directory -->
<directory>${archive}</directory>
<includes>
<include>hello.txt</include>
</includes>
</fileset>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
And executing:
mvn clean package
The build will process the ${archive} file (in this case a sample.zip at the same level of the pom.xml file, that is, in the project.basedir directory) and remove from it the hello.txt file. Then rezip everything.
I just tested it successfully, you can even skip the properties section if not required. However, you should also carefully know that:
The zip file should not be under version control, otherwise it would create conflicts at each build
The behavior most probably should not be part of the default Maven build, hence good candidate for a Maven profile
the plugin replaces the original file, so if that was an issue you could firstly copy it to another location and then process it as above. To copy it, you could use the maven-resources-plugin and its copy-resources goal.

Using Maven Profiles with a WAR-Packaged Product

We have a Maven build (version 2.2.1) that currently produces a WAR file. Our output directory is target/, so we end up with a build artifact target/MyWar.WAR.
I'm adding two profiles to our POM.xml file to facilitate specific build "flavors" that each require a specific version of an A.xml file. In the intermediate build directory target/MyWar/ there are 3 files:
A.xml
A_1.xml
A_2.xml
Building in Maven without a specified profile should use A.xml, and it does currently. I want to use maven-antrun-plugin to (for Profile 1) replace A.xml with A_1.xml, and for Profile 2 replace A.xml with A_2.xml. (Removing the _1 and _2 suffixes.)
This is an example of Profile 1's execution:
<profile>
<id>Profile1</id>
<build>
...
<execution>
<id>Profile1-Replace</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<delete
file="${project.build.outputDirectory}/../MyWar/A.xml" />
<copy
file="${project.build.outputDirectory}/../MyWar/A_1.xml"
tofile="${project.build.outputDirectory}/../MyWar/A.xml" />
</tasks>
</configuration>
</execution>
...
</build>
</profile>
This correctly replaces the files in the intermediate target/MyWar/ directory, but for whatever reason the final WAR that's being produced in target does not reflect these changes.
It's as if running in the 'package' phase is too late, and the WAR has already been built. Changing the phase to 'compile' or 'test', the immediately-previous phases, complain because the A.xml file (and the intermediate build directory) have not even been created yet.
I would suggest to use the process-resources phase instead, or even generate-resources, if you feel that is a better fit. As a last resort, use prepare-package. But the package phase is the wrong place to do this sort of thing. All such modifications might typically occur directly in the source tree.
However, if you do the file manipulation in a separate directory, then you can add it during the package phase using the maven-war-plugin as follows:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>A_variant</directory>
</resource>
</webResources>
</configuration>
</plugin>
Of course if you need to go this route, it would be simpler to keep three directories and choose the appropriate one in your profile.

Using Maven to run a WAR dynamically in Tomcat, how does one add classpath entries so only Tomcat sees them?

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.
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.
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
Vote for http://jira.codehaus.org/browse/MTOMCAT-77 which addresses this need.
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:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.4.1</version>
<executions>
<execution>
<id>copy-env-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${env}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-testEnv-resources</id>
<phase>process-test-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${testEnv}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
<execution>
<id>copy-env-resources-again</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/src/main/resources-env/${env}</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>${basedir}/target/classes</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
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.

Categories