OSGI bundle .bnd file and conflicting import-package statements - java

I inherited a .bnd file that has the import !javax., and my program produces runtime errors if I take out that statement. I currently need to add JavaHelp to my application, which is under javax.help.. When I have both of those imports in my .bnd and I use a class from JavaHelp, it produces a ClassNotFoundException for the specific class, like javax.help.JHelp. Is there a way to handle this situation, I feel stuck.

Bundles generally need to import all the package that do not start with "java.". This includes "javax." packages. If your bnd file explicitly does not import javax. package, then it must rely upon non-standard boot delegation configuration for the framework. That is, the boot delegation provides "free" access to javax. packages from the bundles parent classloader (likely the bootclassloader). So when you remove !javax. from the bnd file, your bundle starts to import those packages from some provider which may not be the same as whatever bootdelegation is providing.

Related

Possible to ignore configuration classes on the classpath?

I have a Spring Boot application that works as expected when ran with embedded tomcat, but I noticed that if I try to run it from an existing tomcat instance that I'm using with a previous project then it fails with a NoClassDefFoundError for a class that I don't use anywhere in my application.
I noticed in the /lib directory I had a single jar that contained a few Spring annotated classes, so as a test I cleaned out the /lib directory which resolved the issue. My assumption is that Spring is seeing some of the configurations/beans/imports on the classpath due to them existing in the /lib directory and either trying to autoconfigure something on its own, or is actually trying to instantiate some of these classes.
So then my question is - assuming I can't always fully control the contents of everything on the classpath, how can I prevent errors like this from occurring?
EDIT
For a little more detail - the class not being found is DefaultCookieSerializer which is part of the spring-session-implementation dependency. It is pulled into one of the classes in the jar located in /lib, but it is not any part of my application.
Check for features provided by #EnableAutoConfiguration. You can explicitly configure set of auto-configuration classes for your application. This tutorial can be a good starting point.
You can remove the #SpringBootApplication annotation from the main class and replace it with an #ComponentScan annotation and an #Import annotation that explicitly lists only the configuration classes you want to load. For example, in a Spring boot MVC app that uses metrics, web client, rest template, Jackson, etc, I was able to replace the #SpringBootApplication annotation with below code and get it working exactly as it was before, with all functional tests passing:
#Import({ MetricsAutoConfiguration.class,
InfluxMetricsExportAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class,
WebClientAutoConfiguration.class,
RestTemplateAutoConfiguration.class,
RefreshAutoConfiguration.class,
ValidationAutoConfiguration.class
})
#ComponentScan
The likely culprit of mentioned exception are incompatible jars on the classpath.
As we don't know with what library you have the issue we cant tell you the exact reason, but the situation looks like that:
One of Spring-Boot autoconfiguration classes is being triggered by the presence of class on the classpath
Trigerred configuration tries to create some bean of class that is not present in the jar you have (but it is in the specific version mentioned in the Spring BOM)
Version incompatibilities may also cause MethodNotFound exceptions.
That's one of the reasons why it is good practice not to run Spring Boot applications inside the container (make jar not war), but as a runnable jar with an embedded container.
Even before Spring Boot it was preferred to take account of libraries being present on runtime classpath and mark them as provided inside your project. Having different versions of the library on a classpath may cause weird ClassCastExceptions where on both ends names match, but the rest doesn't.
You could resolve specific cases by disabling autoconfiguration that causes your issue. You can do that either by adding exclude to your #SpringBootApplication or using a property file.
Edit:
If you don't use very broad package scan (or use package name from outside of your project in package scan) in your Spring Boot application it is unlikely that Spring Boot simply imports configuration from the classpath.
As I have mentioned before it is rather some autoconfiguration that is being triggered by existence of a class in the classpath.
Theoretical solution:
You could use maven shade plugin to relocate all packages into your own package space: see docs.
The problems is you'd have face:
Defining very broad relocation pattern that would exclude JEE classes that need to be used so that container would know how to run your application.
Relocation most likely won't affect package names used as strings in the Spring Boot annotations (like annotations #PackageScan or #ConditionalOnClass). As far as I know it is not implemented yet. You'd have to implement that by yourself - maybe as some kind of shade plugin resource processor.
When relocating classes you'd have to replace package names in all relevant configuration located in the jars. Possibly also merge some of those.
You'd also have to take into account how libraries that you use, or spring uses use package names or files.
This is definitely not a trivial tasks with many traps ahead. But if done right, then it would possibly allow you to disregard what is on the containers classpath. Spring Boot would also look for classes in relocated packages, and you wouldn't have those in ordinary jars.

Websphere: Shared libraries in common classloader earlier on classpath than application modules, even with parent last policy

Background:
I have the following problem: I have several WAR files I need to have deployed on same Websphere server. The WAR files use libraries that depend on having a specific version of XMLSec regisered as the XML Signature Provider (with the Java Security class). Currently I bundle this library with each WAR file (since the WAR files also need to work standalone and on Tomcat's without any special shared library configuration etc.). Each WAR files registers the provider with Security.addProvider() in a ServerContextListener. But this causes problems in the multi-WAR setup, because if one WAR file does the registration with Security.addProvider) and another WAR files tries to fetch it using the XMLSignatureFactory class (which is actually a javax.* class contained inside the XMLSec JAR itself, but which ultimately calls back to the global provider list configured with Security.addProvider), then it causes a ClassCastException inside XMLSignatureFactory, because this class does a cast of what it gets from Security into to its own version of the provider classes, which doesn't work. The exact stack trace is as follows:
Caused by: java.lang.ClassCastException:
org.apache.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory
incompatible with javax.xml.crypto.dsig.XMLSignatureFactory at
javax.xml.crypto.dsig.XMLSignatureFactory.findInstance(XMLSignatureFactory.java:202)
at
javax.xml.crypto.dsig.XMLSignatureFactory.getInstance(XMLSignatureFactory.java:292)
By the way this is not a case of conflict with different versions of XMLSec being in play or conflicts with Websphere's own version. There is only one version albeit it is loaded from different WAR's.
Of course the solution is to have the xmlsec library loaded with a common classloader so that there is only one version of the classes loaded that all WAR files see, which would avoid ClassCastExceptions etc.. But here is the rub: I also need to have each application loaded with the "parent last" policy - or rather, I need the JAR files inside each application to take precedence over Websphere's built-in version of the libraries (for instance Axis2 that I also include in the WAR filesetc.). Furter, I would prefer that I can keep the xmlsec library in each WAR files' WEB-INF/lib folder, so that the WAR files can still work stand-alone (i.e. in other environments which might not have the shared library configured etc.).
So basically I want to have a common class loader loading the XMLSec library, say, somewhere from disk. Let's denote that [SHARED XMLSEC]. Then I want each application classpath to ultimately appear like this:
App1: [SHARED XMLSEC][App1 WEB-inf/lib][Websphere libraries][JDK libraries]
App2: [SHARED XMLSEC][App2 WEB-inf/lib][Websphere libraries][JDK libraries]
etc.
In such a configuration it doesn't matter if App1+App2 themselves contain the XMLSec library since the shared one will take precedence so they will use the common one. At the same time, App1+App2 are still free to override other built-in Websphere libraries (Axis2).
Is it possible to realize this configuration and what options do I need to set? Do you see alternative ways to achieve the same objective?
Since you have a conflict between classes here, I would suggest going for isolated class loaders for each application. On the server side, setting the class loader policy to 'Multiple' should provide isolation between applications.
Once you have this set, configure class loading at the application level to the 'Parent last' configuration for both the applications.
The following Knowledge Center link has the relevant instructions [Steps 2,3 & 4 under the 'Procedure' section] :
http://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/trun_classload.html
[Note: The version of WAS in use is not specified in the question. The Knowledge Center link refers to version 8.5.5.]

What is the difference between javax.faces-2.1.14.jar from jsf-api-2.2.0-m05.jar and jsf-impl-2.2.0-m05.jar

I am confused between the difference of javax.faces-2.1.14.jar from jsf-api-2.2.0-m05.jar and jsf-impl-2.2.0-m05.jar
I am trying to make a jsf project without using maven and while searching for examples I saw different jar dependencies on different tutorials. The tutorial from coreservlets uses javax.faces-2.1.14.jar and the other tutorial I saw from a different site uses 2 jar files jsf-api-2.2.0-m05.jar and jsf-impl-2.2.0-m05.jar
Id like to know the difference of these two set of dependencies.
Thanks in advance guys.
The jsf-api-xxx.jar contains classes that are under packages java package javax.faces. The classes in this jar are implementation of standard API defined by JSF specification. The jsf-impl-xxx.jar contains classes that are under java package com.sun.faces, which as the java package indicates, is Sun's own class for JSF implementation.
The javax.faces-xxx.jar actually merges the previous two jar files together. In the Maven repository, it is under org.glassfish groupId. You will find both javax.faces and com.sun.faces java package inside.
So what should you use for your development? Actually you can use both. But the recommended way is to include jsf-api-xxx.jar only in the compilation classpath. Thus your own application does not depend on any classes under com.sun.faces package to achieve the portability to other JSF implementations. If you are using an application such as GlassFish, the container should already provide the JSF implementation at runtime. You should not package the above JSF jar files with your war or ear file, as server will provide all of them. If you use a container that does not come with a JSF implementation by default such as Tomcat, you need to package javax.faces-xxx.jar or (jsf-api-xxx.jar + jsf-impl-xxx.jar) in the WEB-INF/lib.
They are different versions.
javax.faces-2.1.14.jar is a combined JAR file for 2.1.14
jsf-api-2.2.0-m05.jar and jsf-impl-2.2.0-m05.jar are separate JARs for 2.2.0-m05.
The differences between the versions can be found in the release notes, but 2.2 and 2.1 are non-trivially different.

extend war package imports in gemini container

if i deploy a war-file into a gemini container (e.g. virgo has one) it will be transformed on-the-fly into an osgi bundle by adding some package imports (besides other things).
Is it possible to somehow extend these default package imports using for example a bundle-listener or something like this?
regards
I would strongly recommend that you do the transformation yourself before deploying into the Gemini container, rather than forcing Gemini to do the transformation on-the-fly. First it is very easy to do; second it will be much faster to deploy; third you will be able to add the specific imports that you want.
In order to turn a standard WAR file into a WAB (Web Application Bundle) that remains compatible with traditional WAR deployment, you just need to add the following headers to the MANIFEST.MF of the WAR:
Web-ContextPath to define the context path under which the web application will be served
Set Bundle-ClassPath to WEB-INF/classes plus any JARs under WEB-INF/lib. You will have to name these explicitly e.g.: Bundle-ClassPath: WEB-INF/classes,WEB-INF/lib/a.jar,WEB-INF/lib/b.jar...
Import-Package: javax.servlet,javax.servlet.http plus anything else you want to import.

Deploying 2 war files with common classes in JBoss

I have two war file app1.war and app2.war deployed in a single JBoss instance. Package names for java classes for both war files starts with com.myapp
To add further, there are some Classes that are common between the two apps while there are some that have same fully qualified class names but are different (Source Code has changed).
I want to know, if this could pose threat of any kind to the deployment scenario?
You could get class loading problems if your applications are not isolated, i.e. have their own class loading repository and class loaders. If you configure JBoss to isolate the applications from each other you should be fine (I don't know what is the default for your version but 4.2.3 that we use does not isolate apps by default).
To clarify that a bit:
If you have two classes with different implementations but the same FQCN you could get the wrong class from the class loader for the application that is loaded second. Even if the implementation was the same you could get class cast exceptions or other strange behavior if one app gets the class from the other app.
I had a similar situation with multiple apps.Look at my solution here
Best way is to isolate class loading for your application archives.
For JBoss 5.1.0 GA following worked for me.
Create jboss-classloading.xml file in WEB-INF folder.
Added following lines to this file
Here,
export-all="NON_EMPTY" => Makes sure the classes loaded for this app is not exported
import-all="true" => Imports and uses all of the class definition available.
parent-first="false" => If more than one class with same name is found, one defined under the application will be used first.
FYI. This also helped me embedding the log configuration of log4j in the application war file. Will need to place log4j.xml in WEB-INF/classes and have a log4j.jar in WEB-INF/lib folder.
There will be one class loader instance for each application or standalone module. In other words, classes in app1.war will be loaded in different class loader than the classes in app2.war. This is the default behavior of any Java EE server; So it really doesn't matter about having classes with the same package/names and/or different content. This is the default behavior of any Java EE server.
Having said that, if you tweak the class loader policy of the server or try to load classes (reflect) using anything other than Thread.currentThread().getContextClassLoader(), you could be asking for trouble.

Categories