Skip to Content
author's profile photo Former Member
Former Member

Isolating a portal component using custom classloaders (hard!)

Tough problem coming up.

If you haven't read the thread Inqmyxml.jar exclusing at https://forums.sdn.sap.com/thread.jspa?forumID=41&threadID=22479&messageID=202313#202313

please do that first.

Due to recent problems with webservices in early version of the portal, I'm currently doing a proof of concept on using AXIS to call webservices in the portal.

Of course the classloader problem shows up here, since the xml parser classes are allready loaded by through the classloader of the portal component.

I have however managed to successfully implement total isolation with two different methods, but I feel none of them are good enough for using in a production system.

1. The first thing I did was to place all the jar files I needed for AXIS (including xerces) in a folder AxisEPPoC\dist\PORTAL-INF\lib_internal.

This only so that the portal component classloader doesn't know of them

2. Then I made a fucntion which starts on the private resource path and retrieves all the jar files under it as URL's

3. This was then feed into my own custom parent last classloader (but it also works with the URLClassloader if you set the parent to null). My own custom parent last classloader, basically loads the class in the current classloader if it exist and the class is not a part of the java package, other wise it delegates up to the parent if any parent is set (but I used it without a parent)

4. Then I use the tip from Armin by calling

Thread thread = Thread.currentThread();		ClassLoader orgContextClassLoader =thread.getContextClassLoader();
//myClassloader is my custom parent last classloader
thread.setContextClassLoader(myClassLoader);

//in finally clause of the try I revert
} finally {			thread.setContextClassLoader(orgContextClassLoader);
}

I had hoped this was enough, but I still get an exception when I instansiate the DirectoryLocator of Axis indicating that it is using the xml parser of the portal

Caused by: java.lang.ExceptionInInitializerError: java.lang.ClassCastException: com.inqmy.lib.jaxp.SAXParserFactoryImpl

at javax.xml.parsers.SAXParserFactory.newInstance(Unknown Source)

at org.apache.axis.utils.XMLUtils.initSAXFactory(XMLUtils.java:218)

at org.apache.axis.utils.XMLUtils.<clinit>(XMLUtils.java:119)

at org.apache.axis.configuration.FileProvider.configureEngine(FileProvider.java:209)

at org.apache.axis.AxisEngine.init(AxisEngine.java:187)

at org.apache.axis.AxisEngine.<init>(AxisEngine.java:172)

at org.apache.axis.client.AxisClient.<init>(AxisClient.java:88)

at org.apache.axis.client.Service.getAxisClient(Service.java:143)

at org.apache.axis.client.Service.<init>(Service.java:152)

at org.tempuri.DirectoryLocator.<init>(DirectoryLocator.java:10)

at java.lang.Class.newInstance0(Native Method)

at java.lang.Class.newInstance(Class.java:232)

at com.company.portal.component.AxisEPPoC.doContent(AxisEPPoC.java:47)

5. Since the above failed, I implemented the solution using the java.reflection API on my custom classloader

Basically doing calls like

Class directoryLocatorClass = myClassLoader.loadClass("org.tempuri.DirectoryLocator") ; 
Object directoryLocatorObj =  directoryLocatorClass.newInstance();

Method getDirectorySoapMethod = directoryLocatorClass.getMethod("getDirectorySoap", null);
Object directorySoapObj = getDirectorySoapMethod.invoke(directoryLocatorObj, null);

This works without a problem.

But coding with reflection is not very practical, you lose compile time checking and there is no way to get results in any complex data type (only java.lang types).

6. So I tried one more thing.

This time I created a class which extends a thread and put the webservice call in the run method.

Then I used Armin's trick from 4. and afterwards created a new thread.

Thread axisThread = ((Thread)myClassLoader.loadClass("com.company.portal.component.AxisThread").newInstance());
axisThread.run();

And this worked!

But then it hit me that all this was to no avail, as communicating with the thread in any orderly fashion was impossible (as far as I could think of).

Anyone have any ideas how to solve this (or if it is solvable?)

Add a comment
10|10000 characters needed characters exceeded

Related questions

1 Answer

  • Posted on Jan 17, 2005 at 10:51 AM

    Hi Dagfinn,

    I have some questions, but maybe these start to help 😊

    To start, I've never worked with Axis, so I don't know anything about it's inner structure.

    As far as I understand the aim is to kick out inqmyxml and let Axis use a different implementation (Xerces?). Right?

    You wrote that you excluded the classes from the java package from your parent-last-classloader - does this also hold for javax.*? How about the idea - to make it generic - to look inside the found JARs and to apply the parent-last-strategy only to the found classes?

    I don't know to what "tip of Armin" you are referring - can it be that you are talking about my hint using ThreadContextClassloader?

    If so, my question is: Does Axis make use of the ThreadContextClassloader? (At a first glance, it does not seem to, at least not in the field we are discussing.)

    The next thing is that when trying to instantiate DirectoryLocator (done by DefaultConstructor?), in XMLUtils.java:218 Axis tries to load a SaxParserFactory using SAXParserFactory.newInstance(); (see http://cvs.apache.org/viewcvs.cgi/ws-axis/java/src/org/apache/axis/utils/XMLUtils.java?rev=1.101&view=markup ) (because the method is called from static initializer with null as factoryClassName). This means it uses the system property javax.xml.parsers.SAXParserFactory to load the class - this will be set to com.inqmy.lib.jaxp.SAXParserFactoryImpl. The problem is that you shouldn't set this property to some Xerces class for other threads using this property in parallel...

    In the end, for using reflection it works, it seems that the ThreadContextClassloader does not get used?!

    For the thread alternative, I don't know what communication you want; if you just want some return value within you "main" thread, you could do it this way:

    ReturnValueBean rvb = new ReturnValueBean();
    axisThread.setReturnValueBean(rvb);
    axisThread.run();
    axisThread.join();
    SomeDetailedReturnValue = rvb.getSomeDetail();
    

    and just write return values into the bean within run() method. But I'm quite unsure if that is enough for you and if the whole technique won't lead to other ClassCastException later on...

    I hope we get closer...

    Best regards

    Detlev

    Add a comment
    10|10000 characters needed characters exceeded

Before answering

You should only submit an answer when you are proposing a solution to the poster's problem. If you want the poster to clarify the question or provide more information, please leave a comment instead, requesting additional details. When answering, please include specifics, such as step-by-step instructions, context for the solution, and links to useful resources. Also, please make sure that you answer complies with our Rules of Engagement.
You must be Logged in to submit an answer.

Up to 10 attachments (including images) can be used with a maximum of 1.0 MB each and 10.5 MB total.