This example is a hypothetical scenario based on a real-world problem. Consider a block taken from a pom.xml file as given below. In this scenario, I want to exclude artifact-b dependency from artifact-a and reimport it with a different version. Obviously, excluded artifact-b version is not equal to ${product2.version}.
<dependency>
<groupId>com.company.product1</groupId>
<artifactId>artifact-a</artifactId>
<version>${product1.version}</version>
<exclusions>
<exclusion>
<groupId>com.company.product2</groupId>
<artifactId>artifact-b</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.company.product2</groupId>
<artifactId>artifact-b</artifactId>
<version>${product2.version}</version>
</dependency>
Thus, when mvn install command is executed for the final application, I will only have the artifact-b with ${product2.version} under the folder where the application stores the jars collected from its dependencies.
This is the expected behaviour for my module, but does it also mean that the methods in artifact-a jar will now make calls to the artifact-b jar with ${product2.version}? If so (which is what I expect since target repository does not include the excluded version), what happens if the called method in artifact-b is different in the new version? Does it only check method signatures or are there other factors that can cause compilation/runtime errors?
First of all, as khmarbaise said, the exclusion is unnecessary.
Secondly, increasing the library version can cause all kinds of problems. The compile time problems are mainly method signatures or missing classes, but at runtime, anything can happen, depending on the changes made in the source code of that library.
You can just hope that the maintainers of the library tried to keep everything backwards compatible.
Related
I'm using enforcer plugin of Maven and I see a behavior that I dont quite understand and it's dangerous.
Let's say that I have a conflict since dependency A has bla.jar:1.0 and is in conflict with my dependnecy B which has bla.jar:2.0
Then to fix the conflict, I make an exclude of bla.jar:1.0 from A
<dependency>
<groupId>com.foo</groupId>
<artifactId>A</artifactId>
<version>a.version.bla</version>
<exclusions>
<exclusion>
<groupId>com.omg</groupId>
<artifactId>bla</artifactId>
</exclusion>
</exclusions>
</dependency>
expecting the application will get the bla.jar:2.0 fron classpath. But then I see when I run some unit test that the java proce3ss cannot find bla.jar ion the classpath at all and is giving me ClassNotFound in runtime.
Any idea what's wrong here?
I have in my pom defined from top to bottom B and then A
Please note that exclusions are not the best way to resolve dependency version conflicts.
The best approach is to use <dependencyManagement>. It allows you to set a version that replaces all transitive versions of that dependency.
In your case, I would first change the exclusion to <dependencyManagement>. Then I would proceed in the following way:
Check mvn dependency:list which version of the dependency is on the classpath. It should be the one specified in <dependencyManagement> unless there is no version of that dependency in your dependency tree. If you find more than one, then probably the groupId changed at some point. Then you need exclusions.
Check the scope of the dependency and verify that it is indeed compile.
Then open the dependency jar and see whether this jar really contains the class for which you get ClassNotFound. Often classes change from version to version.
It's kind of hard to explain my problem with words, so I took a pic, which shows exactly what my problem is:
As you can see, I have 3 relevant modules, a global, genui and web.
genui depends on global and web depends on genui.
Directly, web doesn't depend on global, but through genui it obviously does.
As you can see, if I take a look at the hibernate version of web->genui->global, it is 5.2.14, but if I look at it from genui->global, it's 5.3.0.
There is not, and there have never been any versions of either of these modules other than 1.0-SNAPSHOT.
I tried cleaning, deleting the files from .m2/repository, and even tried purging the local repo completely, nothing worked. I have no idea where maven gets the 5.2.14 number, I don't remember ever putting that in. The version is received from a property in parent module.
I figured out that the problem is caused by the pom.xml of web module, more specifically this part:
<dependencyManagement>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
...
</dependencyManagement>
You probably need to exclude that repeated dependency from one of the spring ones... verify all your dependencies and look which are the ones that include hibernate. Check that you also have different versions for servlet-api.
Newly exposed to Maven, I can understand the use case of the <exclusion> tag, but not sure why it wouldn't cause compile error:
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>2.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</exclusion>
</exclusions>
</dependency>
...
Is this only possible only when you have another direct dependency on maven-core? otherwise, compile error should happen. (assuming maven-core is used somewhere in maven-embedder)
You are excluding that artifact from that specific dependency, but it could be getting pulled in from another dependency. Using something mvn dependency:tree -Dverbose -Dincludes=maven-core should show you what else is introducing the dependency. The Maven Enforcer plugin can also help exclude transitive dependencies.
There are different possibilities:
As Carl said: Check your dependency:tree if the dependency is not pulled in from somewhere else.
It is possible that maven-core is not used at all, even if maven-embedder indeed uses it: Assume e.g. that maven-embedder has two classes A and B. You only use A, but maven-core is only used by B. Then (if A and B do not use each other), your project might be entirely independent of maven-core. (A side remark: some jars should logically be two separate jars, but where merged together by whatever reason - in our example, one should think about putting A and B in separate artifacts).
It is possible that transitive dependencies are not necessary at compile time, but are used at runtime.
The error will not thrown in the compile time, It will thrown in run time if you use any feature depends on maven-core
I am trying to build protege-server (https://github.com/protegeproject/org.protege.owl.server) from source. I downloaded the source code. Using "mvm -X package" yields the following error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.
0:compile (default-compile) on project org.protege.owl.server: Compilation failu
re
[ERROR] /c:/Users/user/Programs/webprotege/org.protege.owl.server-master/src/mai
n/java/org/protege/owl/server/connect/local/OSGiLocalTransport.java:[11,32] type
org.osgi.framework.ServiceRegistration does not take parameters
Based on a previous question, an OSGI blogpost explains that the problem was fixed in a later (4.3.1) version of the library.
I tried to refer a newer version of this library in the POM.xml file:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>core</artifactId>
<version>6.0.0</version>
<scope>system</scope>
<systemPath>/c:/Users/user/Downloads/osgi.core-6.0.0.jar</systemPath>
</dependency>
and even downloaded the newer version to specifically target it.
The error still occurs. Is there any way to solve it?
EDIT:
Attempting the solution suggested by #Balazs Zsoldos didn't help and I received the same error message. I noted an import of this package (org.osgi.framework) referring version 1:
<Bundle-Activator>org.protege.owl.server.Activator</Bundle-Activator>
<Bundle-SymbolicName>org.protege.owl.server</Bundle-SymbolicName>
<Bundle-Vendor>The Protege Development Team</Bundle-Vendor>
<Embed-Dependency>antlr, antlr-runtime, stringtemplate</Embed-Dependency>
<Export-Package>org.protege.owl.server*;version=2.0.6-SNAPSHOT</Export-Package>
<Import-Package>!org.antlr.stringtemplate,
!org.apache.commons.cli,
org.osgi.framework;version="1",
*</Import-Package>
An attempt to remove this line did not help either, as it appears in another dependency down stream. I could not find out how to override the downstream import-package instruction.
The effective pom.xml, as generated by eclipse, is attached as a link: https://docs.google.com/document/d/1eHFalUHVZ45ejLes_eqaXLw6ttjcTryphbGr_CKbhRk/edit?usp=sharing
The issue is that older versions of osgi.core are still on the classpath of the as they are imported with different group and artifact ids. Drag and drop the pom.xml to your eclipse and see the Dependency Hierarchy tab of the pom editor to get more information.
The following two are imported by dependencies:
org.osgi:org.osgi.core (by org.apache.felix.log)
org.apache.felix:org.osgi.core (by owlapi distribution)
To solve the problem, you should add the following dependency:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
And as this does not override the org.apache.felix:org.osgi.core dependency, exclude that one:
<dependency>
<groupId>net.sourceforge.owlapi</groupId>
<artifactId>owlapi-distribution</artifactId>
<version>3.4.5</version>
<exclusions>
<exclusion>
<groupId>org.apache.felix</groupId>
<artifactId>org.osgi.core</artifactId>
</exclusion>
</exclusions>
</dependency>
(and remove the dependency with system scope as you do not need it and its artifactId is different from the standard anyway).
Edit
Just realized that the old osgi.core package is also inside org.apache.felix:org.apache.felix.framework that is pulled transitively by ProtegeLauncher via org.apache.felix:org.apache.felix.main:4.0.3. This means that you should either
Increment the version of org.apache.felix:org.apache.felix.main to the newest (or to one that at least implements osgi 4.3). In this case you do not need osgi.core at all
exclude org.apache.felix:org.apache.felix.main from edu.stanford.protege:ProtegeLauncher (and keep version 4.3.1 or higher of osgi.core)
I tried the second one and another issue comes that surfire plugin cannot be downloaded from maven central (or something similar, you will see).
Notes
The developer of this protege library was clearly not familiar how maven dependency management works and what should have been imported as a dependency. The project imports an OSGi runtime environment transitively that should never happen. For compilation only API should be imported and if the target runtime surely contains that API, it should be imported with provided scope. I would recommend to
not use this library or
clean it out (at least the maven dependency part) and send a pull request so the library can have an acceptable quality
Say I have this dependency in my pom.xml file:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
When I do a
clean install
all the javaee-api-6.0.jar will be included in the war file under WEB-INF\lib folder.
Is it possible that instead of including the whole jar, only classes that I use and their dependencies are included?
If you're deploying into a Java EE application server, that entire JAR is already provided by the application server, and can be omitted from the WAR file. You can accomplish this by putting it into the provided scope:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
That makes that dependency available for compilation and test execution, but will not package it into the WAR.
In contrast, trying to determine which individual classes you need so you can only include their class files is an ultimately pointless endeveor. The JVM only loads classes when they are used - that is, unused classes are not loaded.
It is generally impossible to identify the used classes at compile time due to reflection. For instance, consider:
System.console().printf("Please specify the implementation class to use");
String className = System.console().readLine();
FooService service = (FooService) Class.forName(className).newInstance();
service.work();
You can get the JVM to log which classes are loaded, but different executions can use different classes ...
It's not a viable option - at least not in maven, although You know which classes You are using, but You don't know what are the dependencies for each class that You imported - so it might be impossible satisfy it's requirements. This is why we are using tools like maven - to ease the process importing a library.
Read some more about reduce size of built project and see what are Your options there
Except for UberJAR, Your biggest chance (IMHO) would be to identify libraries that are provided by the container, and use provided scope for them.
You also could integrate 3rd party tools like ProGuard
You could use exclusions.
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</dependency>
But I don't think you could exclude at class-levels. This only excludes dependencies useful when there are conflicting dependencies in your project.
It is really not a viable option in my opinion ,as its almost impossible to know internals what all classes are required at runtime until and unless you are seeing the,implementation of all the,3rd part apis that you are using.
I also think the whole idea behind the maven is to ease the development and build process so that you won't have to do any effort in identifying the artifacts that are required at runtime or compile time. Maven will automatically figure out that for you.