Create and install de-lomboked source jar in maven - java

Disclaimer: I have solved this problem and am documenting the solution for the world to know.
How do I create and install a *-sources.jar containing "delomboked" source code in maven?
By default, The maven-source-plugin creates a sources jar without delomboking the source files, which causes projects that depend on the library binaries to complain about mismatching source files.

TL;DR (explained beneath)
Add the following plugins configuration to your plugins configuration in the project.build element of your pom.xml
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.0.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<configuration>
<sourceDirectory>src/main/java</sourceDirectory>
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
<addOutputDirectory>false</addOutputDirectory>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy-to-lombok-build</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>generate-delomboked-sources-jar</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<jar destfile="${project.build.directory}/${project.build.finalName}-sources.jar"
basedir="${project.build.directory}/delombok"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>install-source-jar</id>
<goals>
<goal>install-file</goal>
</goals>
<phase>install</phase>
<configuration>
<file>${project.build.directory}/${project.build.finalName}-sources.jar</file>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<classifier>sources</classifier>
<generatePom>true</generatePom>
<pomFile>${project.basedir}/pom.xml</pomFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Explanation
lombok-maven-plugin will enable you to delombok the source code (${project.basedir}/src/main/java) and place it in the target directory (${project.build.directory}/delombok). Usually this will place the code in the ${project.build.directory}/generated-sources/delombok folder, but because Intellij automatically considers this additional source-code, duplicate code errors will occur when developing your library, in order to stop this, just specify a non-default target directory (in this case just outside of the generated-sources dir).
maven-resources-plugin is necessary in order to also copy resources from the standard ${project.basedir}/src/main/resources directory. If there are any other non-standard resource directories in your project, you should configure them in the resources section for this plugin.
maven-antrun-plugin is used instead of the maven-source-plugin because you cannot specify a custom source directory in the latter. The jar task points to our custom "generated-sources" and produces the standard-named sources jar.
maven-install-plugin install-file goal is used because you cannot attach jars using the install goal. We can hack a solution by manually installing a file using the install-file goal with a classifier of sources.
I hope this helps others who are on struggle street like I was with this problem.

The following solution is based on the one offered above but improves it by using the build-helper plugin to attach the generated delomboked source jar instead of using install-file. This has the benefit that the normal maven install and deploy phases correctly handle the generated file just as they would if the sources plugin had been used.
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.12.0</version>
<executions>
<execution>
<id>delombok-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
<configuration>
<sourceDirectory>src/main/java</sourceDirectory>
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
<addOutputDirectory>false</addOutputDirectory>
<encoding>UTF-8</encoding>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>generate-delomboked-sources-jar</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<jar destfile="${project.build.directory}/${project.build.finalName}-sources.jar"
basedir="${project.build.directory}/delombok"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>attach-delomboked-sources-jar</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.directory}/${project.build.finalName}-sources.jar</file>
<type>jar</type>
<classifier>sources</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Would like to point out that a profile can also be used (to get around the build source directory being un-customizable). The solution is described at https://sudonull.com/post/1197-Lombok-sourcesjar-and-convenient-debug
Add the following to the pom.xml
properties:
<origSourceDir>${project.basedir}/src/main/java</origSourceDir>
<sourceDir>${origSourceDir}</sourceDir>
<delombokedSourceDir>${project.build.directory}/delombok</delombokedSourceDir>
</properties>
Profile and build section changes:
<profiles>
<profile>
<id>build</id>
<properties>
<sourceDir>${delombokedSourceDir}</sourceDir>
</properties>
</profile>
</profiles>
<build>
<sourceDirectory>${sourceDir}</sourceDirectory>
<plugins>
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.20.0</version>
<executions>
<execution>
<id>delombok</id>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
<configuration>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>${origSourceDir}</sourceDirectory>
<outputDirectory>${delombokedSourceDir}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Execute with mvn clean install -Pbuild
This should solve the "Library source does not match the bytecode for class" error in IntelliJ and allow seamless debugging in most cases.
Ref: "Delombok plugin + profile in maven"

Both of the answers are flawed for multi-module projects or pure pom projects, because there are no sources, so you will have to create an empty directory, and it'll produce an empty .jar.
There is a simple (but a bit more complex) way to achieve functionality you want: make your own Maven Plugin.
Sounds overly complicated, but we can re-use maven-sources-plugin and with MOJO's inheritance update necessary parts:
import java.util.List;
import java.util.stream.Collectors;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.source.SourceJarNoForkMojo;
import org.apache.maven.project.MavenProject;
/**
* This goal bundles all the sources into a jar archive, but uses delomboked sources.
*/
#Mojo(name = "jar-no-fork", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true)
public class EnhancedSourceJarNoForkMojo extends SourceJarNoForkMojo {
#Parameter(property = "<some-prefix>.useDelombokSources", defaultValue = "true")
protected boolean useDelombokSources;
#Parameter(property = "<some-prefix>.delombokSourcesLocation", defaultValue = "delombok")
protected String delombokSourcesLocation;
#Override
protected List<String> getSources(MavenProject p) {
// if user doesn't want delomboked sources, use default algorithm
List<String> sources = super.getSources(p);
if (!useDelombokSources) {
return sources;
}
// typically, sources' list will contain: [src/main/java, target/generated_sources].
// replace src/main/java if it's present with delombok-generated sources
String target = p.getBuild().getDirectory();
return super.getSources(p)
.stream()
.map(s -> s.endsWith("java") ? String.format("%s/%s", target, delombokSourcesLocation) : s)
.collect(Collectors.toList());
}
}
Gist with pom.xml routine is available here.

Related

Exclude *target* directory from the maven-checkstyle-plugin scan

I am using Apache Maven Checkstyle plugin in my pom.xml.
I am trying to exclude the target directory from the check style scan but no luck so far. Here is the pom code i am trying.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<id>checkstyle-check</id>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<configLocation>checkstyles.xml</configLocation>
<failsOnError>true</failsOnError>
<failOnViolation>true</failOnViolation>
<consoleOutput>true</consoleOutput>
<includes>**\/*.java,**\/*.groovy</includes>
<excludes>**WHAT GOES HERE TO EXCLUDE THE TARGET DIRECTORY**</excludes>
</configuration>
</plugin>
In Apache Maven Checkstyle Plugin version 3 to specify the location of the source directories we have to use sourceDirectories parameter. Then we can specify only directories of application/library and test sources to be used for Checkstyle:
<sourceDirectories>
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
<sourceDirectory>${project.build.testSourceDirectory}</sourceDirectory>
</sourceDirectories>
Now only src/main/java and src/test/java will be analysed.
Here is my full working example:
<!-- Apache Maven Checkstyle Plugin (checks Java code adheres to a coding standard) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<sourceDirectories>
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
<sourceDirectory>${project.build.testSourceDirectory}</sourceDirectory>
</sourceDirectories>
<!-- relates to https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml -->
<configLocation>/src/main/resources/checkstyle.xml</configLocation>
</configuration>
</plugin>
<excludes>**/generated/**/*</excludes>
This will remove the generated files from the plugin.

How to add src/test/java contents to the output maven jar that gets created

I see a lot of similar questions. But unable to make this work.
I have tried testresources and build-helper-maven-plugin so far
Also I read in 1 thread how to write my own assembly plugin to do something like that.
But posting this again to see if there are cleaner ways that I don't know of
This is existing code and i got to fix it. The thing is when i open the jar after a successful build i am unable to find the src/test/java classes inside the jar. We got a maven build-helper-maven-plugin and maven-jar-plugin. But I don't see the test classes in it still.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/test/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>test</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
I see the generated classes in a test-classes directory inside the target folder. But not inside the jar
I want them inside the jar as I am depending on that jar in another project. The other project is not compiling because its importing that test class inside src/test/Java
I cannot create a new project just for this case as I don't have that liberty.
Did you try maven-dependency-plugin (instead of build-helper-maven-plugin) in combination with maven-jar-plugin ?
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.9</version><!--$NO-MVN-MAN-VER$-->
<executions>
<execution>
<id>unpack</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>you-project-group-id</groupId>
<artifactId>you-project-artifact-id</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>

Including a synthetic file as javadoc artifact in a Maven build

I am building a multimodule project with Maven where one module represents the shaded version of the other module:
parent
|- base
|- base-shaded
The base module contains all my source files but with dependencies I want to import into my own namespace. This shaded module is described by my base-shaded module which basically only contains a POM which configures a dependency on base and the Maven Shade plugin.
This works fine. The Shade plugin includes all the dependencies and creates a shaded artifact, including a source artifact. However, I am missing a javadoc artifact which is not created by the Shade plugin. Therefore, I attempted to copy the base module's javadoc artifact. However, the release:release goal ignores these artifacts which prevents me to deploying to Maven Central. Still, I managed to include copy these files and include them in install target.
Is there a canonical way of including a non-assembled file in a build? I start to think that I might be the wrong approach.
Here is the plugin configuration of my base-shaded POM. Sorry that it is so much, but in the end, it is Maven XML:
<!-- Shade dependencies -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${version.plugin.shade}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>false</shadedArtifactAttached>
<createDependencyReducedPom>true</createDependencyReducedPom>
<dependencyReducedPomLocation>
${project.build.directory}/dependency-reduced-pom.xml
</dependencyReducedPomLocation>
<createSourcesJar>true</createSourcesJar>
<shadeSourcesContent>true</shadeSourcesContent>
<relocations>
<relocation>
<pattern>${shade.source}</pattern>
<shadedPattern>${shade.target}</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<!-- Copy dependency version's javadoc artifacts -->
<plugin>
<groupId>com.github.goldin</groupId>
<artifactId>copy-maven-plugin</artifactId>
<version>${version.plugin.copy}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<resources>
<resource>
<targetPath>${project.build.directory}</targetPath>
<file>
${project.basedir}/../base/target/base-${project.version}-javadoc.jar
</file>
<destFileName>base-${project.version}-javadoc.jar</destFileName>
</resource>
<resource>
<targetPath>${project.build.directory}</targetPath>
<file>
${project.basedir}/../base/target/base-${project.version}-javadoc.jar.asc
</file>
<destFileName>base-shaded-${project.version}-javadoc.jar.asc</destFileName>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<!-- Because the javadoc files are copied manually, they must be installed manually as well -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<executions>
<execution>
<id>install-javadoc</id>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<packaging>jar</packaging>
<classifier>javadoc</classifier>
<file>${build.directory}/base-shaded-${project.version}-javadoc.jar</file>
</configuration>
</execution>
<execution>
<id>install-javadoc-asc</id>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<packaging>jar.asc</packaging>
<classifier>javadoc</classifier>
<file>${build.directory}/base-shaded-${project.version}-javadoc.jar.asc</file>
</configuration>
</execution>
</executions>
</plugin>
Of course, there is a plugin for this task, the build-helper-maven-plugin.
We use something like the following to attach the javadoc produced by the unshaded module into the shaded module
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>attach</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>../unshaded-module/target/unshaded-module-${project.version}-javadoc.jar</file>
<type>jar</type>
<classifier>javadoc</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>

Place all libraries and files for a project in one folder

I use maven in my java build process. The following is a snippet of code that creates an single jar with all dependencies. In order to reduce the data transfer on small changes to the build I'd like to place all project files (including dependencies) in the folder target/build . I plan to rsync the folder with the remote machine running the app and run the app with:
java -cp target/build/* <classname>
How do I modify this snippet to achieve this? I've read the documentation here but don't know how to piece the fix together:
http://maven.apache.org/plugins/maven-assembly-plugin/
http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html
http://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.3</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
Are you asking how to get maven to copy your dependencies to the target folder when you build?
I think you want the maven dependency plugin. It copies the dependencies of your project to an output folder you specify.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>install</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${targetDirectory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
It sounds like you may also need to maven jar plugin to tell it where to package your jar to.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<outputDirectory>${dir}</outputDirectory>
</configuration>
</plugin>
Use the maven dependency plugin
It has the gole: copy-dependencies. This should do what you want.
Example (take from the documentation)
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/alternateLocation</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
[...]
</project>

Deploy additional jar file with Maven?

I have an artifact which is being built and deployed in a particular way (not as a jar file). In the course of deployment, a war file is built.
How can I configure the pom so that the artifact is also deployed as a jar file, to a different location?
Maven deploy means deploy artifact to a Maven Repository, not an application server.
Configure additional JAR artifact like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>make-a-jar</id>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Attach this new artifact to your project:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.directory}/${project.artifactId}-${project.version}.jar</file>
<!-- <file>${project.build.directory}/${project.build.finalName}.jar</file> - if finalName is defined -->
<type>jar</type>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
This blog post and its comments have the answer.
These three plugin configurations will allow you to build/install/deploy a jar version alongside the war.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<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>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>deploy-file</goal>
</goals>
<configuration>
<packaging>jar</packaging>
<generatePom>true</generatePom>
<url>${project.distributionManagement.repository.url}</url>
<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>
The "maven way" is to split out src/main/java into a separate module, and have the war file depend on that.
If you're absolutely resistant to that approach, you may be able to use a profile to alter the contents of the packaging element. I'm not sure if that's possible though.
Separating them is the right way to go. Forcing maven to produce a war and a jar in the same module is possible but will cause you problems down the road.
You should add the corresponding dependency of the artifact in the dependencies of the pom file.
Ex:
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>1.2.2</version>
<scope>compile</scope>
</dependency>
One way to solve this is to have the module build a jar and then use the assembly plugin to build a war file with the jar in WEB-INF/lib of that war. I would strongly recommend against this. You'd be better off having a jar project and a war project with a parent project building both modules.

Categories

Resources