how to use class file from another war - java

We have two different web application and we want to extend few controllers of one war into another war.
We are using maven to build the project.
to include war we have given its dependency as
<dependency>
<groupId>com.abc.exchange</groupId>
<artifactId>employer</artifactId>
<version>2.3.M2-SNAPSHOT</version>
<type>war</type>
<scope>compile</scope>
</dependency>
It is unable to build giving class not found exception.
Can any body help me out how to achieve this?
I am getting error maven build failed :
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project agent-war: Compilation failure: Compilation failure:
[ERROR] \projects\trunk_new\agent\src\main\java\com\platform\agent\web\AgentEmployeeController.java:[22,41] cannot find symbol
[ERROR] symbol : class EmployeeController
[ERROR] location: package com..platform.employer.web

You can define the war plugin to produce a separate jar file which is available via a classifier based on the configuration:
<configuration>
..
<attachClasses>true</attachClasses>
<archiveClasses>true</archiveClasses>
</configuration>
After that you can use this as a separate dependency in other projects. But the best to make a separate jar module out of it.

What you are doing is using a WAR file overlay. Maven does support this. However...
The WAR plugin executes in the package phase. Any plugin, such as the Java compiler (which runs in the compile phase), that executes before this phase will not see any files that the WAR plugin has extracted. (here is the reference list of all Maven phases if that helps)
If you have the ability to refactor your project structure, the best way is to create a normal JAR project with your controllers and have both WARs pull this JAR in as a dependency.
If there is some reason why you can't do this, you will need to do something like:
Configure the WAR plugin to run in a phase earlier than compile, such as generate-resources and makes sure it extracts the overlay class files to ${project.build.outputDirectory}.
You could also use the Maven Dependency Plugin's unpack goal to extract classes from the dependency WAR to ${project.build.outputDirectory}.
But I do recommend if at all possible to refactor your controllers into a separate JAR, it is much easier and more maintainable.

put the controllers into a new jar project and make dependencies from your webprojects to this controllers.

Add the following plugins to your share maven war module (for creating war and jar artifact at the same time):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<packaging>jar</packaging>
<artifactId>${project.artifactId}</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
<file>
${project.build.directory}/${project.artifactId}-${project.version}.jar
</file>
</configuration>
</execution>
</executions>
</plugin>
then add dependency to your dependent maven war module.

Java EE visability standards state that classes from one war shouldn't be available to classes in another war, when they're packaged as an EAR. Most containers will enforce this strictly.
You should put the code you wish to share into a JAR and include that as a dependency in the war you want to use.

In the war project pom.xml include
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
This will create your-project-0.0.1-SNAPSHOT-clases.jar in the target folder along with the war.
In the dependent project pom.xml include
<dependency>
<groupId>com.yourproject</groupId>
<artifactId>you-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<classifier>classes</classifier>
</dependency>
This worked for me. Hope it helps.
Edit: If we want only some class files into the jar then add this to pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>classifier_name</classifier>
<includes>
<include>com/../..</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
In the dependent project pom.xml include
<dependency>
<groupId>com.yourproject</groupId>
<artifactId>you-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<classifier>classifier_name</classifier>
</dependency>
note: the classifier name must be same in both pom.xml files

Related

Spring Boot Maven Plugin - No BOOT-INF directory

Between version 1.3.8.RELEASE of the spring-boot-maven-plugin and version 1.4.0.RELEASE - there has been a change in the generated package structure (if you extract the uber jar file)
1.3.8.RELEASE com, lib, META-INF and org directories
1.4.0.RELEASE has a BOOT-INF, META-INF and org directories
Basically from 1.4.0.RELEASE onwards - all the classes and libs are in the BOOT-INF directory.
Due to this - when you try to run a Spring Boot project on Amazon Lambda - it says that there is a jar not found as it cannot read the new Spring Boot Uber jar structure
My question is - is it possible in the newer versions of the Spring Boot Maven Plugin to get it to generate the uber jar to be the same structure as in version 1.3.9.RELEASE?
I tried the maven-shade-plugin - but that leads to other issues
Any help is greatly appreciated
Thanks
Damien
The solution was to add the MODULE layout for the plugin in the pom.xml file
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layout>MODULE</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
In my case I'm using spring boot 2.X and I declared the spring-boot-maven-plugin after the maven-dependency-plugin (which I used to unpack and create exploded app in Docker) and it must be before the unpack, makes sense, it was unpacking before the spring boot maven plugin executed. Next time I'll declare it first thing in the plugin chain, lost more than 1 hour on this. Hope it helps someone.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${spring.boot.mainClass}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
The answer above with
<layout>MODULE</layout>
does not work anymore, this is because layout element is deprecated in Spring Boot 2.x.
I am using Spring Boot 2.0.x, I found this helpful comment on github:
Support for the module layout was removed in Spring Boot 2.0 having been deprecated in 1.5. Unfortunately, the updates to the Maven Plugin's documentation were missed so we can use this issue to sort that out. You should use a custom LayoutFactory instead.
But as I did not want to implement LayoutFactory I tried this second solution below that actually repackage and creates an extra jar with a classifier given name:
This is due to the change in layout of executable jars in Spring Boot 1.4. Application classes are now packaging in BOOT-INF/classes.
Your client module depends on the repackaged, fat jar of your web module. Due to the new layout that means that the client module can no longer load the web module's classes. If you want to use your web module as a dependency, you should configure Boot's repackaging to apply a classifier to the fat jar. For example:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Doing so will allow other modules to depend on the original jar that does not embed the module's dependencies and has the classes at the root of the jar.
One original jar have the same structure as I wanted like
com.my-package.foo.bar
META-INF
and the second classifier have the newer structure with BOOT-INF/ etc.
For me, the solution was a bit more insidious....I had the spring-boot-maven-plugin nested under pluginManagement, (see below). Doh!
The nasty thing, is that when I'd run mvn spring-boot:run, spring boot comes up just fine, and runs app! It wasn't until we tried to deploy to PCF (as a spring-boot JAR), that we'd get an error that there was something wrong with format of the binary....
<build>
<!--
DON'T DO THIS!!
-->
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
<!--
DO THIS INSTEAD!!
-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Once I removed the pluginManagement tags from the POM, I would now get the ./BOOT-INF structure. Please keep in mind that pluginManagement is typically for a parent-pom structure, where you want that plugin's config used across other modules.
I was using Gradle, instead of Maven, and this is what I had to do:
1- In my build.gradle, I added the following properties as defined in https://spring.io/guides/gs/spring-boot-Docker/.
buildscript {
...
dependencies {
...
classpath('gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.13.0')
}
}
group = 'springio'
...
apply plugin: 'com.palantir.docker'
task unpack(type: Copy) {
dependsOn bootJar
from(zipTree(tasks.bootJar.outputs.files.singleFile))
into("build/dependency")
}
docker {
name "${project.group}/${bootJar.baseName}"
copySpec.from(tasks.unpack.outputs).into("dependency")
buildArgs(['DEPENDENCY': "dependency"])
}
2- My dependency folder was not being written to
ARG DEPENDENCY=target/dependency
instead, I located it in another folder, so I changed this property in the Dockerfile:
ARG DEPENDENCY=build/dependency
With this I got a successful build.

Maven: how to query the executable classpath?

I have a maven project with some specified dependencies.
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
How can I query maven to find out the path it's using for these dependencies, or the classpath I should use for independent execution?
My goal is to build a wrapper which runs the program with the appropriate classpath.
Several alternatives are available in Maven:
Maven Dependency Plugin (build-classpath goal)
Look at the Maven Dependency Plugin, especially the build-classpath goal provides exactly the full classpath for external execution usages. Among many options, The outputFile parameter may be helpful.
You don't need to configure it for usage, just run
mvn dependency:build-classpath
On your project and you'll see the classpath as part of the build output. Or
mvn dependency:build-classpath -Dmdep.outputFile=classpath.txt
To redirect just the classpath to a file.
Maven Dependency Plugin (copy-dependencies goal)
To build a wrapper, you could also look at the copy-dependencies goal, which would copy the required dependencies (jars), including transitive dependencies, to a configured folder (so you don't need hardcoded paths to your local machine).
An example of plugin configuration is available on the official site, here.
For instance, the following configuration:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/dependencies</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Would add to the folder target/dependencies all the dependencies declared in scope compile. NOTE: with respect to the linked official example, I added the <includeScope>runtime</includeScope> configuration entry (which will include compile and runtime scoped dependencies, according to documentation and my tests), otherwise it would also include the test scope by default (which is something I believe you would not need at runtime).
Exec Maven Plugin (java or exec goals)
Alternatively, you can use the Exec Maven Plugin to execute a main from Maven using the required classpath.
An example of plugin configuration is available on the official site, here.
The following configuration for instance:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>my-execution</id>
<phase>package</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.sample.MainApp</mainClass>
</configuration>
</plugin>
Will configure the Exec plugin to run via mvn exec:java the main class MainApp as configured, obviously with the required classpath.
Maven Assembly Plugin
Lastly, the Maven Assembly Plugin also provides facilities to build an executable jar with dependencies, as explained here, in another question on stackoverflow.

Maven package effective pom

I have a Maven project with a number of sub modules. Some of these sub modules are packaged as jar that are deployed to a Nexus Maven repository.
The problem I have is that the packaged jar references the parent pom which is not necessarily deployed.
Is there a way for Maven to deploy the effective pom instead of the pom.xml?
You need to be perfectly aware of the consequences of what you want to do: the effective POM will also contain your current settings (content of settings.xml), thereby possibly publicly exposing whatever passwords you have hard-coded in there. A better solution would be just to deploy the parent POM.
However, if you really want to go down that path, you can have the following configuration:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-help-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>effective-pom</goal>
</goals>
<configuration>
<output>${project.build.outputDirectory}/META-INF/maven/${project.groupId}/${project.artifactId}/pom.xml</output>
</configuration>
</execution>
</executions>
</plugin>
This tells the maven-jar-plugin not to add the Maven descriptor pom.xml and pom.properties to the jar. Instead, the pom.xml is generated by the maven-help-plugin and its effective-pom goal.
If you want the pom.properties file also, you will need to create it manually with the maven-antrun-plugin.

Providing resources files in Maven module

I am developing a new maven module and have some problems with resolving the dependencies. The goal is to create an maven object that provides my jar archive and a bunch of other resources. I created my pom.xml and installed the jar archive with install:install-file to the repository. That works's fine. But now I am struggling with the resources. Here is an example:
pom.xml
myJar.jar
resources/resourceA
resources/resourceB
resources/...
The myJar archive is useless without the resources the they need to be deployed in one package. Can you please provide me an example or a hint which plugin I should use?
Use the maven assembly plugin. We can do the following:
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptors>
<descriptor>src/assembly/jar_with_resources.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
</project>
You need to define the jar_with_resources.xml descriptor where you exactly say what is included in your assembly and what is not.

log4j configuration file in a multi-module Maven project

I am working on a multi-module Maven project, whose structure is like this:
war-module
jar-module
The war-module depends on the jar-module, and will add the jar artifact into the webapp's lib directory after packaging.
And both the war-module and jar-module use Apache log4j for logging, and share the same log4j configuration file (log4j.xml), which locates in jar-module project at present. And this log4j.xml will be packaged into jar-module.jar file, however, I would like to make it into WEB-INF/classes directory in the war package rather than in the jar file so that users will be easy to find this configuration file and modify it if necessary (it is very hard for them to find it if this file is in the WEB-INF/lib/jar-module.jar because there are many other jars under that directory).
My question is: what is the Maven way to solve this problem?
Update:
My real project is a bit more complex, and there is a ear-module which depends on the jar-module too (aka. the jar-module can be used independently in several different projects, and I cannot just put the file into war-module/src/main/resources directory to fix this problem). And I don't want to duplicate some configuration files such as log4j.xml (and other configuration files such as myapp.properties) across the several projects.
I found the answer via some more searching on the web.
Generally, there are three ways to share resources in a multi module Maven project:
Cut and paste them.
Use Assembly and Dependency plugins
Use the maven-remote-resources-plugin
Here's a blog post from Sonatype, the company behind Maven, on sharing resources across projects in Maven, and it is the exact answer I need:
http://www.sonatype.com/people/2008/04/how-to-share-resources-across-projects-in-maven/
In the jar module, exclude the file from the jar:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<excludes>
<exclude>log4j.xml</exclude>
</excludes>
</configuration>
</plugin>
Use the buildhelper plugin to attach the log4j.xml to the build as a separate artifact
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.outputDirectory}/log4j.xml</file>
<type>xml</type>
<classifier>log4j</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
Now in your war artifact, copy the xml to the output directory:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>your.jar.project.artifactId</artifactId>
<version>${project.version}</version>
<type>xml</type>
<classifier>log4j</classifier>
<outputDirectory>${project.build.outputDirectory}
</outputDirectory>
<destFileName>log4j.xml</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
But of course it would be easier to just put the file in [web-artifact]/src/main/resources in the first place :-)
From my experience this can be implemented in an easy way , just :
Make the log4j configuration resource at the parent module.
Call the dependency of log4j in all pom modules.
Detailed steps:
Create a project with common-config
In the src/main/resources folder put the log4j or logback config file
Install the artifact in the local Maven repository to be used by other projects
Add the dependency as a normal Maven dependency in the subsequent projects
<dependency>
<groupId>com.your.group.id</groupId>
<artifactId>common-config</artifactId>
<scope>provided</scope>
</dependency>
I prefer to use the scope provided and provide the file from a classpath config folder at runtime.

Categories

Resources