Prevent maven-shade-plugin from exposing <system> dependency? - maven

Thanks to a separate thread, I realized my project's uber-jar had a bug. I create the uber-jar using maven-shade-plugin because I need the more advanced transformer capabilities to merge several ServiceLoader definitions.
So, the issue is that my project has a system-scope compile-time dependency on tools.jar, locating tools.jar on the local machine relative to the value of the java.home sysprop. The bug is that I didn't realize this dependency was:
leaking into the dependency-reduced pom.xml of the uber-jar, and
even worse, the pattern ${java.home} in the dependency definition was being resolved and hard-coded into the uber-jar's POM. This second part was particularly embarrassing as it reveals the JDK location of the build machine (see line 50 here).
To workaround this problem, I conditionally disabled the profile which created the dependency. All details below are in the <activation> section:
Before:
<profile>
<id>internal.tools-jar</id>
<activation>
<file>
<exists>${java.home}/../lib/tools.jar</exists>
</file>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
</profile>
After:
<profile>
<id>internal.tools-jar</id>
<activation>
<jdk>1.8</jdk>
<file>
<missing>src/main/java/systems/manifold/Dummy.java</missing> <!-- this file is only present in the uber-jar module, therefore exclude if present -->
</file>
</activation>
<dependencies>
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
</profile>
So, after all that, my question is: is this the best way to prevent this system-scoped dependency from leaking into the uber-jar's dependency-reduced pom.xml?
Thanks in advance,
Kyle
Updated 2018-02-16:
Unfortunately the following configuration in the shade plugin doesn't prevent the system-scope dependency from leaking:
<artifactSet>
<excludes>
<exclude>*</exclude>
</excludes>
</artifactSet>
to the shade plugin's configuration but the resolved system scope dependency still shows up in the POM. Puzzling!

Related

Ivy not downloading secondary dependency on Jar

We are having a bit of a transitive issue between Ivy and Maven:
We have a project called ivylib. Ivylib is an Ant/Ivy project that depends upon another project called mvnlib.
Mvnlib depends upon another project called jersey-client. This in turn depends upon jersey-core. Both of these are part of the com.sun.jersey groupId.
In the Mvnlib pom.xml file, we have the following dependency:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.19</version>
Looking at the jersey-client project, I see the following dependency in its pom.xml file:
<profiles>
....
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</profile>
</profiles>
In the ivy.xml file of the Ivylib project, we have the following dependency:
<dependency org="com.travelclick" name="ivylib"
rev="2.1" conf="compile->default"/>
All dependencies of Mvnlib are downloaded and included in Ivylib except for the secondary dependency of the jersey-core jar.
I suspect it has something to do with this dependency being inside a <profile> dependency rather than being listed in the file itself. I was wondering if there's anyway to get the core jar to download.
For now, we simply inlcude the jersey-core jar in our Mvnlib project, but I was wondering if there's another way.
Best thing to do is to check your cache location for your IVY resolve. Once I looked at the dependency list for jersey-client, I noticed that "jersey-core" is not one of them. See below the ivy-1.19.xml file generated from the POM:
<dependencies>
<dependency org="junit" name="junit" rev="4.8.2" force="true" conf="test->runtime(*),master(*)"/>
<dependency org="com.sun.net.httpserver" name="http" rev="20070405" force="true" conf="test->runtime(*),master(*)"/>
<dependency org="org.osgi" name="osgi_R4_core" rev="1.0" force="true" conf="provided->compile(*),provided(*),runtime(*),master(*)"/>
...
</dependencies>
I would just add another dependency to my Ivy file. Copy/Paste the "jersey-cleint" dependency line and change to "jersey-core". It would be nice if the POM had "jersey-core" listed properly though wouldn't it? :-)

How to define a dependency scope in maven to include a library in compile, run, test, but not in assembly/packaging?

I'm building an Apache Spark application that can both be debugged locally and deployed to cluster. To do this, I have to define its dependency to spark-core (a Java/scala library) to fulfil the following requirement.
Included in in compile (other wise the compilation fails)
Included in run and test (for local debugging and unit test)
Excluded in assembly (for deployment to a cluster with provided spark-core, this decrease jar size by 70M, I'm using maven-shade plugin to generate the all-inclusive jar as there are some jar hell issues that cannot be resolved using maven-assembly)
Unfortunately it looks like custom scope wasn't natively supported by maven. Is there a way to enable it using some plugins?
We do exactly that on our maven build: exclude the Spark assembly from being included in the job assembly. We add an exclusion rule to the maven-shade plugin configuration.
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>jar-with-dependencies</shadedClassifierName>
<artifactSet>
<excludes>
<exclude>org.apache.spark:spark-assembly</exclude>
</excludes>
</artifactSet>
...
</configuration>
You can use the scope attribute (provided) for dependency.
This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. This scope is only available on the compilation and test classpath, and is not transitive.
Ref : http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope
eg:
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-b</artifactId>
<version>1.0</version>
<type>bar</type>
<scope>provided</scope>
</dependency>
You should create 2 profiles. 1 for your idea with spark at compile scope (default), the other used during your build (with provided scope).
<profiles>
<profile>
<id>default-without-spark</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>dev</id>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
You'll get what you want without the disadvantage of #maasg solution (all spark transitive dependencies added to your final jar)

Using "provided" classpath in tomcat7-maven-plugin goals

I have some dependencies in my webapp that I've marked as provided because I expect them to be provided by an appserver (maybe a production environment provides these dependencies at the specified versions). How do I simulate that when I'm running tests or in development on my localhost using for example the tomcat7-maven-plugin goals like run?
I can't see any way to do it without manually copying jars around. I can see how to use the test classpath - is there something wrong with what I'm trying to do?
OK, I've found a way of getting this to work - it's reasonable but there's a duplication of dependency information and a magic profile... I feel that the tomcat7-maven-plugin should provide a means of making provided dependencies available in the container when running.
Add a profile that is activated when the tomcat plugin runs, and add the dependencies that have provided scope with compile scope to that profile, eg.
... in project pom ...
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>my-provided-artifact</artifactId>
<version>1.2.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
<profiles>
<profile>
<!-- profile activated as cli param when tomcat7 plugin runs -->
<id>tomcat</id>
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>my-provided-artifact</artifactId>
<version>1.2.3</version>
<scope>compile</scope>
</dependency>
</dependencies>
</profile>
</profiles>
I use, for example, this:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
<path>/myApp</path>
</configuration>
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
</dependencies>
</plugin>
and then also include the dependency again later with provided.

Why does maven not copy the properties files during the build process?

Nothing I've found has been able to help me solve this one specific case. I recently switched from a plain old java web app project (which was working) to a maven web project. I get the following runtime exception:
java.util.MissingResourceException: Can't find bundle for base name com.myapp.config, locale en
I am using Netbeans to create a JSF 2.0, Spring, and Hibernate web app. I have the following directory structure:
src\main\java\com\myapp Contains config.properties
src\main\resources Empty
target\myapp\WEB-INF\classes\com\myapp Contains compiled class files without config.properties
src\main\java\com\myapp Contains config.properties
Inspection of the WAR file in the target folder does not show any sign of the properties file so it's as if the Maven build plug-in is not copying over properties files. I know there is a tag you can place inside the pom but it didn't work for me. The link below mentions that the resources folder (empty for me) has its contents included during the build but if that is the case, how do you do it from Netbeans? I just want the properties file to be packaged with my war so it is accessible when it is deployed to the server.
http://maven.apache.org/plugins/maven-war-plugin/examples/adding-filtering-webresources.html
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myapp</groupId>
<artifactId>myapp</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>myapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>java.net</id>
<name>Repository hosting the Java EE 6 artifacts</name>
<url>http://download.java.net/maven/2</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-hibernate3</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>net.authorize</groupId>
<artifactId>java-anet-sdk</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.15</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
<finalName>${artifactId}</finalName>
</build>
<profiles>
<profile>
<id>endorsed</id>
<activation>
<property>
<name>sun.boot.class.path</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- javaee6 contains upgrades of APIs contained within the JDK itself.
As such these need to be placed on the bootclasspath, rather than classpath of the
compiler.
If you don't make use of these new updated API, you can delete the profile.
On non-SUN jdk, you will need to create a similar profile for your jdk, with the similar property as sun.boot.class.path in Sun's JDK.-->
<compilerArguments>
<bootclasspath>${settings.localRepository}/javax/javaee-endorsed-api/6.0/javaee-endorsed-api-6.0.jar${path.separator}${sun.boot.class.path}</bootclasspath>
</compilerArguments>
</configuration>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>6.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<properties>
<netbeans.hint.deploy.server>gfv3ee6</netbeans.hint.deploy.server>
</properties>
Maven doesn't copy resources from the java source tree by default, but you can get it do that by adding this to your pom.xml:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<excludes><exclude>**/*.java</exclude></excludes>
</resource>
</resources>
</build>
Make sure you exclude the java source files.
From https://rogerkeays.com/how-to-change-mavens-default-resource-folder
What is your project's build path configured to be in Netbeans? You might try changing it to src/main/webapp/WEB-INF/classes. This way class files compiled from your src/main/java folder and any resources you have under src/main/resources should get included in the generated WAR. You would then be able to access your config.properties file if you place it under the src/main/resources folder.
You might also review any includes sections in your pom.xml and ensure you're not accidentally excluding something (if you explicitly include some things, you're likely implicitly excluding everything else).
By default maven will include all files under resources folder. If your properties files are not in the resource folder, then you need to include the following in the pom.xml file under the build section.
<build>
/* other tags like <plugins> goes here */
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
/* other tags like <plugins> goes here */
</build>
Try putting your config.properties under src\main\resources\com\myapp. I was able to test this on a local project. I'm running Maven 3.0.2.
Created a mvn sample project with the webapp archetype:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-webapp -DarchetypeArtifactId=maven-archetype-webapp
Created a directory at src/main/resources/com/foo and put a foo.properties file under it.
Ran a build:
mvn clean install
Then, when looking in the resulting target directory, the foo.properties file appears:
ls -al target/my-webapp/WEB-INF/classes/com/foo/
-rw-r--r-- 1 sblaes staff 4 Apr 2 22:09 foo.properties
You might try those steps on your machine. If that works, then start trying to simplify your POM above by removing things from it to see if it starts working. Trial and error is no fun, but I just don't see anything above that should be breaking it.
Huge gotcha for this:
when your resources are in "test/resources" (e.g. .properties files for tests)
maven doesn't copy them to target, so they're not in the classpath
Check whether your "packaging" is set to "pom" in the pom.xml:
<packaging>pom</packaging>
Fix is:
change your packaging to "jar" or "war" instead

maven profile question

I am new to Maven and I have very basic question. I have one J2EE app(EAR). When I build this app I want to ignore some dependency in lib folder of my war as this jars will be provided by my server like jboss(all hibernate stuff). But when I run this war project inside embedded jetty server then I need it to be inside my lib folder. I heard about the maven profile which can be used for similar purpose. Can somebody give me an example or some detail about it or is there some other way to achieve this task. I have an EAR which contains ejb module(jar) and web module(war).
Thanks
Specify your library in a profile. Set <scope>provided</scope> for your library in a jboss profile. E.g.:
<profiles>
<profile>
<id>jboss</id>
…
<dependencies>
<dependency>
<groupid>...</groupid>
<artifactid>...</artifactid>
<version>...</version>
<scope>provided</scope>
</dependency>
</dependencies>
…
</profile>
<profile>
<id>jetty</id>
…
<dependencies>
<dependency>
<groupid>...</groupid>
<artifactid>...</artifactid>
<version>...</version>
</dependency>
</dependencies>
…
</profile>
</profiles>

Resources