How to use AspectJ + Lombok + Maven + IntelliJ? - java

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<verbose>true</verbose>
<showWeaveInfo>true</showWeaveInfo>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
<verbose>false</verbose>
<Xlint>ignore</Xlint>
<outxml>true</outxml>
<forceAjcCompile>true</forceAjcCompile>
<reweavable>false</reweavable>
<!-- this is important: start-->
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
<!-- this is important: end-->
</configuration>
<executions>
<execution>
<!-- The right phase is very important! Compile and weave aspects after all classes compiled by javac -->
<phase>process-classes</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</plugin>
According to the standard, the aspectj-maven-plugin provides weaving during compilation. But Lombok is a compile-time preprocessor.
Therefore, in order for the weaving to occur after the code is compiled, the following is necessary.
In the plugin:
<forceAjcCompile>true</forceAjcCompile> и <sources/>
You also need to turn off the autobuild in Intellij Idea.
And execute the following command:
mvn clean compile install -Pdev
Only as a result of this command, the necessary weaving begins! Because of the "install" command.
Tell me, please, why is the weaving happening only thanks to this command? Or are other command options possible, for example, so that tests are not pulled up, etc.?

You also need to turn off the autobuild in Intellij Idea. And execute the following command:
mvn clean compile install -Pdev
the statement above is not correct from AspectJ perspective. There are three (if do not pay attention to Spring API) modes to use AspectJ:
compile time - when we need to compile native aspectJ (.aj) syntax files or use inter-type declarations
post-compile time - we compile sources using javac and after that ajc processes resulting classes - that is your case cause you have specified <sources/>
load time weaving - that requires to either specify -javaagent in JVM arguments or have specialised classloader
technically, from maven lifecycle perspective everything should work fine in case of post-compile time weaving, however IDEs typically do not trigger maven lifecycle when you are pressing green arrow near #main or #Test method, instead of that IDEs try to analyse project object model (pom) and make some assumptions about target classpath and dependencies. Unfortunately, IDEs do not support all maven plugins and that is the main reason why sometimes something does not work as expected (for example, IntelliJ do not support code generators like OpenAPI or Axis2, that is why we place that stuff into separate maven modules/projects and run mvn install). It seems that purpose of your mvn ... install is to place correct jar into .m2/repository and give IDE a chance to pick up that jar.
UPD. Some explanations....
Let consider a case when we need to run individual unit test, from maven perspective that would be something like:
mvn -am clean test -Dtest=TestClass#testMethod*
and maven will launch it without facing any difficulties, because before test step it will start compile and test-compile steps, where javac and ajc do the required job. Unfortunately, from developer perspective the maven command mentioned above "takes ages", and that is the reason why IDE tries to optimise that process by compiling only changed classes, unfortunately, if IDE does not support AspectJ (for example, IntelliJ CE does not) or does not recognise maven configuration it has no chance to produce expected output.
Your case is even more complicated because you are using lombok, which is not compatible with ajc (or vice versa), so to produce correct classes you need first compile sources by javac and then postprocess output by ajc.
If your goal is to somehow simplify IDE setup for particular project I would recommend the following:
provide reproducible example, describe your goals and difficulties you have faced with
try to install AspectJ plugin (if you are not on IntelliJ CE), it seems it has some useful options:

Related

Classes in test package not available at compile time in IntelliJ

I have a setup like the following:
Where I have two modules: modulea and moduleb, in this case, moduleb has a dependency to modulea defined as:
<dependency>
<groupId>org.example</groupId>
<artifactId>module-a</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
</dependency>
This allows me to use ClassInTestA in ClassInSourceB without any issues while developing:
However, when I try to build the project, this error prevents IntelliJ to complete the build:
I have come across similar questions in SO:
Question 1
Question 2
Question 3
However, none of the proposed solutions has been able to help my case. I have created an MVCE that is available here as zip and in GitHub.
The real-world project I'm working is neo4j, which follows this structure. Moreover, compilations using mvn install/package work without any issue, the problem appears when working inside IntelliJ.
In general, it makes sense to "open" a new project by building it first outside of IntelliJ with mvn clean package and then import it by just "open"ing the parent module. This worked for me:
And even after a rebuild:
If you don't want to reimport your project by deleting all IDEA folders and files and use the described way above, you can try to build the project via the Maven toolbar (clean and package on the parent module) and then use the "Reimport all Maven projects" button:
At least sometimes this works for me, but honestly not always.
Test classes aren't packed in the final artifact. To share the test classes you'll have to use the jar maven-jar-plugin in modulea:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
and add a dependency in moduleb's pom.xml:
<dependency>
<groupId>org.example</groupId>
<artifactId>module-a</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>tests</classifier>
</dependency>
Test sources are not included during the compile phase
See Apache Maven Compiler Plugin:
compiler:compile is bound to the compile phase and is used to compile
the main source files.
I think main sources should not depend on test sources. Test sources are only for testing the main sources. You could place ClassInTestA under module-a/src/main/java.

Serial execution of plugins in Maven using <phase>

I have the following structure in pom.xml:
<profile>
<id> x </id>
<build>
<finalName> y </finalName>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<!-- DELETE SOME FOLDERS AND SOME FILES -->
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<!-- MOVE A FILE -->
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version> 1.6 </version>
<executions>
<execution>
<phase> <!-- WHAT COMES HERE ? --> </phase>
<configuration>
<!-- I EXECUTE CMD FILE HERE -->
</configuration>
<goals>
<goal> run </goal>
</goals>
<execution>
<executions>
</plugin>
</plugins>
</build>
</profile>
I want to achieve a serial execution for plugins:
Delete some folders and some files (1st plugin)
Move a file (2nd plugin)
Run cmd file (3rd plugin)
What should I use for <phase> to have the order above (sequential execution)? Is it relevant ? Is it ok to use pre-integration-test for example ?
P.S.: Here are the phases: http://maven.apache.org/ref/3.2.2/maven-core/lifecycles.html
Maven doesn't support adding steps to the life cycle (without writing a new plugin).
If you have complex build steps and you absolutely need a certain order and this order violates the standard life cycle, then Maven might not be the right tool for you. Consider Gradle instead.
If you have to use Maven, then stop using plugins for this. Instead, replicate all the build steps using the antrun plugin so everything is done with a single Ant script. Afterwards, find a place in the life cycle where you want all the steps to be executed at once.
generate-sources is a good place if you need something to be done before anything is compiled. If you want to run code after compilation and tests, use prepare-package.
See here for the complete life cycle: http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html#Lifecycle_Reference
If you like to add the execution of different plugins to the same life cycle phase the execution order is defined by the order of the definition in the pom file.
So if you choose to bind all the above three plugins to the package phase than they will be executed in the given order.
You can of course use the antrun plugin which will work also and will implicit execute the steps in the defined order without thinking about the default behaviour of Maven.
You can also use the exec-maven-plugin to execute a script in a particular life cycle phase but which might be not a good solution cause the script is OS dependent. Or you can use the groovy plugin to execute some steps in groovy code.

Different Maven versions give different pmd results

One of my projects used the maven-pmd-plugin and I have found that I receive different results when using Maven 3.0.5 and Maven 3.2.1. All other variables (Java version, OS version, project configuration). I've also tried Maven 3.1.1
I'm running
mvn clean pmd:pmd
on the command line
In Maven 3.0.5, I get one warning:
<violation beginline="44" endline="307" begincolumn="8" endcolumn="1" rule="MoreThanOneLogger" ruleset="Java Logging" package="com.adobe.acs.commons.logging.impl" externalInfoUrl="${pmd.website.baseurl}/rules/java/logging-java.html#MoreThanOneLogger" priority="2">
Class contains more than one logger.
</violation>
In Maven 3.2.1 (and 3.1.1), I get zero warnings.
I also tried removing my custom rulset and now have the maven-pmd-plugin configured like this:
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.1</version>
<configuration>
<linkXRef>false</linkXRef>
<rulesets>
<ruleset>/rulesets/java/logging-java.xml</ruleset>
</rulesets>
<targetJdk>1.6</targetJdk>
</configuration>
</plugin>
With the same result.
Knowing the wider context of your issue helped me to arrive at the solution without too much discussion.
There is a difference in the graph of implicit dependencies between the various maven versions, as in the ones that come along with maven as baked in defaults. I am able to reproduce this issue locally, and when I add the -X switch for debug mode, and run a diff for mvn clean pmd:pmd pmd:check -X on both maven versions, I see the following import in the 3.2.1 output, which I thought was a possible candidate for follow-up, since it has to do not only with the kind of logging that the rule checks for, but also because I happen to know you are using slf4j:
[DEBUG] Imported: org.slf4j.* < plexus.core
It appears that SLF4j was added to plexus.core, which may mean that the slf4j api is available to all plugin classpaths now, whereas before it would require that the plugin imported the library in its own dependencies.
I went to see what the PMD MoreThanOneLoggerRule is looking up, and it turns out it only looks for Log4j loggers and JCL loggers by resolved type, and anything named "Logger" if it can't resolve the type:
http://pmd.sourceforge.net/pmd-5.1.1/xref/net/sourceforge/pmd/lang/java/rule/logging/MoreThanOneLoggerRule.html
The solution appears to be to add the slf4j API right in the pmd plugin dependencies, as in:
<plugin>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.1</version>
<configuration>
<linkXRef>false</linkXRef>
<rulesets>
<ruleset>/rulesets/java/logging-java.xml</ruleset>
</rulesets>
<targetJdk>1.6</targetJdk>
</configuration>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
</plugin>
This puts org.slf4j.Logger on the PMD classpath, so that it is able to resolve the type during analysis. Since it is neither a Log4j Logger or JCL Logger, it no longer cares about having more than one of them.

Maven, Lombok and Eclipse - Meaningful Integration Pattern

So, I've been wanting to use Lombok for a while - and I'm finally starting a project where I will be able to use it. The important thing to note is that this will be a large, enterprise-grade application and thus the integration patterns used have to be meaningful, with as few hacks as possible.
So I've taken a look at the lombok-maven-plugin, and the whole delombok fudge. I understand this is going to duplicate all my code, and expand the lombok annotations where present. This gives me a second set of generated .java files that need to be used by maven during compilation.
However, by generating these new source files - eclipse picks them up and attempts to pull them into my project. Thus it fires off a million (OK, slight exaggeration) errors about duplicate classes.
Some solution suggest I change the <sourceDirectory> in my POM. This makes things no better, as a mvn eclipse:eclipse will now completely omit my src/main/java java directory from the project - only showing me the output from the delombok process.
Then come the suggestions that I need to use one profile to compile / package the project, and another to mvn eclipse:eclipse. This is not an acceptable solution, as I’m having to spend enough time maintaining / explaining my already complex maven setup – without having to introduce an entire new profile (in addition to my existing profiles).
I’m hoping for some inspiration to save me from writing off Lombok for my project. It’s a great tool to reduce boilerplate code, but it simply doesn’t seem ready for prime-time enterprise usage – which I find very disappointing :-(
The following is my current POM:
<build>
<sourceDirectory>target/generated-sources/delombok</sourceDirectory>
<testSourceDirectory>target/generated-test-sources/delombok</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.12.2.0</version>
<dependencies>
<dependency>
<groupId>sun.jdk</groupId>
<artifactId>tools</artifactId>
<version>1.7</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
<executions>
<execution>
<id>delombok</id>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
</configuration>
</execution>
<execution>
<id>test-delombok</id>
<phase>generate-test-sources</phase>
<goals>
<goal>testDelombok</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>src/test/java</sourceDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Which is currently only putting the delomboked code in my eclipse project.
Final note - I'm also quite frustrated that I have to manually install lombok on all the eclipse instasnces we're going to use. Mostly because it's me who will get the phonecall from all the developers that can't get it working. I get why it's not as simple as mvn eclipse:eclipse but I still wanted to note my dissapointment. If we had to manually set up every library for use on each developer's machine, we'd be back in the pre-maven days.
We are successfully using lombok in our project for 1.5 years now. We're not using any delombokification, but instead have lombok as a provided dependency like so
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>0.11.0</version>
<scope>provided</scope>
</dependency>
That's all it needs. We can use the lombok annotations and they are recognized by both eclipse and maven builds. This is even compatible with EclEmma in eclipse, which marks the annotations as (un)covered, when the respective generated code is(n't).
You have to install it on every Eclipse instance manually, since most of the JDT isn't open for eclipse plugins to modify. That's a technical restriction the lombok developers cannot lift. Anyways, the installer is quite simple and never failed me so far.
You don't necessarily need to use lombok-maven-plugin to take advantage of Lombok. As I understand, the delombofication that the plugin does is meant to allow things like code coverage and javadoc to have a full version of the code. Even then, the process would only occur at, say, Javadoc building time.
The question is if your project can live without that. If yes, then just adding lombok as Maven dependency is all you need.
In Eclipse you'll indeed need to install it. Note that the fact the Lombok is still kind of experimental, is perhaps of the reasons it's not included in Eclipse by default.

Disable maven build number plugin

Currently we are working on the big maven project that has about 100 modules, some of them have submodules as well.
Some of modules use Maven Build Number plugin. The project is hosted under subversion.
Recently we started to use git locally in our development team.
After cloning subversion repo and trying to build the Project, we received following well known error:
The svn command failed.
Command output:
svn: ‘.’ is not a working copy
Unfortunately in our case it is not an option to create a new profile or just remove plugin definition from POM (this will follow to messing up hundreds of POM files).
I found the following article http://abstractionextraction.wordpress.com/2012/09/27/git-svn-vs-maven-build-number-plugin/ but honestly, it's not something that I would really like to do...
Is there any smart way to disable this plugin. Like command-line parameter?
I think you may skip failure on obtain revision without change project pom.xml - buildnumber-maven-plugin has option revisionOnScmFailure which you may use like:
mvn -Dmaven.buildNumber.revisionOnScmFailure=no-scm package
In that case value no-scm will be used if scm call was unsuccessful. Off course you may change it and provide any other string.
Per the mojo documentation, you could use the revisionOnScmFailure property.
However, it doesn't have a command line option. You'll have to modify those pom.xml files.
See "Defining Parameters Within a Mojo" in the Maven Java Plugin Development Guide
One approach would be to use a property in your pom to specify the execution phase of the build number plugin, as shown below.
<project>
..
<properties>
<buildnumber.plugin.phase>validate</buildnumber.plugin.phase>
..
</properties>
..
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.2</version>
<executions>
<execution>
<phase>${buildnumber.plugin.phase}</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
..
</configuration>
</plugin>
</plugins>
..
</project>
Then provide the property on the command line to disable the plugin, as shown in the following example.
mvn install -Dbuildnumber.plugin.phase=none

Categories

Resources