1 2 Previous Next 17 Replies Latest reply on Oct 9, 2008 4:54 AM by Bernd Ruecker

    jBPM Classloading

    Bjoern Wand Newbie

      Hi,
      we are using jBPM in conjunction with OSGi on jBoss Appserver and had problems with the classloading of jBPM. So we looked at the Source Code of ClassLoaderUtil.java and found out that you are actually not using the context class loader to for classloading, which was the cause for our problems. For my understanding this is common practice for libraries used in J2EE Environments. There already is a comment in current ClassLoaderUtil implementation stating that class loading could be made configurable:

       public static ClassLoader getClassLoader() {
       // if this is made configurable, make sure it's done with the
       // jvm system properties
       // System.getProperty("jbpm.classloader")
       // - 'jbpm'
       // - 'context'
       //
       // or something like Thread.currentThread().getContextClassLoader();
       return ClassLoaderUtil.class.getClassLoader();
       }
      


      We implemented a patch that reflects the proposal of this comment. It enables jBPM to use the context class loader or to plug in a 'custom' classloader whose class name is defined by another system property. Classloading with the current classloader is the default, if no system property is set. We would like to provide this code to the community. Please give me feedback what you think about this.

        • 1. Re: jBPM Classloading
          Fady Matar Novice

          Can you post the code?


          It enables jBPM to use the context class loader or to plug in a 'custom' classloader whose class name is defined by another system property.

          We might need to look at alternatives to specify the classloader

          • 2. Re: jBPM Classloading
            Bjoern Wand Newbie

            The code looks like this:

            public static final String JBPM_CLASSLOADER = "jbpm.classloader";
            public static final String JBPM_CLASSLOADER_CLASSNAME = "jbpm.classloader.classname";
            
            ...
            
            public static ClassLoader getClassLoader() {
             if (System.getProperty(JBPM_CLASSLOADER).equals("jbpm")) {
             return ClassLoaderUtil.class.getClassLoader();
             } else if (System.getProperty(JBPM_CLASSLOADER).equals("context")) {
             return Thread.currentThread().getContextClassLoader();
             } else if (System.getProperty(JBPM_CLASSLOADER).equals("custom")) {
             try {
             String customClassLoaderClassName = System.getProperty(JBPM_CLASSLOADER_CLASSNAME);
             if (customClassLoaderClassName == null) {
             throw new JbpmException(
             "'jbpm.classloader' system property set to 'custom' but 'jbpm.classloader.classname' is empty!");
             }
             return (ClassLoader) Thread.currentThread().getContextClassLoader().loadClass(
             customClassLoaderClassName).newInstance();
             } catch (InstantiationException e) {
             throw new JbpmException("Error instantiating custom classloader "
             + System.getProperty(JBPM_CLASSLOADER_CLASSNAME), e);
             } catch (IllegalAccessException e) {
             throw new JbpmException("Error accessing custom classloader "
             + System.getProperty(JBPM_CLASSLOADER_CLASSNAME), e);
             } catch (ClassNotFoundException e) {
             throw new JbpmException("Custom classloader "
             + System.getProperty(JBPM_CLASSLOADER_CLASSNAME) + " not found ", e);
             }
             } else {
             return ClassLoaderUtil.class.getClassLoader();
             }
            }
            



            • 3. Re: jBPM Classloading
              Bjoern Wand Newbie

              Hi,
              can someone state a comment to the issue. I would like to know what you think about the proposal and how to proceed?

              • 4. Re: jBPM Classloading
                Tom Baeyens Master

                good solution, but the configuration mechanism proposed is not in sync with the jbpm configuration.

                please propose the same kind of configuration but based on JbpmConfiguration.Configs.getProperty (or something like that. i don't remember it by heart) and verify that the test suite still works.

                then we can take it in.

                • 5. Re: jBPM Classloading
                  Tom Baeyens Master

                  koen, can you monitor this and take it in ?

                  • 7. Re: jBPM Classloading
                    Bjoern Wand Newbie

                    So, what would be the next steps for me to do?

                    • 8. Re: jBPM Classloading
                      Bjoern Wand Newbie

                      I will need CVS access to make the changes?!

                      • 9. Re: jBPM Classloading
                        Ronald van Kuijk Master

                        not specifically... if you make the changes in the code and post them here or attach them to a jira issue that is fine to. CVS access is only needed if you want real committer access for more than one patch

                        • 10. Re: jBPM Classloading
                          Bjoern Wand Newbie

                          Hi, I have refactored the code. Now it uses the JbpmConfiguration.Configs class to get the properties. I paste the complete ClassLoaderUtil class.

                          
                          package org.jbpm.util;
                          
                          import java.io.IOException;
                          import java.io.InputStream;
                          import java.util.Properties;
                          
                          import org.apache.commons.logging.Log;
                          import org.apache.commons.logging.LogFactory;
                          import org.jbpm.JbpmConfiguration;
                          import org.jbpm.JbpmException;
                          import org.jbpm.graph.def.ProcessDefinition;
                          import org.jbpm.instantiation.ProcessClassLoader;
                          
                          /**
                           * provides centralized classloader lookup.
                           */
                          public class ClassLoaderUtil {
                           private static Log log = LogFactory.getLog(ClassLoaderUtil.class);
                          
                           public static Class loadClass(String className) {
                           try {
                           return getClassLoader().loadClass(className);
                           } catch (ClassNotFoundException e) {
                           throw new JbpmException("class not found '" + className + "'", e);
                           }
                           }
                          
                           public static ClassLoader getClassLoader() {
                           if (JbpmConfiguration.Configs.hasObject("jbpm.classloader")) {
                           String jbpmClassloader = JbpmConfiguration.Configs.getString("jbpm.classloader");
                           String jbpmClassloaderClassname = JbpmConfiguration.Configs.getString("jbpm.classloader.classname");
                          
                           if (jbpmClassloader.equals("jbpm")) {
                           return ClassLoaderUtil.class.getClassLoader();
                           } else if (jbpmClassloader.equals("context")) {
                           return Thread.currentThread().getContextClassLoader();
                           } else if (jbpmClassloader.equals("custom")) {
                           try {
                           if (jbpmClassloaderClassname == null) {
                           throw new JbpmException(
                           "'jbpm.classloader' property set to 'custom' but 'jbpm.classloader.classname' is empty!");
                           }
                           return (ClassLoader) Thread.currentThread().getContextClassLoader().loadClass(
                           jbpmClassloaderClassname).newInstance();
                           } catch (InstantiationException e) {
                           throw new JbpmException("Error instantiating custom classloader " + jbpmClassloaderClassname, e);
                           } catch (IllegalAccessException e) {
                           throw new JbpmException("Error accessing custom classloader " + jbpmClassloaderClassname, e);
                           } catch (ClassNotFoundException e) {
                           throw new JbpmException("Custom classloader " + jbpmClassloaderClassname + " not found ", e);
                           }
                           } else {
                           throw new JbpmException("'jbpm.classloader' property set to '" + jbpmClassloader
                           + "' but only the values 'jbpm'/'context'/'custom' are supported!");
                           }
                           } else {
                           return ClassLoaderUtil.class.getClassLoader();
                           }
                           }
                          
                           public static InputStream getStream(String resource) {
                           return getClassLoader().getResourceAsStream(resource);
                           }
                          
                           public static Properties getProperties(String resource) {
                           Properties properties = new Properties();
                           try {
                           properties.load(getStream(resource));
                           } catch (IOException e) {
                           throw new JbpmException("couldn't load properties file '" + resource + "'", e);
                           }
                           return properties;
                           }
                          
                           /**
                           * searches the given resource, first on the root of the classpath and if
                           * not not found there, in the given directory. public static InputStream
                           * getStream(String resource, String directory) { InputStream is =
                           * getClassLoader().getResourceAsStream(resource); if (is==null) { is =
                           * getClassLoader().getResourceAsStream(directory+"/"+resource); } return
                           * is; }
                           *
                           * public static Properties getProperties(String resource, String directory) {
                           * Properties properties = new Properties(); try {
                           * properties.load(getStream(resource, directory)); } catch (IOException e) {
                           * throw new JbpmException("couldn't load properties file '"+resource+"'",
                           * e); } return properties; }
                           */
                          
                           public static ClassLoader getProcessClassLoader(ProcessDefinition processDefinition) {
                           return new ProcessClassLoader(getClassLoader(), processDefinition);
                           }
                          
                          }
                          


                          Since this is my only patch I will not need committers access. Koen you will take care of this patch? Does this match your expectations?

                          • 11. Re: jBPM Classloading
                            Koen Aers Master

                            Can you attach the code to a JIRA issue to make sure that we don't forget?

                            Thanks,
                            Koen

                            • 12. Re: jBPM Classloading
                              Bjoern Wand Newbie

                              Hi Koe, I opened a new JIRA for this issue with key: JBPM-1148

                              https://jira.jboss.org/jira/browse/JBPM-1148

                              • 13. Re: jBPM Classloading
                                Bernd Ruecker Master

                                Hi Bjoern,

                                did you use the proposed solution in your project?

                                I wanted to use it for an own problem with classloading (and I can also commit it back to jbpm after it works correctly) but have the problem, that this is leading to and endless loop, since the classloader is already needed to load the jbpm.cfg.xml in the JbpmConfiguration (InputStream jbpmCfgXmlStream = ClassLoaderUtil.getStream(resource);).

                                Exception StackTrace:

                                 at org.jbpm.util.ClassLoaderUtil.getClassLoader(ClassLoaderUtil.java:50)
                                 at org.jbpm.util.ClassLoaderUtil.getStream(ClassLoaderUtil.java:83)
                                 at org.jbpm.JbpmConfiguration.getInstance(JbpmConfiguration.java:279)
                                 at org.jbpm.JbpmConfiguration.getInstance(JbpmConfiguration.java:257)
                                 at org.jbpm.JbpmConfiguration$Configs.getObjectFactory(JbpmConfiguration.java:418)
                                 at org.jbpm.JbpmConfiguration$Configs.hasObject(JbpmConfiguration.java:426)
                                 at org.jbpm.util.ClassLoaderUtil.getClassLoader(ClassLoaderUtil.java:50)
                                


                                How did you solve this?

                                Thanks
                                Bernd


                                • 14. Re: jBPM Classloading
                                  Bjoern Wand Newbie

                                  Hi Bernd,

                                  sorry for my late answer. I was on vacation...

                                  We used a much more simplified solution whitout the JbpmConfiguration Class. We used System.getProperty to make things configurable. You can see it in a previous post of this thread.

                                  The guys from jBPM wanted us to use the JbpmConfiguration Class for the patch. Unfortunately is seems that this does not work. :-(

                                  I also saw your and Toms comment in the corresponding jira and I think that this will solve the original problem.

                                  1 2 Previous Next