views:

112

answers:

4

From the perspective of a cross application/applet java accessibility service, how would you link to a package but only optionally execute an action based on existence/availability of a package (being already loaded) at runtime?

I think what I'm interested in here is a way to resolve the class identity crisis but rather than the issue being beetween 2 apps sharing objects, being a service loaded at a higher level of the class loaders. It seems like reflection is the way to go, but I am not sure how or if I can implement a derived class this way. I need to add a specific listener derived from the specific optional classes, I can load the listener using the applet class loader but the internals still fail. Say you wanted to add an JInternalFrameListener, but Swing wasn't guaranteed to be available, using reflection you can find the method to add the listener, but how can you create and have the frame listener work if it cannot find any of the related classes because they can't be found in the base classloader! Do I need to create a thread and use setContextClassLoader to the classloader that knows about swing so that I can get the class to be loaded reliably? simply trying to set the class loader on my existing thread didn't seem to work.

Earlier description of issues Sorry, I'm not quite sure what to ask or how to make this clear, so it rambles on a bit.

Say a class uses some feature of another, but the other class may not always be available - say finding the website from JNLP if this is a JNLP app.

At one stage I thought that simply compiling against JNLP would mean that my class would not load unless JNLP was available, and so to identify this optional section I simply wrapped a try{} catch( NoClassDefFoundError ) around it.

Later something changed (perhaps changing jdk or ?? I don't recall) and it seemed that I should also use a try{} catch( ClassNotFoundException ).

Now I wanted to extend this idea to other optional features, but it doesn't seem to work consistently.

Say I wanted to add some feature to do something more advanced in a JRE1.6 runtime using the same jar and classes as I run in a JRE1.3, or say I want to handle some controls in a specific gui toolkit which may not always be used like SWT or oracle.forms.

Is there some way of doing this more reliably? It just seems wrong to cause an exception and catch it to ignore it all the time.

The current issue comes down to being able to compile against oracle.forms but then the accessibility component installed in ext is unable to access the oracle.forms classes even though objects from the oracle.forms package have been created. If I throw the frmall.jar into the ext directory to test then the accessibility component works up to the point that the whole lot gets flakey because of the different versions of the same package.

I seem to be caught up on an issue with the class loader not being the right one or something (??). How do I find the right one?

Edit: The answers so far are kindof interesting but not quite getting me where I want to be.

In the case of the gui components I currently compile in the form of a factory something like...

import oracle.forms.ui.*;
import java.awt.*;
static public IComponentNode newNode( INode parent, Component component ) {
  System.out.println( component.getClass().toString() );
  try{
  if( component instanceof FormDesktopContainer )
     ... does stuff here like return new FormDesktopNode( parent, (FormDesktopContainer) component )
  } catch ( NoClassDefFoundError a ) {
    System.out.println( a.getMessage() ); 
  }

where it prints out class oracle.forms.ui.FormDesktopContainer and then throws and exception on the instanceof call with NoClassDefFound thus printing out oracle/forms/ui/FormDesktopContainer

So how can it have an instance of a class yet not be able to find it?

+1  A: 

You can check the availability of a class by calling

ClassLoader.getSystemClassLoader().loadClass("my.package.MyClass")

if it throws a ClassNotFoundException, it's not available. If you get the Class object, it is. You can then choose behaviour based on whether or not the class is available.

Jorn
+2  A: 

How about this? messy, but it ought to work:

public boolean exists(String className){

  try {
      Class.forName(className);
      return true;
      }
  catch (ClassNotFoundException){
      return false;
  }
}
Steve B.
A: 

I suggest compiling the majority of your code against your minimum target. Have code that uses particular optional libraries clearly separated, but dependent upon the bulk of your code. Dynamically load the code that uses optional libraries once. The main class should do something that checks for the presence of the required library/version in its static initialiser.

In the case of JNLP, your JNLP main class load the JNLP dependent code statically.

(Note that attempting to catch class loading related exceptions from normally linked code is unreliable.)

Tom Hawtin - tackline
Sorry a bit of a Java nub here really, can you throw me a link to some examples on the dynamic/static loading of classes.
Greg Domjan
A: 

getSystemClass loader was not useful for this purpose as there where multiple possible class loaders to interact with based on which applet the given window was in. The accessibility components being loaded at a more base class loader cannot see the applet specific classes.

To interact with the objects reflection does the job, though it does add so much more to maintain.

// statically linking would be
return component.getText();

// dynamically is
try {
  return (String)component.getClass().getMethod("getText", new Class [] {}).invoke(component, new Object [] {});
} catch (Throwable e) {
  e.printStackTrace();
}

The trickier bit is in writing a class derived from an interface that is not directly accessible, using the Proxy service allows this to be accomplished, providing the proxy service the applet specific class loader and the dynamically loaded class for the interface.

public void addListener(Container parent) {
  if (parent == null) { return; }
  if ("oracle.forms".equals(parent.getClass().getName())) {
    // Using the class loader of the provided object in the applet
    // get the "class" of the interface you want to implement
    Class desktopListenerClass = Class.forName( "oracle.DesktopListener"
         , true, parent.getClass().getClassLoader());

    // Ask the proxy to create an instance of the class, 
    // providing your implementation through the InvocationHandler::invoke
    Object desktopListener = Proxy.newProxyInstance(
         parent.getClass().getClassLoader()
         , new Class[] { desktopListenerClass }, new InvocationHandler() {

      public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
        if ("functionName".equals(method.getName())) {
          // do stuff
        }
        return null;
      }
    });

    // do something with your new object
    Method addDesktopListener = parent.getClass().getMethod("");
    addDesktopListener.invoke(parent, desktopListener);
  }
}

examples cut down to show general method

Greg Domjan