3 Replies Latest reply: Jun 16, 2010 2:05 AM by 843798 RSS

    Custom ClassLoader

    843798
      I'm trying to use a custom ClassLoader to instantiate a class with a constructor that has one argument. the part of code that does the job is:
      String className = /* SOME INITIALIZATION HERE */;
      
      final Class<?> myClass = new BaseLoader().loadClass(name, true);
      
      final Class[] types = new Class[] { AbstractCommunicationFactory.class };
      final Constructor con = myClass.getConstructor(types);
      
      final Object[] args = new Object[] { this.communicator };
      final IGuiAbstractFactory ts = (IGuiAbstractFactory)con.newInstance(args);
      First problem: the myClass.getConstructor(types) throws a NoSuchMethodException but the myClass.getConstructors() has such constructor in the returned array. So... why it cannot find it?

      Then: suppose I can find the constructor by checking in the array if a constructor has a paramater with the same canonical name as AbstractCommunicationFactory.class.
      final Constructor con = myClass.getConstructors()[0];
      In this situation, the constructor is the correct one (with the parameters) but the newInstance(args) method throws a IllegalArgumentException.

      If I use
      final Class<?> myClass = Class.forName(name);
      instead of using the BaseLoader, everything works...

      Any idea?

      My BaseLoader is
      /*
       * [OMISSIS]
       * BaseLoader.java
       *
       * Version 2010-05-29
       *
       * Copyright © 2010 [OMISSIS]
       * All rights reserved
       * [OMISSIS] Proprietary information
       * Contains proprietary/trade secret information which is the property of
       * [OMISSIS] and must not be made available to, or copied or used by anyone
       * outside [OMISSIS] without its written authorization.
       */
      
      
      package [OMISSIS].loader;
      
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      
      //~--- non-JDK imports ---------------------------------------------------------
      
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      
      //~--- classes -----------------------------------------------------------------
      
      /**
       * Class description.
       *
       * @author Marco Bresciani, Marco.Bresciani@[OMISSIS]
       * @version 2010-04-27
       */
      public class BaseLoader extends ClassLoader {
      
          /**
           * Field LOG_BASE description.
           */
          private static final Logger LOG_BASE =
              LoggerFactory.getLogger(BaseLoader.class);
      
          /**
           * Field BUFFER_SIZE description.
           */
          private static final int BUFFER_SIZE = 8192;
      
          //~--- methods -------------------------------------------------------------
      
          /**
           * Method loadClass description.
           *
           * @param pClassName
           * @param pResolve
           * @return
           * @throws ClassNotFoundException
           */
          @Override
          public Class<?> loadClass(final String pClassName,
                                       final boolean pResolve)
                  throws ClassNotFoundException {
      
              /* variables definition */
              Class<?> cls = null;
      
              BaseLoader.LOG_BASE.trace("Loading class: {}, resolve: {}",
                                        new Object[] { pClassName,
                      pResolve });
      
              // 1. is this class already loaded?
              synchronized (this) {
                  cls = this.findLoadedClass(pClassName);
              }
      
              if (cls == null) {
      
                  // 2. get class file name from class name
                  final String clsFile = pClassName.replace('.', '/') + ".class";
      
                  // 3. get bytes for class
                  byte[] classBytes = null;
      
                  try {
      
                      /* block variables */
                      final InputStream input = getResourceAsStream(clsFile);
                      final byte[] buffer = new byte[BaseLoader.BUFFER_SIZE];
                      final ByteArrayOutputStream output =
                          new ByteArrayOutputStream();
      
                      /* loading loop */
                      for (int n = input.read(buffer, 0, BaseLoader.BUFFER_SIZE);
                              n != -1;
                              n = input.read(buffer, 0, BaseLoader.BUFFER_SIZE)) {
                          output.write(buffer, 0, n);
                      }
      
                      classBytes = output.toByteArray();
                  } catch (IOException e) {
                      BaseLoader.LOG_BASE.trace(e.getLocalizedMessage(), e);
                  }
      
                  if (classBytes == null) {
                      throw new ClassNotFoundException("Cannot load class: "
                                                       + pClassName);
                  }
      
                  // 4. turn the byte array into a Class
                  try {
                      cls = this.defineClass(pClassName, classBytes, 0,
                                             classBytes.length);
      
                      if (pResolve) {
                          this.resolveClass(cls);
                      }
                  } catch (SecurityException e) {
                      BaseLoader.LOG_BASE.trace(e.getLocalizedMessage(), e);
      
                      // loading core java classes such as java.lang.String
                      // is prohibited, throws java.lang.SecurityException.
                      // delegate to parent if not allowed to load class
                      cls = super.loadClass(pClassName, pResolve);
                  }
              }
      
              return cls;
          }
      }
        • 1. Re: Custom ClassLoader
          843798
          This
                          /* class loading */
                          final Class<?> myClass = Class.forName(name);
          //                final Class<?> myClass = new BaseLoader().loadClass(name, true);
          
                          /* constructor search */
                          final Class<?>[] types =
                              new Class<?>[] { AbstractCommunicationFactory.class };
                          final Constructor<?> con = myClass.getConstructor(types);
          
                          /* class instantiation */
                          final Object[] args = new Object[] { this.communicator };
          
                          result = (IGuiAbstractFactory) con.newInstance(args);
          works. But, as I said, if I use the custom ClassLoader, it does not work anymore, in two steps.
          • 2. Re: Custom ClassLoader
            EJP
            You probably need to obtain the AbstractXXXFactory class via the same classloader.
            • 3. Re: Custom ClassLoader
              843798
              So, something like:
              final Class<?> myAbstract = new BaseLoader().loadClass("mypackage.AbstractCommunicationFactory", true);
              final Class[] types = new Class[] { myAbstract };
              instead of
              final Class<?> myClass = new BaseLoader().loadClass(name, true);
              final Class[] types = new Class[] { AbstractCommunicationFactory.class };
              Ok. I'll check.