【问题标题】:how to make a dynamically loaded plugin web application-aware如何使动态加载的插件 Web 应用程序感知
【发布时间】:2011-08-28 06:41:18
【问题描述】:

我决定在我的 glassfish Web 应用程序中实现动态类加载,作为一种尝试的方式,并支持可以在运行时由 Web 应用程序加载和执行的小插件。

我添加了以下类:

public class PluginManager {

   private static final String dropBoxDir = "file:///path/to/dropbox/";
   private static final URLClassLoader dropBoxClassLoader;
   static {
      try {
         URL dropBoxURL = new URL(dropBoxDir);
         dropBoxClassLoader = URLClassLoader.newInstance(new URL[]{dropBoxURL});
      }
      catch (MalformedURLException mue) {
         throw new RuntimeException("MalformedURLException thrown during PluginManager initialization - the hardcoded URL " + dropBoxDir + " must be invalid.", mue);
      }
   }

   //this method is called by a web service
   public static void runPluginFromDropBox(String fullClassName) {
      try {
         //load the plugin class
         Class<?> pluginClass = dropBoxClassLoader.loadClass(fullClassName);
         //instantiate it
         Runnable plugin = (Runnable)pluginClass.newInstance();
         //call its run() method
         plugin.run();
      }
      catch (ClassNotFoundException cnfe) {
         throw new RuntimeException("The class file for " + fullClassName + " could not be located at the designated directory (" + dropBoxDir + "). Check that the specified class name is correct, and that its file is in the right location.", cnfe);
      }
      catch (InstantiationException ie) {
         throw new RuntimeException("InstantiationException thrown when attempting to instantiate the plugin class " + fullClassName + " - make sure it is an instantiable class with a no-arg constructor.", ie);
      }
      catch (IllegalAccessException iae) {
         throw new RuntimeException("IllegalAccessException thrown when attempting to instantiate the plugin class " + fullClassName + " - make sure the class and its no-arg constructor have public access.", iae);
      }
      catch (ClassCastException cce) {
         throw new RuntimeException("Plugin instance could not be cast to Runnable - plugin classes must implement this interface.", cce);
      }
   }
}

然后在一个单独的项目中,我创建了一个测试插件:

public class TestPlugin implements Runnable {
   @Override
   public void run() {
      System.out.println("plugin code executed");
   }
}

我部署了 Web 应用程序,然后将 TestPlugin 编译为 .class 文件并将其放入指定的文件夹中。我调用了一个 Web 服务,它使用类名点击 runPluginFromDropBox() 并获得了预期的输出。

这一切都作为概念证明,但我的插件实际上是无用的,除非它可以知道我的 Web 应用程序的类。我从那以后读到.war 仅用作独立应用程序,而不是用于其他库的类路径,这对于这个小项目来说并不是一个好兆头。

我看了这个讨论:Extending Java Web Applications with plugins,感觉我正无缘无故地陷入设计挑战的沼泽,应该转过身来。然而,那篇文章有点老了,而且是 Tomcat 特有的,所以我只是想问问是否有任何直接的方法可以让我在没有一些复杂的第三方框架的情况下解决这个问题。

【问题讨论】:

    标签: java plugins glassfish classloader war


    【解决方案1】:

    war 文件的类由特定的类加载器加载,以将 war 与部署在同一服务器上的其他 web 应用程序隔离开来,并能够取消部署 war。

    要了解 webapp 类,您的插件类加载器应该将 webapp 类加载器作为其父类。我不知道使用额外的类加载器可能会遇到的所有问题,但我怀疑当容器将取消部署并重新部署 webapp 时,您可能会遇到内存泄漏和其他令人讨厌的问题(内存中保存的静态值等)。你也可能在容器之间存在差异。

    【讨论】:

    • 当你说“插件类加载器”时,你是指我用来加载插件的URLClassLoader吗?该代码虽然在网络应用程序中。我想知道插件以某种方式在其类路径上发生战争有多难。因此,例如,如果网络应用程序有一个 Foo 类,我的插件可以 import mywebapp.Foo; 然后在其 run() 方法中与 Foo 交互 - 还是我天真地偏离了轨道?
    • 是的,我说的是用于加载插件类的类加载器。我的回答是这个类加载器应该把战争的类加载器作为它的父类加载器。查看 URLClassLoader 的构造函数。有一个以父类加载器作为参数。请注意,导入仅在编译时使用。在编译时,您只需设置适当的类路径即可访问 webapp 类。在运行时你需要战争的类加载器作为父类加载器。
    • 好的,我明白你在说父类加载器。但是我认为情况已经如此 - 除非我弄错了对newInstance() 的调用使用默认指定的ClassLoader 作为父级,这应该是网络应用程序的。所以我不确定为什么这是相关的,除非我遗漏了一些东西。我认为我的问题归结为插件库对 Web 应用程序类的编译时意识。我在这里遗漏了一些简单的东西 - 我可以把战争放在插件的类路径上吗?
    • newInstance 使用默认的 ClassLoader 作为父级。不是 webapp 的类加载器。有一个以父类加载器为参数的重载 newInstance 方法。是的,当然,在编译插件时,您需要插件项目类路径中的 webapp 类。不要将战争放在类路径中。将其 WEB-INF/classes 目录和 jars 放在 WEB-INF/lib 中。要根据另一个类编译一个类,您需要在类路径中使用这个其他类。对于 Java 编译器来说,这个其他类是否在运行时在 webapp 中使用并不重要。
    • 好吧,我没有意识到 webapp 的类加载器和默认的类加载器之间的区别。谢谢你对我的耐心。至于将您提到的目录/jar 放在类路径中,我无法弄清楚如何通过 Netbeans 进行操作,因为它不允许我访问战争的内容。不知道我是怎么解决这个问题的。
    猜你喜欢
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    • 2021-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多