Seperate spring.factories into multiple files - java

In Spring Boot, you can do the following:
src/main/resources/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.AConfiguration,\
org.springframework.boot.autoconfigure.admin.BConfiguration,\
org.springframework.boot.autoconfigure.admin.CConfiguration,\
org.springframework.boot.autoconfigure.admin.DConfiguration,\
org.springframework.boot.autoconfigure.admin.EConfiguration,\
org.springframework.boot.autoconfigure.admin.FConfiguration,\
Which is very nice. However after a year of development the list of auto configuration is now > 15 lines, which makes it hard to manage.
Would like to know if it is possible to separate the spring.factories into multiple files? Preferably would like to keep the whole project in one JAR.
Or maybe there is another ways to help organize the EnableAutoConfiguration that I am not aware of?
Thanks in advance!

While using spring-boot we use multiple "starters", each with an auto-configuration and spring.factories file.
So, one way could be to split your project into modules - one for each auto-configuration, define a dedicated spring.factories file in the module, and import all the modules as a runtime dependency in the main application module.
You can use maven or gradle to manage the multi-module project and the dependencies among them:
Gradle: https://guides.gradle.org/creating-multi-project-builds/
Maven: https://www.baeldung.com/maven-multi-module
Example:
root
moduleA
src/main/resources/META-INF/spring.factories
moduleB
src/main/resources/META-INF/spring.factories
and so on...

I have found a solution for this question.
Note: This exact solution assume that you only used EnableAutoConfiguration in your spring.factiores, it would crash if you use more than one type of config inside spring.factories.
One can do:
src/main/resources/META-INF/spring.factories
src/main/resources/META-INF/spring-2.factories
src/main/resources/META-INF/spring-3.factories
src/main/resources/META-INF/spring-4.factories
and merge this into one file.
Note, I am using Maven Antrun but I suspect Gradle would also have a similar feature.
In your pom.xml, add the following:
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>default-ci</id>
<goals>
<goal>run</goal>
</goals>
<phase>process-resources</phase>
<configuration>
<target>
<replace token='org.springframework.boot.autoconfigure.EnableAutoConfiguration=' value=','
dir="${project.build.directory}/classes/META-INF">
<include name="spring-*.factories"/>
</replace>
<concat destfile="${project.build.directory}/classes/META-INF/spring.factories" overwrite="yes" append="yes">
<fileset dir="${project.build.directory}/classes/META-INF" includes="spring-*.factories" />
</concat>
</target>
</configuration>
</execution>
</executions>
</plugin>
And in spring.factories is the normal config:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.AConfiguration,\
org.springframework.boot.autoconfigure.admin.BConfiguration
But in spring-2.factories and others, you start with ,\ instead of the default statement:
spring-2.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.CConfiguration
spring-3.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.DConfiguration
After all that, the outcome spring.factories in your output class directories will be a very nice:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.AConfiguration,\
org.springframework.boot.autoconfigure.admin.BConfiguration,\
org.springframework.boot.autoconfigure.admin.CConfiguration,\
org.springframework.boot.autoconfigure.admin.DConfiguration

Related

Wildfly 24 - Module Annotations not working?

I am having some problems deploying my application in Wildfly 24.
I created a Module containing some jar's that aid my deployments executions. Some of those Jar's have annotations like #Entity and #WebService.
According with the Wildfly documentation, I created a global module in the standalone.xml like:
<global-modules>
<module name="server.defs" annotations="true" />
</global-modules>
Also, I have created (via maven) the jandex index for both the jar in the module and the jar deployed. On opening the jar's I can see the jandex.idx on the META-INF folder. To do this i used in the pom.xml the following:
<plugin>
<groupId>org.jboss.jandex</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
But when i start the server, its like the annotations are not parsed.
Hibernate can't find the class's with the #Entity annotations and the class's using the #WebService annotations in the modules are failing to deploy.
If I don't use the modules, everything is deployed correctly..
Anyone has any idea of whats the problem?
Thank you!
I left the project in standby for a while and now I rebooted it. Answering the original question, yes you can have annotated class's in the modules directories.
Just be sure every single dependency needed is included in the module.xml. You don't need any ' annotations="true" '.

Create two artifacts (war) for a single maven project

I have a Java web project that we deploy on the server of two different customers, 99% of the code is the same, right now I have two ApplicationBuilders, which is the class that contains the customization for each customer.
Anytime I want to deploy the new version I have to manually comment a line, build (with maven), uncomment that line, comment the other one and build again.
public class ApplicationBuilderFactory {
private static final IApplicationBuilder app;
static {
// app = new Customer1ApplicationBuilder()
app = new Customer2ApplicationBuilder();
}
}
public static IApplicationBuilder get() { return app; }
}
I want to avoid all this and the best thing would probably just create two different wars.
What's a good way to do this? I don't use (nor like) dependency injection frameworks and it seems overkill to add one just for a single class, but I may consider it.
One way to approach this is to use the Maven WAR Plugin Overlays feature.
Instead of trying to build multiple artifacts from one project (which can become unwieldy after a while), you create one base WAR project, and then a separate WAR project for each customer that only contains the components that need to be different.
Each customer specific WAR will be overlaid with the base WAR. This will make it easier to customise not only the ApplicationBuilderFactory but also specific web content and assets.
This also has the following benefits
customer specific features are guaranteed to be isolated from each other;
different customers can have their own release cycle and source control repository
it's easy to add subsequent customers
Create 2 different Maven Profiles, one for each customer, that copies a version of class ApplicationBuilderFactory to the right directory before compile stage.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>copy-files</id>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target name="copy files">
<copy file="${project.build.sourceDirectory}/pkg/ApplicationBuilderFactory.java.${extension}" tofile="${project.build.sourceDirectory}/pkg/ApplicationBuilderFactory.java" />
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>customer1</id>
<properties>
<extension>customer1</extension>
</properties>
</profile>
<profile>
<id>customer2i</id>
<properties>
<extension>customer2</extension>
</properties>
</profile>
Instead of having only one src/main/java/pkg/ApplicationBuilderFactory.java, we have:
src/main/java/pkg/ApplicationBuilderFactory.java.customer1
src/main/java/pkg/ApplicationBuilderFactory.java.customer2.
So before compiling java code, we copy one of these versions to the src/main/java/pkg/ApplicationBuilderFactory.java.
So generate 2 different .wars using 2 different profiles.

Sharing test resourcing between modules issue

I want to share test resources between 2 modules A and B. In A test resources I have directory with some files. Like this
-dir
----f1
----f2
I've done all according to Share test resources between maven projects . Now from B test I can access to resources using syntax like:
this.getClass().getClassLoader().getResource("dir/f1")
And it's works perfectly fine. But I don't want hardcore all file names like f1 or f2. What I really want is getting all files from directory. Like
File foo = new File(this.getClass().getClassLoader().getResource("dir").getFile());
assert foo.isDirectory()
foo.list()
...
But when I create foo in such way it even doesn't exist (foo.exist() returns false).
How can I deal with it?
Update. Alternative solution using ant
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<configuration>
<target>
<fileset id="d" dir="${basedir}/src/test/resources/dir" includes="*"/>
<pathconvert property="d" refid="d">
<map from="${basedir}/src/test/resources/" to=""/>
</pathconvert>
<touch file="${basedir}/src/test/resources/d/listOfCharts.txt"/>
<echo file="${basedir}/src/test/resources/d/listOfCharts.txt" message="${charts}"/>
</target>
</configuration>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
The approach you talk about creates a JAR which can be used in later tests. You can't list the contents of a Jar this way. You need to use Jar you need to read it as a Jar specially
How do I list the files inside a JAR file?

How to attach a maven plugin to a phase by default?

I have a maven plugin that should run in the compile phase, so in the project that consumes my plugin, I have to do something like this:
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>my-goal</goal>
</goals>
</execution>
</executions>
What I need is to by default attach my-goal to the compile phase if the user has included my plugin already (ideally the above part wouldn't be necessary, just the plugin declaration).
Is this possible?
Put an #phase annotation in your Mojo classdef annotations.
The doc says:
#phase <phaseName>
This annotation specifies the default phase for this goal. If you add an execution for this goal to a pom.xml and do not specify the phase, Maven will bind the goal to the phase specified in this annotation by default.
If this doesn't work, I guess a JIRA is warranted.
Create an instance of src\main\resources\META-INF\plexus\components.xml in your plugin.
In there create a LifeCycle mapping for the artifact types that your want your Mojo to support. Make sure that it lists all the phases and plugins you want to support. Probably best to copy from the one from maven-core.jar.
Then add your plugin in to the appropriate LifeCycle(s) at the phase at which you want them built.
Eg the consume-aar Mojo added into the compile phase of the aar lifecycle.
<!-- Android archive (aar) support -->
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>aar</role-hint>
<implementation>
org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping
</implementation>
<configuration>
<phases>
<generate-sources>
com.jayway.maven.plugins.android.generation2:android-maven-plugin:generate-sources
</generate-sources>
<process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
<compile>
com.jayway.maven.plugins.android.generation2:android-maven-plugin:consume-aar,
org.apache.maven.plugins:maven-compiler-plugin:compile
</compile>
This is possible, but it is an undocumented maven feature.
Use this components.xml:
<component-set>
<components>
<component>
<role>org.apache.maven.lifecycle.Lifecycle</role>
<implementation>org.apache.maven.lifecycle.Lifecycle</implementation>
<role-hint>myplugin</role-hint>
<configuration>
<id>accurest</id>
<phases>
<phase>my-plugin-not-used-phase</phase>
</phases>
<default-phases>
<compile>
my.package:my-plugin:${project.version}:my-goal
</compile>
</default-phases>
</configuration>
</component>
</components>
but your plugin need to be added with <extensions>true</extensions> to modify existing lifecycle.
More: How to bind plugin mojos (goals) to few phases of default lifecycle?
Real project: https://github.com/spring-cloud/spring-cloud-contract/blob/master/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/resources/META-INF/plexus/components.xml
You associate plugin to maven lifecyle goal. The plugin configuration should be declared in phase.
For example if you wan to run some plugin during build phase you'll need to do something like this :
<project>
...
...
<build>
<plugin>
**Configuration of plugin**
</plugin>
</build>
</project>
Please read carefully about maven lifecycles here (it is fundamental for understanding of maven):
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
And then read about how to configure a plugin : http://maven.apache.org/guides/mini/guide-configuring-plugins.html
P.S. Getting into logic of maven is not easy at the beginning. But it is rewarding afterwards.

Use Maven just to fetch some library jars

We are using ANT for our build process and don't have plans to change this in the near future.
Is it possible to use Maven to just fetch common Open Source jar files (e.g. Log4J, SWT, JFace) and put them in the right location of our project, so we don't have to store them in our version control — preferable without creating the typical Maven-cache in the home directory?
NO NO NO Everyone!
If you're using Ant, the best way to use Maven repositories to download jar dependencies is to use Ivy with Ant. That's exactly what Ivy is for.
Installing Ivy and getting to work with current Ant projects is simple to do. It works with Nexus and Artifactory if you use those as your local Maven repositories.
Take a look at Ivy. It is probably exactly what you want.
In variation of org.life.java's answer, I would not do mvn install.
Instead, in the pom.xml I would add the following bit:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Now you just need to do mvn generate-sources, which is a lot faster than the full mvn install, and all dependencies will be copied to the specified directory.
Oh btw, isn't that what Apache Ivy is about? Extending Ant to understand Maven's dependency management?
It's possible, you should use maven-ant-tasks.
In particular its dependencies ant task. With this setup no Maven install is required.
<?xml version="1.0" encoding="UTF-8"?>
<project
name="download-dependency"
basedir="."
default="download-dependency"
xmlns:artifact="antlib:org.apache.maven.artifact.ant"
>
<target name="download-dependency">
... define properties ...
<taskdef
resource="org/apache/maven/artifact/ant/antlib.xml"
uri="antlib:org.apache.maven.artifact.ant"
/>
<artifact:dependencies>
<localRepository path="${local-repo.dir}"/>
<remoteRepository id="central" url="${repository-uri}"/>
<dependency
groupId="${groupId}"
artifactId="${artifactId}"
version="${version}"
type="${type}"
classifier="${classifier}"
scope="runtime"
/>
</artifact:dependencies>
</target>
</project>
The only binary you should check into your project is maven-ant-tasks.jar.
Actually in our project I used Sonatype Nexus ( documentation ) Maven repository manager to centralize access to different repositories and even maintain some binaries unique to our environment. With Nexus' help I just fetch maven-ant-tasks.jar with ant's <get> task from a known URL. You don't have to use Nexus, but it greatly speeds up builds, because it caches binaries close to your developer's machines.
Ivy does just this,
when it bootstraps itself:
http://ant.apache.org/ivy/history/latest-milestone/samples/build.xml
<property name="ivy.install.version" value="2.0.0-beta1"/>
<property name="ivy.jar.dir" value="lib"/>
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar"/>
<target name="resolve" unless="skip.download">
<mkdir dir="${ivy.jar.dir}"/>
<echo message="installing ivy..."/>
<get src="http://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar" dest="${ivy.jar.file}" usetimestamp="true"/>
</target>

Categories

Resources