About exist two same name class in different jar? - java

In my current project I used Guava Cache to cache something with expiration, but when actually call this interface, it had below error
Caused by: java.lang.NoSuchMethodError: com.google.common.base.Platform.systemNanoTime()J
at com.google.common.base.Ticker$1.read(Ticker.java:64)
at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2225)
And the reason is that there are two Platform in the classpath
one in Gauva, one in google-collections:jar
And LocalCache of Guava uses Paltform of google-collections causes this error. I have some question about this, why not Class in the same jar has higher priority? why not randomly choose one, but always use Platform of google-collections?

Yes it is possible that two jars include the same class name. It happens when you import for example two jars of different versions of the same library.
If you import dependencies using maven for example when you import a library it can import dependencies of other libraries causing this kind of problem. In this case you need to explicit that when you import a library you need to exclude a secondary dependency. This is done with the exclusions tag.

You can force priority by specifying classpath order. Java look for class in jars in order that is pecifyed in classpath.
In case you using maven and ide to run you have no control over order of classpath, but you can exclude dependee module from classpath.
<dependency>
<groupId>some</groupId>
<artifactId>id1</artifactId>
<version>1.0.0.0</version>
<exclusions>
<exclusion>
<groupId>another</groupId>
<artifactId>id2</artifactId>
</exclusion>
</exclusions>
</dependency>
In this case id2 will not be in your classpath anymore.

class loading priority will depends on the ClassLoader, you can find lot of articles about java class loading. if you are trying to run a standalone java application probably class loading will be done by URLClassLoader.
in this case all urls(jar file locations or classes) given in the -cp or -classpath will be added as URLs to the URLClassLoader, then when application needs to load a class this URLClassLoader will iterate through its URLs and once it found the class it will be loaded.
so everything depends on the class path order.
NOTE: but in some context class loading is not simple as this. ex: jboss-module class loader.

Related

Resolving conflict between transitive dependencies

I have a pom.xml where i've got hadoop-core dependency as provided
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>${hadoop.version}</version>
<scope>provided</scope>
</dependency>
When I add cfg4j as compile time dependency
<dependency>
<groupId>org.cfg4j</groupId>
<artifactId>cfg4j-core</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.cfg4j</groupId>
<artifactId>cfg4j-consul</artifactId>
<version>4.4.0</version>
</dependency>
I've got an exception "java.lang.NoSuchMethodError: javax.ws.rs.core.Response.someMethod". I've investigated the problem and find out that the problem is from hadoop and cfg4j-consul. Hadoop core depends to jersey-core and cfg4j depends on cxf. Both declared javax.ws.rs as dependecy so the problem is that jersey has version 1.1 and cxf has 2.0.2. Hadoop dependency is provided, cause it's needed by Flink (framework) and it's in the lib folder. I can't just upgrade it or remove it, nor add it as compile time and exclude the lib. Even I was able to do it, I have no guarantees that hadoop will work as expected. I guess shading doesn't fix the problem cause it's not with cfg4j but one of the dependency of his dependency.
Is there way to resolve the conflict? Does gradle has it's onw ways to fix such issue?
Two approaches:
Shading: A bit more difficult as you say because this a transitive dependency, but I would have a look a Maven shade plugin and it would still be possible to declare the dependency directly if necessary.
Don't use the dependency and try to find some other library or solution for your problem.
Try the following steps, here is the source: https://reflectoring.io/nosuchmethod/
Your issue has nothing to do with the choice between Mavern and Gradle, switching therefor will not help.
Fixing a NoSuchMethodError
There are a lot of different flavors of NoSuchMethodErrors, but they all boil down to the fact that the compile time classpath differs from the runtime classpath.
The following steps will help to pinpoint the problem:
Step 1: Find Out Where the Class Comes From
First, we need to find out where the class containing the method in question comes from. We find this information in the error message of the NoSuchMethodError:
Exception in thread "main" java.lang.NoSuchMethodError:
io.reflectoring.nosuchmethod.Service.sayHello(Ljava/lang/String;)Ljava/lang/String;
Now, we can search the web or within the IDE to find out which JAR file contains this class. In the case above, we can see that it’s the Service class from our own codebase and not a class from another library.
If we have trouble finding the JAR file of the class, we can add the Java option -verbose:class when running our application. This will cause Java to print out all classes and the JARs they have been loaded from:
[Loaded io.reflectoring.nosuchmethod.Service from file:
/C:/daten/workspaces/code-examples2/patterns/build/libs/java-1.0.jar]
Step 2: Find Out Who Calls the Class
Next, we want find out where the method is being called. This information is available in the first element of the stack trace:
Exception in thread "main" java.lang.NoSuchMethodError:
io.reflectoring.nosuchmethod.Service.sayHello(Ljava/lang/String;)Ljava/lang/String;
at io.reflectoring.nosuchmethod.ProvokeNoSuchMethodError.main(ProvokeNoSuchMethodError.java:7)
Here, the class ProvokeNoSuchMethodError tries to call a method that does not exist at runtime. We should now find out which library this file belongs to.
Step 3: Check the Versions
Now that we know where the NoSuchMethodError is provoked and what method is missing, we can act.
We should now list all of our project dependencies.
In Gradle, we can call:
./gradlew dependencies > dependencies.txt
If we’re using Maven, a similiar result can be achieved with:
mvn dependency:list > dependencies.txt`
In this file, we can search for the libraries that contain the class with the missing method and the class that tries to call this method.
Usually we’ll find an output like this somewhere:
\--- org.springframework.retry:spring-retry:1.2.2.RELEASE
| \--- org.springframework:spring-core:4.3.13.RELEASE -> 5.0.8.RELEASE
The above means that the spring-retry library depends on spring-core in version 4.3.13, but some other library also depends on spring-core in version 5.0.8 and overrules the dependency version.
We can now search our dependencies.txt file for 5.0.8.RELEASE to find out which library introduces the dependency to this version.
Finally, we need to decide which of the two versions we actually need to satisfy both dependencies. Usually, this is the newer version since most frameworks are backwards compatible to some point. However, it can be the other way around or we might even not be able to resolve the conflict at all.

Camunda: cannot be cast to (same class)

I've included a .jar in my maven project writing this in pom.xml:
<dependencies>
<dependency>
<groupId>org.loopingdoge.acme.model</groupId>
<artifactId>acme-model</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/acme-model.jar</systemPath>
</dependency>
</dependencies>
acme-model.jar contains org.loopingdoge.acme.model.House but this cast
public class HouseAdder implements JavaDelegate {
public void execute(DelegateExecution delegateExecution) throws Exception {
House house = (House) delegateExecution.getVariable("house");
}
}
gives me this error when deployed on a Wildfly server:
18:50:20,255 ERROR [org.camunda.bpm.engine.context] (default task-45) ENGINE-16004 Exception while closing command context:
org.loopingdoge.acme.model.House cannot be cast to org.loopingdoge.acme.model.House: java.lang.ClassCastException: org.loopingdoge.acme.model.House cannot be cast to org.loopingdoge.acme.model.House
at org.loopingdoge.acme.services.HouseAdder.execute(HouseAdder.java:13)
Such cases happens when a class will be loaded over different classloaders. java make them distinct even if package and classname are identical.
You need to find out on which ways this class will be loaded. As first step, find the jars which contains that class.
or/and read this on SO
Ran into the same problem. Further analysis showed that two different class loaders were used, as stated in this thread already. In my case the culprit was devtools of spring boot, a tool which likely many will have active in their pom. Seems that devtools doesn't play well with (in my case) camunda java delegates with embedded camunda engine.
I guess that you have the class in multiple places, e.g. packaged the jar within WildFly and the WAR you deploy. Check that you have the class only one time on the classpath.
By the way: Better not use system dependencies if not absolutely necessary. This is what maven repositories are for.

Replace provided Maven dependency with local class

If I check my effective pom I will find the following entry:
<dependency>
<groupId>com.package.of.other.department</groupId>
<artifactId>someArtifact</artifactId>
<version>2.4.2</version>
<scope>provided</scope>
</dependency>
This comes from the parent pom that we have to use to let our software (bpmn processes) run on a company wide platform.
Now comes a hacky part. There will be a bigger change and we cannot use someArtifact anymore. Unfortunately that artifact gets called directly by all our processes (you design the process and configure the full qualified class name for an item) and can't just configure a different artifact, as that would most likely break a lot of the running processes.
The simple plan was to create a class with the same package name and with the same class name, remove every dependency to the original package and everything should work fine. During the tests I noticed that it doesn't use my new class but still the original one, most likely because it gets provided as dependency via the mandatory parent pom and for some reason prefers that over my local one.
Excluding a provided dependency from the parent pom doesn't seem to work that easily?! Any idea how I could solve my issue?
If the application is regular java, the class that will be load is the first class met in the classpath order.
If you use other runtime package dependency management, the strategy is different. As example you can adjust your import-package in OSGi to ensure the use a class contains in private-package.

Find which dependency jar has required class

How can you find which dependency to import if you just know the class name.
Imagine i am getting error
hbase/mapreduce/HbaseDBMapper.java:[9,53] package org.apache.hadoop.hbase.mapreduce.replication does not exist
This is due to fact that i dont have dependency which can give me this package.
How can i find which dependency to import from maven repository or any other website
In general, http://www.findjar.com/ can be used to find the jar files and consequent maven dependencies, given a class.
In this specific case, you may want
<artifactId>hbase-server</artifactId>
<groupId>org.apache.hbase</groupId>
as the class is defined in https://github.com/apache/hbase/tree/master/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce

How do I determine which version of a class my object is?

I'm trying to write a plugin into a framework application (Joget). My plugin source looks something like this:
public class MyPlugin extends ExtDefaultPlugin implements ApplicationPlugin, ParticipantPlugin {
...
public void execute(){
...
SecurityContextImpl secContext = (SecurityContextImpl) WorkflowUtil.getHttpServletRequest().getSession().getAttribute("SPRING_SECURITY_CONTEXT");
}
}
When I run the plugin, I get the following exception.
java.lang.ClassCastException: org.springframework.security.context.SecurityContextImpl cannot be cast to org.springframework.security.context.SecurityContextImpl
I'm using Maven. Now since both have the same package name, I'm assuming I'm accidentally using the wrong package version in my plugin JAR (that contains the SecurityContextImpl class) than the one in the framework. But I've double-checked and it looks like I'm including the correct one in my plugin package.
Is there a way to see the classloader or source JAR of my class (e.g. using reflection in some manner)? Any other ideas on how to address this?
This type of java.lang.ClassCastException, where both classes names are equals, occur when the same class, or two class with the same name are loaded by 2 different classloaders.
I don't know Joget, but you are talking about plugin. Frameworks often load plugins in separate classloaders to ensure a proper isolation between them.
Since you say I've double-checked and it looks like I'm including the correct one in my plugin package., you may want to remove spring-security from your package, as it's probably already loaded by the framework classloader.
You're using Maven, so you may simply add <scope>provided</scope> to the spring-security dependency (but not sure, since we don't have your pom.xml)
I've got the same exception when I was running my plugin.
There are two cases in general:
1. The class is a local class.
And that is to say, there is no repository(groupId, artificateId, etc) to be deployed in the pom.xml of your plugin. The solution is, go the target folder and open the xxx-0.0.1-snapshot.jar file, then open META-INF/MANIFEST.MF, add the source file of that class /dependency/file.jar, then add the source jar to the dependency folder
Remarks: It is better to give a version of the your local file and add it as shown in your pom.xml to let it be found as src in your code.
<!-- your source jar need to be renamed as example-1.0.0.jar -->
<dependency>
<groupId>this.should.be.the.prefix.of.your.package</groupID>
<artificateId>file.name<artificateId>
<version>1.0.0</version>
<systemPath>${basedir}/lib/stclient_updated-1.0.0.jar</systemPath>
<scope>system</scope>
</dependency>
2. The class is a remote class with repository
I had this type of problem once because the repository is not correct, see also Maven Repository to find the official repository of the source.
I hope that it could help =)
Cheers

Categories

Resources