Skip navigation
3323 Views 26 Replies Latest reply: Aug 16, 2011 4:44 AM by Antony RSS
Antony Bronze 65 posts since
May 29, 2011
Currently Being Moderated

Jul 1, 2011 4:13 AM

How to use different log4j version in plugin

My plugin has a depency on a jar file that depends on log4j 1.3.  Even if I include the log4j 1.3 jar file in my plugin/lib directory I still get undefined method Logger.isTraceEnabled

 

Is there any way to separate out a plugin so that its classloader will find classes locally before delegating upwards.  I think it uses the JiveClassLoader, but that will just tag the jar/zip files from the lib directory onto the existing path and not allow the plugin classes to override those in the core lib directory.

    • Walter Ebeling KeyContributor 689 posts since
      Oct 25, 2006
      Currently Being Moderated
      Jul 4, 2011 12:58 AM (in response to Antony)
      How to use different log4j version in plugin

      There is an feature issue related to plugins http://issues.igniterealtime.org/browse/ SPARK-1311 You are welcome to provide some patches etc to Spark and engage in this with Mircea.

    • Mircea Carasel Bronze 90 posts since
      Apr 28, 2011
      Currently Being Moderated
      Jul 5, 2011 3:26 AM (in response to Antony)
      How to use different log4j version in plugin

      Antony,

       

      IMO, we should keep spark plugins as much as possible decoupled from the container (Spark in our case) If you want to load some jars from your plugin only for your plugin, then the plugin should create its own class loader, and not Spark.

       

      It may be another way that you can try in order to load your own xstream or log4j for your plugin, without making changes in spark code.

       

      Your findings are correct: all jars that are found inside the plugin's  /lib directory are loaded in the PluginClassLoader defined in Spark (PluginManager.java -> private PluginClassLoader classLoader;)

      So basically this class loader is more like a generic class loader that loads all plugins into spark.

      All other jars that may reside inside one plugin and are used only by the plugin, should be loaded by the plugin itself using its own class loader instance.

       

      I think that sparkplug API can be extended to make this easier.

       

      What I thing you can do is the following:

       

      1. create your own class loader instance inside your Plugin

      2. put the jars that you need to use (log4j, xstream, etc) in your plugin in a different directory than lib, lets say: privatelib directory. Since those jars are not in plugin's lib directory, they won't be loaded in the generic plugin class loader created in spark (PluginManager.java -> private PluginClassLoader classLoader;)

      3. during plugin creation, in the constructor of your plugin org.jivesoftware.spark.plugin.Plugin implementation for instance, load all jars from privatelib directory in your dedicated plugin class loader instance.

       

      Hope this helps,

      Mircea

        • Mircea Carasel Bronze 90 posts since
          Apr 28, 2011
          Currently Being Moderated
          Jul 6, 2011 2:06 PM (in response to Antony)
          Re: How to use different log4j version in plugin

          Hi Antony,

           

          Please see below

           

          [Antony]

          Requiring a Plugin writer to take on the intricacies of writing a custom ClassLoader makes writing Plugins too complex

          [Mircea]

          I totally agree, a Plugin writer should not do that, I was just thinking that we can extend current mechanism to automatically create a dedicated plugin class loader that will load plugin dependencies

           

          [Antony]

          I don't see any point in the current PluginClassLoader usage - in fact it's a really bad thing to lump all the plugin lib directories together and can lead to real support issues

          [Mircea]

          Well, I am not sure if this is bad or not, but IMHO there should be a way to make Spark container aware of what plugins are installed. Loading all plugins in the same class loader proves to be a solution... What this doesn't seem to resolve are plugin dependencies (other plugin jars), as you noticed.

          I think that there are three major aspects here:

           

          1. There is Spark - the plugin container

          2. There are plugins

          3. There are plugin dependencies.

           

          What we have a this point:

          For 1 - there is the the Spark class loader responsible for spark libraries loading

          For 2 - there is the Plugin class loader that loads all plugin jars (lib directories)

          In addition for point 3 What you need is to load plugin dependencies in dedicated class loaders for each plugin. All those class loaders should inherit the generic plugin class loader

           

          IMO, there is no need to beak the current functionality, removing the clas loader from point 2 I think will break  the plugin dependency concept (in plugin.xml you can put a dependency tag and make one plugin to be loaded after a parent plugin)

           

          I was looking on how Tomcat class loading takes place: The Overview Diagram:

          http://www.jajakarta.org/tomcat/tomcat3.2-4.0/tomcat-4.0b5/src/catalina/docs/dev /classloaders.html

           

          The plugin class loader created by spark (point 2) is somehow similar with "Shared" class loader from above diagram. What is missing right now are class loaders below Shared

           

          Mircea

              • Mircea Carasel Bronze 90 posts since
                Apr 28, 2011
                Currently Being Moderated
                Jul 14, 2011 5:09 AM (in response to Antony)
                How to use different log4j version in plugin

                Hi,

                I looked over the patch. Overall it looks OK, but when I tested it, I noticed that in case when we have plugins that depend on another plugin, so when one plugin require the loading of a dependency plugin first, there are problems, in a sense that the plugin that has to get loaded first, doesn't seem to be loaded properly.

                 

                If we have resources like properties files or images that are located in the plugin's jar and we want to access them right after Spark is started, before all UI is painted, are not found. Those plugin resources seem that are not loaded quick enough.

                    • Mircea Carasel Bronze 90 posts since
                      Apr 28, 2011
                      Currently Being Moderated
                      Aug 9, 2011 2:48 AM (in response to Antony)
                      How to use different log4j version in plugin

                      I applied the patches in the following way: I put PluginClassLoader changes from Plugins.patch and PluginManager changes from PM.patch and it looks good now.

                       

                      The only problem that I noticed is that it appears that when you have plugins that depend one of another, the jar files are loaded both in the shared class loader and the plugin classloader

                       

                      For instance, when you create a singleton and use this singleton both in a .java class of the plugin that contains this singleton, and also use this singleton in a .java class of a different plugin that depends on this plugin, I noticed that this singleton is created again.

                       

                      Please check the above behavior and also make sure that you create some static access methods in PluginManager that returns a plugin's classloader, being given the plugin name as parameter

                       

                      Also, please create a new patch that contains all changes and attach it on a separated task here:

                      SPARK-1311

                       

                      Thanks,

                      Mircea

                        • Mircea Carasel Bronze 90 posts since
                          Apr 28, 2011
                          Currently Being Moderated
                          Aug 15, 2011 1:06 AM (in response to Antony)
                          Re: How to use different log4j version in plugin

                          I opened ticket: SPARK-1424

                           

                          I also applied your patch but I think that something was lost during your merges, because after logging into Spark I see:

                           

                          log4j:ERROR A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable.

                          log4j:ERROR The class "org.apache.log4j.Appender" was loaded by

                          log4j:ERROR [org.jivesoftware.spark.plugin.PluginClassLoader@7cd37a] whereas object of type

                          log4j:ERROR "org.apache.log4j.ConsoleAppender" was loaded by [org.jivesoftware.launcher.JiveClassLoader@15c7850].

                          log4j:ERROR Could not instantiate appender named "S".

                           

                          I also created lib directory under plugins - but after spark starts this just disappears

                           

                          Other question is related to changes for PluginManager.loadPublicPlugin(File pluginDir) - it looks like that right now always returns null, but I think that it is supposed to be changed to void

                            • Mircea Carasel Bronze 90 posts since
                              Apr 28, 2011
                              Currently Being Moderated
                              Aug 15, 2011 1:20 PM (in response to Antony)
                              Re: How to use different log4j version in plugin

                              I applied the patch and for me it still wipes out plugins/lib directory. I am thinking to simplify things a bit

                              Lets just forget about plugins/lib or plugin/classes for common dependencies...

                              Lets use the shared classloader only for loading dependency plugins, other plugins to have their own class loader.

                               

                              So for instance if we have the following structure of plugins:

                               

                              Plugin A, Plugin B, Plugin C, Plugin D, Plugin E

                               

                              Plugin A and Plugin B depend on Plugin C

                              Plugin D depend on Plugin E

                               

                              Shared class loader will load Plugin C and Plugin E

                              Plugin A, B and D will have dedicated class loaders

                               

                              In this way we will also avoid to have things loaded twice - in the shared classloader and plugin dedicated class loader as well

                               

                              In other scenario for instance, if we have only plugins that do not depend one of another the shared class loader will not load anything..

                               

                              I am not sure if this is a doable proposal and if this scheme solves your original need with different version of log4j to be available in plugin. If this is doable, lets just do that, and think of another more complex scenario in a step 2. Please let me know what do you think

                               

                              Thanks,

                              Mircea

                                • Mircea Carasel Bronze 90 posts since
                                  Apr 28, 2011
                                  Currently Being Moderated
                                  Aug 16, 2011 2:46 AM (in response to Antony)
                                  Re: How to use different log4j version in plugin

                                  In the second patch it was added a test that cannot work:

                                  +    if (file.getName() == "lib" || file.getName() == "classes")

                                  +        continue;

                                  I think this is why it didn't work for me. Instead, it should have been: file.getName().equals("classes")

                                   

                                  Anyway - so lets forget about plugins/classes, plugins/lib, common directories or any other paths. I made a change on your first patch where I loaded dependency plugins in the shared classloader, and only for other plugins I created dedicated class loaders

                                   

                                  So, in scenario: Plugin A, Plugin B, Plugin C, Plugin D, Plugin E, Plugin F

                                  Plugin A and plugin B have a dependency on Plugin C

                                  Plugin D has a dependency on plugin E

                                  Plugin F does not have any dependencies

                                   

                                  will have separate class loaders for Plugin A, Plugin B, Plugin D, Plugin F, added in your hashMap for plugins class loaders

                                  Plugin C and Plugin E will be loaded in the shared class loader - lets accept the limitation that those plugins will not have common classes - this would have been a problem for every plugin right now when we have one single class loader

                                   

                                  Given the Fact that the shared class loader is parent for all dedicated classloaders that are loaded in the hashMap we are OK, every plugin works.

                                  Also, the case when a singleton is present in a dependency plugin and used in other plugins is resolved.

                                   

                                  The error with log4j class loading is anyway encountered only when you have plugins that contain the log4j library, because log4j library is present in spark lib also, I think we can live with this. Plugins should not contain the same version of log4j as spark does. We better update spark lib directory with the latest version of log4j

                                   

                                  Please review if my changes are ok with you. If there are OK, I will recommend for the patch to be commited in spark svn.

                                   

                                  I only performed changes in your PluginManager.private void createPluginClasses() method

                                   

                                  The attached patch contains your first (SparkPlugin-110815.patch) patch + my changes

                                  Attachments:

More Like This

  • Retrieving data ...

Bookmarked By (0)

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points