How to inject module declaration into JAR?
Yes, this is possible with the --patch-module
option. This option is most often used at runtime, but it also works at compile time:
javac --patch-module <module name>=<path to jar> module-info.java
creating module-info for automatic modules with jdeps in java 9
The short answer is that, yes, you'll have to convert the libraries to explicit modules.
The jlink
tool is intended to provide a trimmed binary image that has only the required modules. The issue is that automatic modules have access to the classpath (aka the unnamed module) which can read all JDK modules. So nothing would be trimmed.
This thread states this as well, with a link to a YouTube video.
This example converts commons-lang3-3.5.jar
to an explict module for a jlink
demo.
Edit: to be more specific, here is an example script that converts, in order, jackson-core
, jackson-annotations
, and jackson-databind
legacy jars to modular jars.
The idea is:
- run
jdeps --generate-module-info
on the legacy jar - unzip the legacy jar into a folder, add
module-info.java
from above, re-compile, and re-zip
The trick is that modular jars with dependencies will require those dependencies as command-line parameters. For example, here is jackson-databind
(abstracted somewhat):
# here, jackson-core and jackson-annotations have been built
# jackson-databind
jdeps --module-path $ROOT_DIR/modules \
--add-modules jackson.annotations,jackson.core \
--generate-module-info work $JACKSON_DATABIND_JAR
javac --module-path $ROOT_DIR/modules \
--add-modules jackson.annotations,jackson.core \
-d $ROOT_DIR/classes module-info.java
How to make one module depend on another module artifact?
Looks like it should work to me. But you might try mvn install
instead of mvn package
.
Including dependencies in a jar with Maven
You can do this using the maven-assembly plugin with the "jar-with-dependencies" descriptor. Here's the relevant chunk from one of our pom.xml's that does this:
<build>
<plugins>
<!-- any other plugins -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
Spring Dependency Injection and Plugin Jar
It seems like all You need is to create the Spring ApplicationContext
properly. I think it's possible without classpath mingling. What matters most are the locations of the Spring configuration files within the classpath. So put all Your plugin jar's into WEB-INF/lib
and read on.
Let's start with the core module. We'll make it to create it's ApplicationContext
from files located at classpath*:META-INF/spring/*-corecontext.xml
.
Now we'll make all plugins to have their config files elsewhere. I.e. 'myplugin1' will have its config location like this: classpath*:META-INF/spring/*-myplugin1context.xml
. And anotherplugin
will have the configs at classpath*:META-INF/spring/*-anotherplugincontext.xml
.
What You see is a convension. You can also use subdirectiries if You like:
- core:
classpath*:META-INF/spring/core/*.xml
- myplugin1:
classpath*:META-INF/spring/myplugin1/*.xml
- anotherplugin:
classpath*:META-INF/spring/anotherplugin/*.xml
What matters is that the locations have to be disjoint.
All that remains is to pass the right locations to the ApplicationContext
creator. For web applications the right place for this would be to extend the ContextLoaderListener
and override the method customizeContext(ServletContext, ConfigurableWebApplicationContext)
.
All that remains is to read Your config file (its location can be passed as servlet init parameter). Than You need to construct the list of config locations:
String locationPrefix = "classpath*:META-INF/spring/";
String locationSiffix = "/*.xml";
List<String> configLocations = new ArrayList<String>();
configLocations.add(locationPrefix + "core" + locationSiffix);
List<String> pluginsTurnedOn = getPluginsTurnedOnFromConfiguration();
for (String pluginName : pluginsTurnedOn) {
configLocations.add(locationPrefix + pluginName + locationSiffix);
}
applicationContext.setConfigLocations(configLocations.toArray(new String[configLocations.size()]));
This way You can easily manage what is and what is not loaded into Spring ApplicationContext
.
Update:
To make it work there's one more hidden assumption I made that I'm about to explain now. The base package of the core module and each plugin should also be disjoint. That is i.e.:
- com.mycompany.myapp.core
- com.mycompany.myapp.myplugin1
- com.mycompany.myapp.anotherplugin
This way each module can use <context:componet-scan />
(on equivalent in JavaConfig) easily to add classpath scanning for it's own classes only. The core module should not contain any package scanning of any plugin packages. The plugins should extend configuration of ApplicationContext
to add their own packages to classpath scanning.
Related Topics
Conversion from 12 Hours Time to 24 Hours Time in Java
Maximum Size of a Method in Java 7 and 8
Mockito: Mock Private Field Initialization
Java Type Generic as Argument for Gson
Reverse Hashmap Keys and Values in Java
What Java Ftp Client Library Should I Use
How to Remove Accents from a Unicode String
Value Change Listener for Javafx's Textfield
Problems Resteasy 3.09 Corsfilter
How to Convert a Java Object to Xml with Open Source APIs
How to Change the Name of a Java Application Process
How to Create a Delay in Swing
Random Number with Probabilities
Java Swingworker Thread to Update Main Gui