2

I my application I am facing below exception,

/component/ProviderServices;Lcom/sun/jersey/core/spi/factory/InjectableProviderFactory;)V
        at com.sun.jersey.api.client.Client.<init>(Client.java:212)
        at com.sun.jersey.api.client.Client.<init>(Client.java:150)
        at com.sun.jersey.api.client.Client.create(Client.java:476)
        at com.example.data.DataReader.getData(DataReader.java:25)
        at com.example.data.TestServlet.doGet(TestServlet.java:41)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)

I found the reason for this exception but I don't know how to resolve it. The problem is I am having two jars namely jersey-bundle-1.1.5.1 and jersey-core-1.17.1 in my classpath. ContextResolverFactory.java is present in both jars with same package name. init method is present in jersey-core-1.17.1 but not in jersey-bundle-1.1.5.1. In windows build environment it is working fine. That means the JRE picks the ContextResolverFactory.java of jersey-core-1.17.1 correctly and executes the init method. Whereas in linux environment the JRE picks ContextResolverFactory.java of jersey-bundle-1.1.5.1 and tries to invoke the init method and throwing the above exception. I cant remove a jar blindly, since both jars are needed for different business purpose.

How to fix it in both linux and windows environment?

Why it is working fine in windows environment but not in linux environment?
Jaya Ananthram
  • 3,433
  • 1
  • 22
  • 37
  • 3
    You cannot have the same class in two jars. You have no control over which one gets loaded. You must figure out which JAR is a superset of the other one and use only that. I suspect `jersey-bundle` is a superset of `jersey-core`. – Jim Garrison Nov 14 '14 at 05:58
  • 1
    Check the classpath, on windows the jar with the class you expect is probably first, and on linux the other comes first. From the names of the jars it looks like you are using two versions of the same library, have you looked if there is a 1.17.1 bundle ? Usualy core = bare minimum and bundle = core AND some extra, so using both is never a good idea anyways. – Ryu Kajiya Nov 14 '14 at 06:00
  • See also https://stackoverflow.com/q/4266592/32453 – rogerdpack Aug 04 '17 at 22:00

3 Answers3

1

I fully agree with the commenters. Per se it is bad practice to have the same class (in the same package) on the classpath multiple times. This will almost always cause troubles. The best thing would be to check whether or not you can make your code work with jersey 1.17.1 and use only the jersey-core-1.17.1 jar.

However, I also understand that there are situations where you do not have control over these dependencies i.e. where 3rd party libraries depend on specific versions of a certain library and you just have to work around these issues.

In these cases it is important to notice that the default java classloaders respect the order of the elements in the classpath. I assume that the order of the CLASSPATH variable in your Linux installation is different from that on your Windows installation.

If you are using an IDE such as Eclipse during your development please check the build path setup there and try setting the CLASSPATH variable on your production in exactly the same order.

For your reference please also check these other questions on stackoverflow:

Controlling the order of how JARs are loaded in the classpath

Is the order of the value inside the CLASSPATH matter?

In the case of Tomcat the order of the JAR files in WEB-INF/lib cannot be defined. The only thing you could do here would be to ship the JAR file that needs to be loaded first to some other directory in your production environment such as the JRE/lib directory, the Tomcat/common directory or the Tomcat/shared directory. Which all have priority over the WEB-INF/lib directory. See Control the classpath ordering of jars in WEB-INF/lib on Tomcat 5? for details on how this worked on older Tomcat versions.

Community
  • 1
  • 1
Matthias Steinbauer
  • 1,786
  • 11
  • 24
  • I agree with your answer. I have an doubt from your answer. In local I am having classpath with order lets say a.jar as first, b.jar as second. Both jar contains Test.java so If I access Test.java it will use from a.jar since in class path order, a.jar occurs at first. But in production environment the .classpath file will not be present. I will put all jars in tomcat\lib\mylib. How it will choose the order now? – Jaya Ananthram Nov 14 '14 at 08:03
1

One of the guiding principles that I try to follow when I develop my own applications is that I want to make them "dummy-proof." I want to make it as easy as possible on the end user.

Therefore, I would change the build of the applications to include ContextResolverFactory.class in your final jar (from jersey-core-1.17.1.jar). That's the general approach. The specific tool you use to achieve this might vary.

I would use maven and the maven-shade-plugin. This plugin can even do what's called a relocation where you provide the original package in the pattern tag, and you provide the desire new package location in the shadedPattern tag:

<build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>1.6</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
                <configuration>
                    <relocations>
                        <relocation>
                            <pattern>com.sun.jersey.core.spi.factory</pattern>
                            <shadedPattern>${project.groupId}.${project.artifactId}.com.sun.jersey.core.spi.factory</shadedPattern>
                        </relocation>
                    </relocations>
                    <artifactSet>
                        <includes>
                            <include>com.sun.jersey:jersey-core</include>
                        </includes>
                    </artifactSet>
                    <minimizeJar>true</minimizeJar>
                </configuration>
            </execution>
        </executions>
    </plugin>
    </plugins
</build>

Even if you're not experienced with maven, you could still make a small side project whose only purpose is to refactor the package location. Then, you would add this dependency to your project and use it to reliably access the init() method.

If you are experienced with maven, then I highly recommend splitting your project up into what's called a maven multi module POM project. This would be the new build order:

  1. The Interface module
  2. The Implementation Layer
  3. The Runtime module

Where the Implementation Layer typically consists of many different modules that all depend upon the Interface module. And the Runtime module chooses the correct implementation at runtime.

You might not see the value if you currently only have one implementation... But down the road, it adds flexibility if you need to add more implementations, because you will be able to add them easily. Because your code never directly references an implementation, but rather, it always uses the interface, and it doesn't care which implementation is used.

So, this would make it harder on you, the developer, but easier on the end-user. Whether they're on windows, linux, or mac, it just works!

Nikolaii99
  • 115
  • 6
0

After checking the source-code, I noticed that all the logic of init() was moved to the constructor.

So another option, is to simply use the new constructor and catch the exceptional circumstance where it's not there, in which case, you would just use the default constructor followed by the init() method:

ContextResolverFactory factory = null;
try {
    factory = new ContextResolverFactory(providerServies, ipf);
} catch (InvalidClassException ex) {
    factory = new ContextResolverFactory().init(providerServices, ipf);
}
// ...
ContextResolver<MyType> cr = factory.resolve(type, mediaType);
if (cr == null) // handle null and not null...

Hopefully this helps. Good luck!

Nikolaii99
  • 115
  • 6
  • Yes.. you are right but calling the ContextResolverFactory init method is not handled in my code. It was internally called from other libs. I cannot change as you given in sample code.. Bad luck for me :) – Jaya Ananthram Nov 17 '14 at 04:19