How to prevent lombok from being packaged into Spring Boot jar? - spring-boot

If you visit official Lombok Maven guide, you will see that it's scope should be provided.
When I create a new project from scratch using start.spring.io and add Lombok, it gets only <optional>true</optional> in resulting pom.
Then I package Spring boot app like this and out of curiosity decide to see what gets packaged inside of jar:
mvn clean package -DskipTests=true && unzip target/demo-0.0.1-SNAPSHOT.jar -d exploded
Lombok is packaged, does not matter if I set scope to provided or only have optional=true.
How to prevent Lombok from being included to Spring Boot jar?
My last thought was to try another goal which comes with Spring Boot plugin:
mvn spring-boot:repackage
But unfortunately it produces the following error:
repackage failed: Source file must be provided

Lombok will only be available in a seperate folder lib-provided instead of the usual lib folder if you are packaging war file.
In order to exclude lombok completely from the final jar/war file, you have to do this:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.1.RELEASE</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
Please refer to Exclude a dependancy section of Spring Boot Maven Plugin https://docs.spring.io/spring-boot/docs/2.3.x/maven-plugin/reference/html/#repackage-example-exclude-dependency for more details.

Looks like it's a transitive dependency of one of your dependency. If you confirm it, you know what to do.

Related

Should lombok have a provided scope in spring-boot-dependencies?

Based on the Project Lombok documentation the lombok dependency should be a provided scope (https://projectlombok.org/setup/maven). Should that scope be defined within the <dependencyManagement> of the spring-boot-dependencies pom.xml?
The spring-boot-dependencies pom.xml is just a description of libraries versions that work well together. They are defined in dependencyManagement.
If you have a Spring Boot application and you want to use lombok, you have to explicitly say that in a 'dependency' section in your project pom, but you can exclude the version as this is already defined in spring-boot-dependencies. And you can also declare that the dependency is 'provided' or better yet 'optional'.
<scope>provided</scope> means that the library is needed for compilation and
test classpath, however it is provided by some sort of container
<optional>true</optional> means that a library is needed for compilation, but it is not necessary at runtime
Edit: it seems that Spring Boot maven plugin always packages your dependencies, even if you declare them as optional or provided, at least when you package your app as a jar, I didn't test this with a war.
The reason is that a jar contains an embedded servlet container and Spring Boot packaging needs to provide this container with the provided libraries, makes sense! (thank you #Peter Wippermann).
I guess the conclusion is that it doesn't really matter if you provide a scope for lombok when packaging a jar with Spring Boot maven plugin, as the library will always get packaged, unless you want to use the scope for semantic reasons...
For lombok not be included in the package you just need to configure the Spring Boot maven plugin with the exclusion configuration.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>

How create JAR from upstream Web App as Dependency for downstream Web App

I have (amongst other modules) a NetBeans8.1 Maven web app project CoreWeb that (with other module dependencies resolved) runs standalone fine, and is supposed to under any circumstances at least build its default WAR and run standalone.
I have a 2nd NetBeans8.1 Maven web app project SpecWeb that depends on CoreWeb; it is to reuse both JSF managed beans from CoreWeb and also leverage XHTML composite component JSF resources from CoreWeb.
But on building SpecWeb it does not resolve the CoreWeb dependency, because CoreWeb does not (by default) also build a JAR:
Failed to execute goal on project SpecWeb: Could not resolve dependencies
for project com.example.multi:SpecWeb:war:1.0-SNAPSHOT: Could not find
artifact com.example.multi:CoreWeb:jar:1.0-SNAPSHOT -> [Help 1]
(I had this problem with an Ant version of my project with multiple modules and solved it using a small piece of Ant task script to build the extra JAR from CoreWeb for use in SpecWeb, worked perfectly.)
Q: How can I generate a JAR (as well as the WAR) from CoreWeb for use in SpecWeb ?
Under Project > Actions: Build Project I've tried adding various additional goals to install for the Build Project action (using the NetBeans prompting feature).
Using install jar:jar did not work. It indeed created a JAR (as well as the WAR), but that JAR only included the managed bean classes, not the resources, and on build from the "downstream" SpecWeb it still failed to find it:
$ jar tf target/CoreWeb-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
com/
com/example/
com/example/multi/
.. Java managed bean classes ..
META-INF/maven/
META-INF/maven/com.example.multi/
META-INF/maven/com.example.multi/CoreWeb/
META-INF/maven/com.example.multi/CoreWeb/pom.xml
META-INF/maven/com.example.multi/CoreWeb/pom.properties
I get the same JAR structure built (without JSF XHTML composite component web resources) if I manually change the packaging in the pom.xml to jar:
<groupId>com.example.multi</groupId>
<artifactId>CoreWeb</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--
<packaging>war</packaging>
-->
EDIT: Overlays also not working (in NetBeans)
I have also tried using Overlays, but it's not working as expected. As soon
as I turn the dependency into type war NetBeans can't resolve classes from CoreWeb imported into classes from SpecWeb, and it won't even compile (and the class files have NetBeans Java warnings because of the failed import):
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>CoreWeb</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
Also, as soon I enter <type>war</type> the CoreWeb project vanishes from the list under the project navigator node SpecWeb > Dependencies.
Also tried Warpath plugin
NetBeans still does not see classes in CoreWeb imported into classes in SpecWeb, so SpecWeb won't compile.
After lot of research (mostly on the main Maven site) and trial and error, I have found a robust solution compatible with NetBeans.
A general description of the strategy. The trick is to create an additional "client" JAR version of the CoreWeb project for temporary use by the SpecWeb project (enabling NetBeans editor to "see" the classes of CoreWeb), and declare it as an additional dependency of only 'provided' scope (so it does not make it into the final SpecWeb WAR). Then also use overlays to combine both the composite components of CoreWeb and the managed beans of CoreWeb with the SpecWeb project into the final WAR, being careful to exclude the index.xhtml from CoreWeb.
Steps
In the pom.xml from CoreWeb include this:
<!-- Also make JAR form using classifier during package phase -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>client</classifier>
</configuration>
</execution>
</executions>
</plugin>
On build this will create:
$ ls -1 CoreWeb/target/CoreW*
CoreWeb/target/CoreWeb-1.0-SNAPSHOT-client.jar
CoreWeb/target/CoreWeb-1.0-SNAPSHOT.war
CoreWeb/target/CoreWeb-1.0-SNAPSHOT:
META-INF/
WEB-INF/
index.xhtml
resources/
(Note NetBeans can run CoreWeb independently directly over CoreWeb/target/CoreWeb-1.0-SNAPSHOT with its own index.xhtml.)
SpecWeb can then use CoreWeb-1.0-SNAPSHOT-client.jar thus in its pom.xml:
<dependencies>
...
<!-- This is needed for the editor and for the compilation only (not at runtime) -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>CoreWeb</artifactId>
<version>${project.version}</version>
<type>jar</type>
<classifier>client</classifier>
<scope>provided</scope> <!-- To prevent being written even to target folder -->
</dependency>
<!-- This is needed for the overlay -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>CoreWeb</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>runtime</scope>
</dependency>
Note how there are 2 separate types of dependency of SpecWeb on CoreWeb:
A dependency on the CoreWeb-1.0-SNAPSHOT-client.jar using the classifier client and with scope runtime (so it won't be included under /target at all). This will appear in the NetBeans project window under a special node Non-Path Dependencies, with a clear indication that it is for [runtime] only.
A dependency on the CoreWeb-1.0-SNAPSHOT.war for the overlay to use. This will appear in the NetBeans project window under the node Dependencies.
Then also in the pom.xml for SpecWeb specify the overlays (excluding the CoreWeb's index.html) and packaging (to be sure also excluding any "client" jars):
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<packagingExcludes>WEB-INF/lib/*-client.jar</packagingExcludes>
<overlays>
<overlay>
<groupId>${project.groupId}</groupId>
<artifactId>CoreWeb</artifactId>
<excludes>
<exclude>index.xhtml</exclude>
</excludes>
</overlay>
<overlay>
<!-- empty groupId/artifactId represents the current build -->
</overlay>
</overlays>
</configuration>
</plugin>
The SpecWeb project behaves correctly in the NetBeans editor (classes from CoreWeb are seen), compiles ok, and can be run over SpecWeb/target/SpecWeb-1.0-SNAPSHOT with its own index.html, with access to both the JSF managed beans and JSF composite components of CoreWeb.
CAVEAT: NetBeans XHTML editor will complain when editing JSF pages from SpecWeb that the composite components provided by CoreWeb don't exist because of this known NetBeans Bug 257684 - JSF 2 composite components in JAR are not recognized , but the project will still run ok.

Maven Jetty plugin configure repository

We use jetty plugin for local deployment of our application. Recently i added a repository in the pom and added dependencies both in the plugin section and the dependencies section outside as well, when i build the war and deploy it on standalone app server everything works ok, however the same application when i try to run through the jetty application it throws me error for that particular dependency.
Is there any way that we can configure the external repositories to be used by the plugins in order to resolve the dependencies.
Thanks,
- Vaibhav
If I understand your issue right, you can add a dependency to the jetty-maven-plugin itself and have it available to your application. Something like this:
<plugin>
<groupId>org.eclipse.jetty.maven</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.3.14.v20161028</version>
<dependencies>
<dependency>
<groupId>foo</groupId>
<artifactId>bar</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
</plugin>

Add external library .jar to Spring boot .jar internal /lib

I have an external .jar that cannot be imported from public repositories using pom.xml, it's sqljdbc41.jar.
I can run the project locally from my IDE, and everything will work. I referenced the library after downloading it like so:
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc41</artifactId>
<version>4.1</version>
<scope>system</scope>
<systemPath>${basedir}/lib/sqljdbc41.jar</systemPath>
</dependency>
When I run mvn clean package to create my .jar file and try to run the created .jar, a mistake will pop up, which mentions the SQL Server references are not valid. I then extracted my .jar file and true enough, everything that is referenced in the pom.xml file properly gets downloaded and added, however, my SQL Server does not.
I can, in a very hacky way* just manually add the sqljdbc41.jar to my /lib folder after it's been compiled as a .jar, and it'll work, however that seems highly unoptimal. What would be a better approach?
*Opening the .jar file with Winrar, going to the /lib folder, manually selecting my sqljdbc41.jar file, then make sure to select the No Compression option bottom left where Winrar gives you compression options, in case you find this by Google and no one answered.
you can set 'includeSystemScope' to true.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
You could install the sqljdbc41.jar in your local repository :
mvn install:install-file -Dfile=path/to/sqljdbc41.jar -DgroupId=com.microsoft.sqlserver -DartifactId=sqljdbc41 -Dversion=4.1 -Dpackaging=jar
And then declare the dependency as a standard dependency :
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc41</artifactId>
<version>4.1</version>
</dependency>
If you use a remote artifact repository (nexus, archiva...) you also need to deploy the artifact on this repository. You can find more here : https://maven.apache.org/guides/mini/guide-3rd-party-jars-remote.html
Another way, you can put it into the resources folder, such as resources/lib/xxx.jar, then config the pom.xml like this:
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc41</artifactId>
<version>4.1</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/sqljdbc41.jar</systemPath>
</dependency>
In Spring Boot: I also faced similar issue and below code helped me.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.7.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
It works for me:
project {root folder}/libs/ojdbc-11.2.0.3.jar
pom.xml:
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>11.2.0.3</version>
<scope>system</scope>
<systemPath>${basedir}/libs/ojdbc-11.2.0.3.jar</systemPath>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
In my case, the fault was providing a version number without "dot" in tag:
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<scope>system</scope>
<version>1</version>
<systemPath>${basedir}/src/main/resources/lib/tools.jar</systemPath>
</dependency>
This one works:
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<scope>system</scope>
<version>1.8</version>
<systemPath>${basedir}/src/main/resources/lib/tools.jar</systemPath>
</dependency>
When Spring-Boot projects are used with maven or gradle plugins they packaged the applicaiton by default as executable jars.
These executable jars cannot be used as dependency in any another Spring-Boot project because the executable jar add classes in BOOT-INF/classes folder. This means that they cannot be found when the executable jar is used as a dependency because the dependency jar will also have the same class path structure as shown below.
If we want to use project-A as a maven dependency in project-B then we must have two artifacts. To produce the two artifacts, one that can be used as a dependency and one that is executable, a classifier must be specified. This classifier is applied to the name of the executable archive, leaving the default archive for use as a dependency.
To configure a classifier of exec in Maven, you can use the following configuration:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
So the MAJIC WORD here is <classifier>exec</classifier> this will create a jar structure as below and then it could easily be conusmed by spring-boot project as maven dependency jar on class path.
The above plugin need to be add in project-A pom that is going to be used as dependency in project-B. Same is explained in spring documentation section 16.5. as well.
In order to work through the local repository, the target .jar file that we will work with must be in the s2 folder. Several methods can be used for this:
The file can be taken manually and put in the relevant place (not
preferred). The same process can be done by installing it via the
console.
Relevant Remote URL is written in the .pom file dependencies and
automatically places it in the s2 folder when Intellij is refreshed
(validate) in the IDE used.
The same process can be done by addressing the .pom file dependencies via the centeral repository.
Attention: ComponentScan should not be forgotten for the related jar work on SpringBot.

Maven WAR dependency

I am writing a project for acceptance testing and for various reasons this is dependent on another project which is packaged as a WAR. I have managed to unpack the WAR using the maven-dependency-plugin, but I cannot get my project to include the unpacked WEB-INF/lib/*.jar and WEB-INF/classes/* to be included on the classpath so the build fails. Is there a way to include these files into the classpath, or is there a better way of depending on a WAR?
Many thanks.
There's another option since maven-war-plugin 2.1-alpha-2. In your WAR project:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
This creates a classes artifact which you can use in the acceptance tests project with:
<dependency>
<groupId>your-group-id</groupId>
<artifactId>your-artifact-id</artifactId>
<version>your-version</version>
<classifier>classes</classifier>
</dependency>
Indeed, by design, Maven doesn't resolve transitive dependencies of a war declared as dependency of a project. There is actually an issue about that, MNG-1991, but it won't be solved in Maven 2.x and I'm not sure that I don't know if overlays allow to workaround this issue. My understanding of the suggested solution is to duplicate the dependencies, for example in a project of type pom.
(EDIT: After some more digging, I found something interesting in this thread that I'm quoting below:
I have been helping out with the development of the AppFuse project over
the last month where we make heavy use of the war overlay feature in the
Maven war plugin. It is a really nifty feature!
To get max power with war overlays I have developed the Warpath plugin
that allows projects to use war artifacts as fully fledged dependencies.
In brief:
1) The contents of the /WEB-INF/classes directory in the war dependency
artifacts can be included in the project's classpath for normal compile,
etc tasks.
2) Transitive dependencies from the war dependency artifacts become
available for use by other plugins, e.g. compile and ear - so no more
having to include all the dependencies when creating skinny wars!
The plugin has now been actively used in the AppFuse project for the
last few months, and I feel it is at a point where it is both usable and
stable.
Would the war plugin team be interested in including the warpath
functionality inside the war plugin? It would seem to be the most
natural place to host it.
So, I don't have any experience with it, but the maven warpath plugin actually looks nice and simple and is available in the central repo. To use it,include the following plugin configuration element in your pom.xml file:
[...]
<build>
<plugins>
<plugin>
<groupId>org.appfuse</groupId>
<artifactId>maven-warpath-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>add-classes</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
[...]
And add the war dependencies you want included in the classpath as warpath type dependencies:
[...]
<dependencies>
<dependency>
<groupId>org.appfuse</groupId>
<artifactId>appfuse-web</artifactId>
<version>2.0</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.appfuse</groupId>
<artifactId>appfuse-web</artifactId>
<version>2.0</version>
<type>warpath</type>
</dependency>
</dependencies>
[...]
Both the war and warpath dependency types are needed: the war type is used by the Maven war plugin to do the war overlay, the warpath type is used by the Warpath plugin to determine the correct list of artifacts for inclusion in the project classpath.
I'd give it a try.)
Use overlays. First, your test project need to have also packaging war.
Declare dependency of war project you want to test:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>your-project-arftifactId</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>test</scope>
</dependency>
then configure maven-war-plugin overlay:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webresources</directory>
<filtering>true</filtering>
</resource>
</webResources>
<overlays>
<overlay/>
<overlay>
<groupId>your.group</groupId>
<artifactId>your-project-artifactId</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
In the above example in test project I overwrite webresources configuration files (like conxtext etc.).
EDIT: This solution wasn't tested with Maven 3.
Good point, Justin. That got me actually solving my problem, namely: including a war into an assembly AND including all its transitive dependencies.
I could not duplicate the war-dependency as 'jar' as you suggested since the assembly plugin would not find a jar referenced by that groupId/artefactId, but
duplicating the war-dependency as type pom
works!
The war and its transitive dependencies are not included in the assembly.
To exclude the (now also appearing) pom file I had to add an exclude element like this:
<excludes>
<exclude>*:pom</exclude>
</excludes>
into my assembly.xml file.
I think this could also be a workaround for the original question of this thread.
If you list the dependency on the war project as a jar dependency it seems to pickup the required jars/resources. I'm using Maven 2.2 + m2eclipse.

Resources