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

    jBPM Classloading

    bwand

      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

          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
            bwand

            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
              bwand

              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

                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

                  koen, can you monitor this and take it in ?

                  • 6. Re: jBPM Classloading
                    koen.aers

                    Yes

                    • 7. Re: jBPM Classloading
                      bwand

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

                      • 8. Re: jBPM Classloading
                        bwand

                        I will need CVS access to make the changes?!

                        • 9. Re: jBPM Classloading
                          kukeltje

                          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
                            bwand

                            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

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

                              Thanks,
                              Koen

                              • 12. Re: jBPM Classloading
                                bwand

                                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
                                  camunda

                                  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
                                    bwand

                                    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