如何链接到包,但只能在运行时根据包的存在/可用性执行操作?

时间:2009-04-15 15:16:45

标签: java user-interface accessibility

从跨应用程序/ applet java可访问性服务的角度来看,如何链接到一个包,但只能在运行时根据包的存在/可用性(已经加载)执行操作?

我认为我对此感兴趣的是一种解决class identity crisis的方法,而不是两个共享对象的应用程序之间的问题,而是在类加载器的更高级别加载的服务。 似乎反射是要走的路,但我不确定如何以及如何以这种方式实现派生类。我需要添加一个从特定可选类派生的特定侦听器,我可以使用applet类加载器加载侦听器,但内部仍然失败。假设你想添加一个JInternalFrameListener,但是不保证Swing可用,使用反射你可以找到添加监听器的方法,但是如果它找不到任何相关的类,你怎么能创建框架监听器呢?因为在基础类加载器中找不到它们!我是否需要创建一个线程并使用setContextClassLoader到知道swing的类加载器,以便我可以让类可靠地加载?只是尝试在我现有的线程上设置类加载器似乎不起作用。

问题的早期描述 对不起,我不太清楚要问什么,或者如何清楚地说明这一点,所以它有点摇摇欲坠。

假设某个类使用了另一个类的某些功能,但另一个类可能并不总是可用 - 比如说这是JNLP的网站,如果这是一个JNLP应用程序。

在某个阶段,我认为简单地编译JNLP意味着除非JNLP可用,否则我的类不会加载,因此为了识别这个可选部分,我只是在它周围包裹try{} catch( NoClassDefFoundError )

后来发生了一些变化(可能改变了jdk或者我不记得了)似乎我也应该使用try{} catch( ClassNotFoundException )

现在我想将这个想法扩展到其他可选功能,但它似乎并不一致。

假设我想添加一些功能,使用与JRE1.3中运行的相同的jar和类在JRE1.6运行时执行更高级的操作,或者说我想处理特定gui工具箱中的某些控件可能并不总是像SWT或oracle.forms一样使用。

有没有办法更可靠地做到这一点?导致异常并让它一直忽略它似乎是错误的。

当前的问题归结为能够针对oracle.forms进行编译,但是即使已经创建了oracle.forms包中的对象,ext中安装的辅助功能组件也无法访问oracle.forms类。如果我将frmall.jar放入ext目录进行测试,那么可访问性组件可以解决整个批次因为同一个软件包的不同版本而变得松散的问题。

我似乎陷入了类加载器不是正确的问题(??)的问题。我如何找到合适的人?

编辑: 到目前为止,答案很有意思,但并不能让我想到的地方。

对于gui组件,我目前以工厂的形式编译,如...

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() ); 
  }

打印出class oracle.forms.ui.FormDesktopContainer,然后使用NoClassDefFound调用instanceof上的抛出和异常,从而打印出oracle/forms/ui/FormDesktopContainer

那么它怎么能有一个类的实例但却找不到呢?

4 个答案:

答案 0 :(得分:2)

这个怎么样?凌乱,但它应该工作:

public boolean exists(String className){

  try {
      Class.forName(className);
      return true;
      }
  catch (ClassNotFoundException){
      return false;
  }
}

答案 1 :(得分:1)

您可以通过调用

来检查课程的可用性
ClassLoader.getSystemClassLoader().loadClass("my.package.MyClass")

如果它抛出ClassNotFoundException,则它不可用。如果你得到了Class对象,那就是。然后,您可以根据类是否可用来选择行为。

答案 2 :(得分:0)

我建议您针对最低目标编译大部分代码。使用特定可选库的代码清楚地分开,但取决于代码的大部分。动态加载使用可选库的代码一次。主类应该做一些事情来检查其静态初始化器中是否存在所需的库/版本。

对于JNLP,您的JNLP主类静态加载JNLP依赖代码。

(请注意,尝试从正常链接的代码中捕获类加载相关的异常是不可靠的。)

答案 3 :(得分:0)

getSystemClass加载器对此目的没用,因为有多个可能的类加载器根据给定窗口所在的applet进行交互。在更基类加载器上加载的辅助功能组件无法看到applet特定的类。 / p>

与对象进行交互反射可以完成工作,尽管它确实增加了很多维护。

// 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();
}

棘手的一点是编写一个派生自不能直接访问的接口的类,使用代理服务允许完成此操作,为代理服务提供applet特定的类加载器和接口的动态加载类。 / p>

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);
  }
}

示例缩减以显示一般方法