Duplicate classes when using Maven AspectJ weave dependencies - java

We are using the Maven AspectJ plugin to build our web application. It makes use of "weaveDependencies" to add aspects to some dependency jar files.
Now we end up with two versions of some classes in the web application archive, one in WEB-INF/classes and one in the original jar file in WEB-INF/lib. It seems that only the one in classes has the aspects.
I am afraid that this can cause problems.
What is the best way to fix this?
The same problem is discussed (without solution) over at the Eclipse forums.
The whole pom.xml itself is huge, and of course the sub-projects that are included have their own, too. I hope the extract below from the WAR project is informative enough.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
<filters>
<filter>${basedir}/src/etc/${environment}/environment.properties</filter>
</filters>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version> <!-- NB: do use 1.3 or 1.3.x due to MASPECTJ-90 - wait for 1.4 -->
<dependencies>
<!-- NB: You must use Maven 2.0.9 or above or these are ignored (see
MNG-2972) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<weaveDependencies>
<weaveDependency>
<groupId>OURPROJECT</groupId>
<artifactId>OURPROJECT-api</artifactId>
</weaveDependency>
<weaveDependency>
<groupId>OURPROJECT</groupId>
<artifactId>OURPROJECT-service</artifactId>
</weaveDependency>
</weaveDependencies>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>

In a servlet container and inside a WAR, the classes inside WEB-INF/classes always have precedence over classes with the exact same name found inside a jar in WEB-INF/lib.
This is a quote from the servlet spec:
The Web application class loader must load classes from the WEB-INF/
classes directory first, and then from library JARs in the
WEB-INF/lib directory.
This has been so since at least Servlet 2.4. This allows an application to selectively patch just a few library classes without having to repackage jars manually or via maven plugins.
In your case you can be certain that the classes with the aspects will always be taken, as they are in WEB-INF/classes and have priority over classes in WEB-INF/lib.

Related

AspectJ weaving from dependency not applying to project

I have a Java 8 Maven project that defines a custom annotation and an aspect. When running test code in that project itself, it is applying the aspect to the annotated classes. I am then packaging and installing project.
I then bring in that dependency into a new project (non-Spring). The new project is then not having the aspect applied to it's classes, though it does bring in the new annotation.
How do I have a single JAR to define an annotation and aspect and have it applied to all of my projects with Maven?
You need to specify your aspect project dependency as an aspect library in your aspectj-maven-plugin configuration in your pom.xml. Let's suppose your aspect module has the groupid:artifactid groupid:aspect-module. Your pom.xml should look similar to this:
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>groupid</groupId>
<artifactId>aspect-module</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>groupid</groupId>
<artifactId>aspect-module</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Note that I'm switching off the maven-compiler-plugin because they tend do overwrite each other's output with the aspectj-maven-plugin, and the AspectJ compiler should be able to compile normal java files and weave them in the same step anyway, so using the maven-compiler-plugin is redundant. If you are using Eclipse + AJDT, this maven configuration will much better reflect what happens in your IDE while you're developing.

Libraries not included in javaFX jar

I have built a javaFX application using Maven.The problem is that the javaFX jar file generated from maven, doesn't contain dependencies included.
I have included the javaFx dependency this way :
<dependency>
<groupId>com.oracle</groupId>
<artifactId>javafx</artifactId>
<version>8.0.151</version>
<scope>system</scope>
<systemPath>C:\Program Files\Java\jdk1.8.0_151\jre\lib\ext\jfxrt.jar</systemPath>
</dependency>
I have also included the javaFx plugin :
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx.plugin.version}</version>
<configuration>
<vendor>Vendor</vendor>
<mainClass>org.bsh.generator.App</mainClass>
<jarFileName>CSVGenerator</jarFileName>
</configuration>
<executions>
<execution>
<id>create-jfxjar</id>
<phase>package</phase>
<goals>
<goal>build-jar</goal>
</goals>
</execution>
</executions>
</plugin>
When I build the jar using goal "build-jar", the jar generated doesn't include other dependencies. Any help please?
Dependencies scoped as system are not respected, this is intentional. Only provided and runtime scoped dependencies/libraries are respected by the plugin.
Disclaimer: I'm the maintainer of the javafx-maven-plugin.

Running JDK8 for aspectj

I am trying to run aspectj-maven plugin with JDK8. But it is giving errors like
"The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files"
Any help on how to resolve, or if the aspectj-maven-plugin supports JDK8. I am using 1.6 version of aspectj--maven-plugin.
I had to achieve the same and I drove crazy trying to figure out this, fortunately I could solve it and here I give you what I did:
To use aspectj-maven-plugin with Java 8 you need version aspectj-maven-plugin 1.7 (Note that aspectj-maven-plugin 1.6 works for Java 7).
So, the maven plugin configuration needs to be:
<!-- AspectJ configuration -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7-SNAPSHOT</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
By the way, the aspectJ jars needed are:
<!-- Spring AOP + AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.1</version>
</dependency>
And the most important thing I've struggled was that you need to install the aspectj-maven-plugin 1.7 jar manually into your pom.xml since this jar aren't on maven repo yet.
You can get it from Haus Jira (look at the Attachment section):
https://jira.codehaus.org/browse/MASPECTJ-131
Btw, once you download it an copy it to your repo you need to create your own aspectj-maven-plugin-1.7-SNAPSHOT.pom file within the corresponding directory. You can copy it from version 1.6 BUT ensure you modify the following content:
<version>1.7-SNAPSHOT</version>
<properties>
<aspectjVersion>1.8.1</aspectjVersion>
<mavenVersion>2.2.1</mavenVersion>
<changesPluginVersion>2.9</changesPluginVersion>
</properties>
That's all here you go, hope to help.
You don't need 1.7-SNAPSHOT for that. I have this snippent in POM and everything works:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.1</version>
</dependency>
</dependencies>
</plugin>
Another way to solve this problem is to downgrade JDK level to 1.7. JDK 1.7 works well with aspectj-maven-plugin of versions 1.4 - 1.6.
Here is a screenshot showing how to change JDK level in IntelliJ IDEA project:
Set Project SDK to 1.7. See documentation for more details.

Log4j2 custom plugins - annotation processing with Maven Assembly Plugin

I am not very familiar with Maven, I started using it just yesterday, but I like it. In my project I use Log4j2 library for logging and because of insufficiecy of advanced plugins (like appenders, converters) I need to use custom plugins. log4j-api and log4j-core (also with a bunch of other libraries) are added as dependencies in pom.xml associated with my project. Actually I am using version 2.0 of Log4j.
Log4j uses annotation processing to pre-load classes marked as #Plugin. As far as I know, in older releases of log4j, additional plugin entry had to be specified in pom.xml to trigger plugin processing, or the packages with custom plugins had to be typed into the packages attribute in the configuration file (https://logging.apache.org/log4j/2.x/manual/configuration.html#ConfigurationSyntax). But this is not supported since 2.0-rc2.
In v2.0 this should be done automatically, as long as log4j-core is available to the building engine. There is a file Log4j2Plugins.dat in myproject-0.0.1-SNAPSHOT.jar/META-INF/org/apache/logging/log4j/core/config/plugins/ that contains mappings of my custom plugins - that's OK.
For building with Maven I use also Maven Assembly Plugin. Its goal single is binded to package phase. After packaging the project I naturally have one additional jar in the target directory - myproject-0.0.1-SNAPSHOT-jar-with-dependencies.jar. However, Log4j2Plugins.dat file in this jar contains mappings of original plugins, the same file as in the log4j-core library. And that's the problem, since it doesn't hold any references to my custom plugins. It seems that the file from myproject-0.0.1-SNAPSHOT.jar is being overwritten with the original file from the log4j library, but I am not sure what's the case.
So when I run myproject-0.0.1-SNAPSHOT-jar-with-dependencies.jar, log4j can't find the plugin classes from my project. I think that myproject-0.0.1-SNAPSHOT.jar would run ok, but I can't run it without the dependencies.
The packages attribute in configuration should be re-enabled in 2.0.1 release, but if I don't want to wait for the release, I have to use the annotation processing method.
Do you have an idea how to fix it?
I tried to run it with release 2.0-rc1 of log4j, where the packages attribute of the configuration element was usable. The result is: log4j successfully loaded the class of my custom plugin. However, there were so many other errors (that arised in this specific release) that make the program even more unusable.
This is one good point, that ensures me that if the packages attribute will be enabled in next release 2.0.1, my plugin will work. It should be reinstated according to this issue tracking: https://issues.apache.org/jira/browse/LOG4J2-741
Added my pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jjurm</groupId>
<artifactId>twbot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>twbot</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.jjurm.twbot.system.Run</mainClass>
</manifest>
</archive>
</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>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.jjurm.twbot.system.Run</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>net.snaq</groupId>
<artifactId>dbpool</artifactId>
<version>6.0</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>commons-jxpath</groupId>
<artifactId>commons-jxpath</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.15</version>
</dependency>
</dependencies>
</project>
I think the problem stems from packaging the dependencies into the jar. Doing a quick dive into the code, it looks like the plugin processor overwrites the plugin dat file for each set of plugins it handles. My guess is that during the packaging process, your custom plugins are processed and written to the dat file, and then overwritten when your log4j dependency is processed for inclusion in the package. There may be better solutions, but off the top of my head I would suggest that you do one of the following:
Do not package the dependencies into your jar. Just package your project and then include the dependencies on your classpath when you execute. Even if you want to package everything in a single portable jar, doing this would allow you to at least confirm if your plugins are being overwritten, or if there is something else wrong.
Create a separate project for your custom plugins, package it separately from your main project, and then include the resulting jar as a dependency. As with option 1, make sure that you do not include the log4j jars in this package. Once you have created your custom plugin jar, you can package it along with the other dependencies in your main jar and it should work fine since your custom plugin jar will have its own plugin dat file.
Good luck!
Another solution is mentioned in Log4j 2 issue 673. Use maven shade plugin with specific transformer instead of maven assembly plugin.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<transformers>
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
</transformers>
</configuration>
<dependencies>
<dependency>
<groupId>com.github.edwgiz</groupId>
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
<version>2.6.1</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
If I understand it accurately, transformer creates Log4j2Plugins.dat file by correctly merging Log4j2Plugins.dat from all dependencies and the main jar, i.e. all plugins will be included.

repackage dependencies and fix pom

My maven project has a dependency. I use a few 3rd party classes. I would like to automatically repack them into my jar and exclude the dependency from pom.xml stored in jar file.
i checked shade plugin and jarjar plugin. none of them replaces pom in produced jar. what's the point of including dependencies' binaries if those dependencies are still listed in pom? how should i properly repack dependencies?
Maven shade plugin has the feature you need. After running
mvn shade:shade
It generates file named dependency-reduced-pom.xml in your projects folder, and this file doesn't have dependencies which are already placed in jar.
This behaviour is configured via following shade-plugin options:
createDependencyReducedPom
dependencyReducedPomLocation
You can use the maven-dependency-plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id></id>
<phase>process-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<includes>org\/apache\/commons\/lang\/StringUtils.class</includes>
</configuration>
</execution>
</plugin>
And then set the dependency scope of commons-lang to provided:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>provided</scope>
</dependency>
commons-lang will not be carried as a transitive dependency nor be included in the jar packaging, but StringUtils will be included in the jar.

Categories

Resources