Maven is a bit over my head sometimes... I have created a library which has an optional dependency on slf4j and on log4j. By optional, I mean:
My library needs those logging frameworks at compile time
My library doesn't need them at runtime, but if it "discovers" them, it will use them
Currently, I have marked that dependency as "optional" and "provided":
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
</dependency>
But some of my users have reported issues, because they don't need log4 / slf4j. Is my dependency correct? Unfortunately, I find the official documentation a bit too abstract to understand this problem.
Did you check this documentation. It describes your use case very good. Marking dependencies as optional will not resolve them as transitive dependencies in the application which use your library (even if the scope is compile).
In difference to <scope>provided</scope> which is used for required dependencies which will be provided by the runtime environment an <optional>true</optional> dependency is not necessarily meant to be required (The idea is that some of the dependencies are only used for certain features in the project, and will not be needed if that feature isn't used.).
If a project which uses your library will use any functionallity provided by the optional dependencies the project has to declare these dependencies for their own.
As your configuration seems to be correct for me I do not know the reason what probles occur. Maybe your optional dependencies get resolved by other libraries in versions you do not expect. That of course might cause problems.
Related
I made custom dependency which uses spring 4.x version and I include it in a project which uses spring 3.x version. When a method from this dependency is called it uses classes from spring 3.x version not from 4.x. Is it possible to force this dependency to use spring 4.x whereas the project itself will use spring 3.x ?
I don't think that is possible due to the fact that, when finally project is running, the dependencies are resolved on the basis of group id and artifact id and not on their version. Which is why your application is using 3.x dependency as it is overriding the one mentioned in the parent project. Hope this helps.
Yes, you can if you separate your application (which you probably don't want to). Another approach: You might think about using another class loader within the same JVM. This, however, leads to a probably bigger bunch of problems, especially using Spring.
Dzone article about loading the same class from libs with different versions.
As pvpkiran noted, you want to exclude the spring v3 transitive dependency from your custom artifact. From the maven documentation - Optional Dependencies and Dependency Exclusions:
<project>
...
<dependencies>
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
The description for the "provided" scope of maven dependencies contains this note:
"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."
Question is if there is a xml snippet available (maybe an official one) that gives me the "provided" dependencies for a specific tomcat version.
The thing you are looking for is a Bill of Materials (BOM). This can be imported in the dependencyManagement section of your pom.xml by setting the type and the scope of the dependency to pom and import, respectively.
Unfortunately, Tomcat does not seem to provide an official BOM for its provided dependencies. There is an unofficial version on Github, but depending on what you want to use it for, this may not be the best solution. According to the docs at github, you can us it like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>fr.husta.tomcat</groupId>
<artifactId>tomcat-provided-spec-bom</artifactId>
<version>8.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
When you use a full-blown JavaEE-Server like JBoss EAP, official BOMs are provided, e. g. this one.
I noticed that the Spring Boot Sample Data Redis declares the following dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
See here for full POM: https://github.com/spring-projects/spring-boot/blob/v1.0.0.RC4/spring-boot-samples/spring-boot-sample-data-redis/pom.xml
I see no mention of the <artifactId>spring-boot-starter-redis</artifactId>
My question is: when do I use spring-boot-starter versus spring-boot-starter-xxx where xxx is the name of the project (here Redis)?
The answer to the specific question: spring-boot-starter is a baseline for the others, and for standalone (non-web) apps that don't use any other Spring components - it has basic support for Spring, Logging, and Testing, but nothing else (no webapp features, no database etc.). Since all the other starters depend on it, once you use another one you can remove the vanilla starter. EDIT: see here https://github.com/spring-projects/spring-boot/commit/77fd127e09963a844f8fb4e574e1f0d9d3424d4e.
Up to you on the redis starter, but I would use the starter if it exists, since it will typically cut down on the number of dependencies you need to declare. The redis one actually doesn't add a lot of value (hence it didn't exist until recently), but it probably ought to be used in the sample.
In pom.xml , we will provide compiletime and runtime as scope in the dependency ? whats the significance of that ? Please provide some applicable example for understanding this.
The following is taken from the maven documentation
compile
This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.
runtime
This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.
So for example if we have the following two dependencies in our POM:
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging-api</artifactId>
<version>1.1.3</version>
<scope>compile</scope> <!-- can be ommitted as it is the default -->
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
<scope>runtime</scope>
</dependency>
Then the classes from commons-logging-api would be on the classpath during compilation of my module, whereas classes from commons-logging would not be available - if by accident I had a direct reference to a class from commons-logging in one of my project's classes then the build would fail.
However during runtime or test compilation & execution the classes from commons-logging would be on the classpath so could be used (i.e. by classes from commons-logging-api, or directly in tests of the project).
Both compile and runtime dependencies are included transitively (under the same scope) by Maven when your project is referenced as a dependency in another project.
p.s. As mentioned by kostja there is also the provided scope
provided
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.
Basically the difference between provided and compile is that provided dependencies are not transitive.
Imagine you are deploying your application to a Java EE compliant server. The server provides all libraries implementing the Java EE standard, so you don't need to deploy them with your application.
During development, you will need the Java EE libraries with the compile time scope, since you need to compile the classes.
During the runtime however the dependencies are provided by the application server. Maven uses the 'provided' scope for such cases.
For example I have dependency:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.6</version>
</dependency>
Can I exclude one class, for example org/slf4j/Marker.class?
Try it with the shade plugin
Details on why use shade and basic usage
Excluding a single class in not possible. Within <dependency> tags you can define <exclusions/>. However, these are for entire dependencies.
The shade plugin should be handled with care. Generally, it's not good practice to be creating a jar containing all your dependencies in one place as it tends to lead to problems if you are to be using the produced artifact in another project as a dependency. For example, shading slf4j in your jar and then depending on your artifact in another project where you have another slf4j will bring you grief.
You could change those classes and define them in a different jar/module which should be included as a dependency before the jar that supplies the dependency where your class to be excluded resides (Marker.class).
Maven remembers the classpath ordering from version 2.0.9.