How to select version of library to use in OSGI bundle - java

I develop jira plugin, where I should send email with images.
There is one bug with javax.mail-1.4.5, that was fixed in javax.mail-1.4.7
I tried a lot, but was not able when plugin deployed on jira tell to use my version of javax.mail (1.4.7). It all the time use javax.mail-1.4.5 from root class loader.
I all the time recieve next Exception:
java.lang.LinkageError: loader constraint violation: when resolving overridden method "javax.mail.internet.MimeMessage.getDataHandler()Ljavax/activation/DataHandler;" the class loader (instance of org/apache/felix/framework/ModuleImpl$ModuleClassLoader) of the current class, javax/mail/internet/MimeMessage, and its superclass loader (instance of org/apache/catalina/loader/WebappClassLoader), have different Class objects for the type getDataHandler used in the signature
at com.solarwinds.MailSenderJob.buildSimpleMessage(MailSenderJob.java:163)
My question is next: how to tell OSGi, that here I would like to use another version of library?

Add the specific library to the class path of your project. Or if you're using Maven, simply edit the pom.xml file.

Related

Byte Buddy - java.lang.NoClassDefFoundError: javax/servlet/ServletRequest

I am trying to instrument service method of javax.servlet.Servlet interface implementations as following:
.transform(
new AgentBuilder.Transformer.ForAdvice()
.include(MyAgent.class.getClassLoader())
.advice(
named("service")
.and(takesArgument(0, ServletRequest.class))
.and(takesArgument(1, ServletResponse.class))
, "com.MyAdvice"
)
)
Now, if I attach this agent to an already running Spring boot application via agentmain - I see below exception on trying to access any web page
java.lang.LinkageError: loader constraint violation: when resolving method 'void javax.servlet.http.HttpServletRequestWrapper.<init>(javax.servlet.http.HttpServletRequest)'
the class loader org.springframework.boot.loader.LaunchedURLClassLoader #18be83e4 of the current class,
org/springframework/web/servlet/resource/ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper, and the class loader 'app' for the method's defining class, javax/servlet/http/HttpServletRequestWrapper,
have different Class objects for the type javax/servlet/http/HttpServletRequest used in the signature (org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper is in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader #18be83e4, parent loader 'app'; javax.servlet.http.HttpServletRequestWrapper is in unnamed module of loader 'app')
I understand its because javax/servlet/http/HttpServletRequest instance is loaded from 2 different jars - one that came through agent and another through spring boot embedded tomcat. If I try to set scope as provided in agent's pom.xml [ since I do not want to include servlet-api in agent bundled jar and try use already provided servlet-api implementation from Spring boot ], I get Caused by: java.lang.NoClassDefFoundError: javax/servlet/ServletRequest - possibly because the classloader loading the agent can't see spring boot provided servlet api.
Is there any possible workaround or fix to resolve this situation? Thank you for your time and feedback.
Ideally, your agent does not contain such classes. Rather, match classes by their name: takesArgument(0, named("javax.servlet.ServletRequest")), for instance.

Hibernate java.lang.ClassCastException: _$$_javassist_856 cannot be cast to javassist.util.proxy.Proxy when using Websphere Shared Library

Websphere 8.0.0.11
Hibernate 4.2.21.Final
I have found many questions about this same problem but none of them worked for me.
If I deploy the application in Websphere it works OK.
However we have defined a shared library that contains all the third party libraries (spring, hibernate, javassist, etc) so that our WARs are thinner.
This way during deployment we associate our thin WAR against that Websphere shared library.
The point is that when we deploy the application this way the ClassCastException Hibernate exception _$$_javassist_856 cannot be cast to javassist.util.proxy.Proxy is thrown.
I have checked the loaded jars in the websphere console and can only see one javassist jar (3.18.1-GA) in the classpath.
Why could this be happening?
UPDATE
I have also tried using PARENT_FIRST and PARENT_LAST class loading.
UPDATE 2
I just found out that Websphere is loading its own javassist jar:
URL location = ProxyFactory.class.getProtectionDomain().getCodeSource().getLocation();
logger.info("{}", location);
It prints: file:/opt/IBM/WebSphere/AppServer/plugins/javassist.jar
After trying pretty much everything I found on S.O. without any success I decided to downgrade Hibernate to version 4.1.12.Final. This is the maximum 4.x version compatible with Websphere 8.x.
The problem is that Javassist leaves traces in its generated code. With Javassist on the class path twice, its classes are loaded twice. Two types are however only equal if they have the same name and are loaded by the same class loader. In your case, the generated class resolves its Javassist dependeny to a type that is loaded by your application class loader while your code is casting the instance to the Javassist type that is loaded by the Websphere class loader (or the other way around).
Are you sharing any Hibernate dependencies between applications? Try to not use any shared libraries related to Hibernate in your application to avoid this.

Extending Liferay UserLocalServiceImpl in an Ext plugin

In my Liferay 6.1.1 installation I have extended (note: not overridden) the UserLocalServiceImpl class in an Ext plugin to provide a more relaxed screen name validation. However, I have been unable to get Liferay to load the new class instead of the default UserLocalServiceImpl class.
As far as I can tell, the "normal" way to replace service classes is to use a hook plugin. Unfortunately, hook plugins cannot access the implementation classes. I would have to extend UserLocalServiceWrapper instead of UserLocalServiceImpl, which would be very cumbersome for this particular use.
Is there a way to get Liferay to use my modified service class from an Ext plugin? Alternatively, is there a way to allow a hook plugin access to the implementation classes?
It is possible to change the service class implementation from an Ext plugin using the following procedure:
Create a new folder named META-INF under docroot/WEB-INF/ext-impl/src in the Ext folder.
Create a new file named ext-spring.xml under docroot/WEB-INF/ext-impl/src/META-INF.
Search the Liferay source distribution for the portal-spring.xml file. In Liferay 6.1.1 it can be found at portal-impl/src/META-INF/portal-spring.xml.
Copy the content of portal-spring.xml to the ext-spring.xml file that was created earlier.
Remove all bean entries from ext-spring.xml, except for those that correspond to classes that will be replaced/extended.
For each remaining entry, change the class attribute to point to the replacement class.
Re-deploy the Ext plugin and restart the Liferay application server.

REST client inside of OSGi application

I need to integrate a REST client into an existing OSGi application implemented using Apache Felix. The REST service is based on RESTeasy implementation (version 2.3.2.Final) of JAX-RS. I created a separate bundle with clients' dependencies, exporting required RESTeasy packages and importing them in the bundle where the client is used, but unfortunately I cannot get it working inside of the OSGi context.
I tried two different approaches. First one using the generic ClientRequest:
ClientRequest request = new ClientRequest(MyService.URL_TEST+"/stats");
request.body(javax.ws.rs.core.MediaType.APPLICATION_XML, stats);
ClientResponse<String> response = request.post(String.class);
The error that I get in this case is pretty weird:
[java] java.lang.RuntimeException: java.lang.ClassCastException:
org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor cannot be cast to
org.jboss.resteasy.client.ClientExecutor
where I it is known for sure that ApacheHttpClient4Executor implements the ClientExecutor interface.
When I try to use my own REST client wrapper around RESTeasy like this:
MyService myService = MyServiceClient.getInstance();
myService.saveStatistics(stats);
I get a different exception:
[java] java.lang.LinkageError: ClassCastException: attempting to
castjar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar
!/javax/ws/rs/ext/RuntimeDelegate.classtobundle:
//78.0:1/javax/ws/rs/ext/RuntimeDelegate.class
As far as I understand, the LinkageError most probably has to do with the way RESTeasy initializes the RuntimeDelegate using some classloader tricks, which probably fall under the restrictions of OSGi framework. I get the suspicion that the java.lang.ClassCastException mentioned first has the same source.
Is there any way to get RESTeasy working inside of OSGi?
PS: discussion about a similar issue with RESTeasy, but outside of OSGi: java.lang.LinkageError: ClassCastException
Update:
these are the libraries included into restclient bundle:
activation-1.1.jar commons-codec-1.2.jar commons-httpclient-3.1.jar commons-io-2.1.jar commons-logging-1.0.4.jar flexjson-2.1.jar httpclient-4.1.2.jar httpcore-4.1.2.jar javassist-3.12.1.GA.jar jaxb-api-2.2.3.jar jaxb-impl-2.2.4.jar jaxrs-api-2.3.2.Final.jar jcip-annotations-1.0.jar jettison-1.3.1.jar jsr250-api-1.0.jar junit-4.10.jar log4j-1.2.14.jar resteasy-jaxb-provider-2.3.2.Final.jar resteasy-jaxrs-2.3.2.Final.jar resteasy-jettison-provider-2.3.2.Final.jar scannotation-1.0.3.jar slf4j-api-1.6.4.jar slf4j-log4j12-1.6.4.jar myservice-common-0.1.0.3.jar my-service-client-0.1.0.3-SNAPSHOT.jar stax-api-1.0-2.jar xmlpull-1.1.3.1.jar xpp3_min-1.1.4c.jar xstream-1.4.2.jar
These are the exports from the restclient bundle: javax.ws.rs, javax.ws.rs.ext, javax.ws.rs.core, org.jboss.resteasy.client, org.jboss.resteasy.client.cache, org.jboss.resteasy.client.extractors, org.jboss.resteasy.client.marshallers, org.jboss.resteasy.client.core.executors, javax.xml.bind.annotation, org.jboss.resteasy.plugins.providers, org.jboss.resteasy.plugins.providers.jaxb, org.jboss.resteasy.spi
Have a look at the SpringSource Bundle Repo, it's got some very useful pre-built bundles of common libraries including the Apache HTTP Client which we are using (in conjunction with gson) to do our RESTful comms.
(unfortunately a legacy module of my project still uses OSGi, but using RESTeasy 3.0.16 now)
When I need to OSGify a dependency my preferred solution now is to wrap it using the excellent Apache Ops4j Pax Tipi project.
The project provides a preconfigured Maven setup (parent POM handles the bundling) and you just have to adapt the GAV coordinates of the original project in a Tipi sub module with a org.apache.ops4j.pax.tipi prefix and build the new bundle project which draws in the original dependency, unpacks and wraps it as OSGi bundle.
You can start from an existing Tipi sub project that best matches your project setup (dependencies, etc.) and adapt any OSGi imports/exports missing (most often, these are created automatically by the maven-bundle-plugin anyway).
This worked quite well for me as long as the original project did not contain too many exotic or malformed dependencies.
However you may run into snags like transitive dependencies using the root package, as I currently experience, which can be a real show stopper (finding out which library is a real nightmare).
Unfortunately, RESTeasy seems to be affected by this, as I get exactly the same error (default package , even after declaring non-test and non-provided dependencies as optional:
The default package '.' is not permitted by the Import-Package syntax.
Upgrading the maven-bundle-plugin to the latest release 3.0.1 yields a different error (even less helpful):
[ERROR] Bundle org.ops4j.pax.tipi:org.ops4j.pax.tipi.resteasy-jaxrs:bundle:3.0.16.Final.1 : Can not parse name from bundle native code header:
[ERROR] Error(s) found in bundle configuration
Update seems to be solved by upping Tipi version in POM to 1.4.0, testing...
Is RESTEasy mandatory ?
I personally use jersey in OSGi and it is working perfectly, both as client and server.
This problem isn't limited to RESTeasy. It also occurs with Jersey.
It is occurring because you have two copies of the JAX-RS classes on the classpath.
You can see this in the LinkageError:
[java] java.lang.LinkageError: ClassCastException: attempting to cast jar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar!/javax/ws/rs/ext/RuntimeDelegate.class to bundle://78.0:1/javax/ws/rs/ext/RuntimeDelegate.class
i.e. one copy is coming from:
D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar
and the other from the OSGI bundle.
This causes problems for the RuntimeDelegate class, which by default uses the system class loader to create the RuntimeDelegate implementation (see javax.ws.rs.ext.FactoryFinder).
The problem can also occur if the same jar is loaded via two different class loaders.
There are a couple of workarounds:
remove the jaxrs-api-2.3.2.Final.jar from the system class path
set the thread context class loader to that of your bundle, prior to making any JAX-RS calls.
The FactoryFinder will use this to load the RuntimeDelegate.
To avoid polluting your code with calls to Thread.currentThread().setContextClassLoader(myBundleClassLoader), you can wrap your JAX-RS client using a Proxy. e.g. see the Thread context classloader section of https://puredanger.github.io/tech.puredanger.com/2007/06/15/classloaders/

Spring cannot resolve type in WEB-INF/libs

I recently start a spring web project with HBase. The problem is,spring seems unable to resolve types under WEB-INF/libs. it complains about "unresolved org.apache.hadoop.conf.Configuration" which is indirectly referenced from required .class file, which is actually in a .jar under the WEB-INF/lib library. Is there anything to be set for spring container to find it?
You need to make sure that Spring itself was loaded by the same class loader: the WAR class loader. This class loader should include all JARs in WEB-INF/lib and all .class files in WEB-INF/classes.
There's a way to pass a class loader into Spring to use a different class loader than the one that loaded it (or the Thread's context class loader), but that gets more complicated.
OK I got the problem. It seems that my project depended on hadoop-core-1.0.2 whilst hbase depend on hadoop-core-1.0.0 and that confused Spring. I fixed the dependency and problem solved.

Categories

Resources