This discussion is archived
3 Replies Latest reply: Jun 16, 2010 12:05 AM by 843798 RSS

Custom ClassLoader

843798 Newbie
Currently Being Moderated
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 Newbie
    Currently Being Moderated
    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 Guru
    Currently Being Moderated
    You probably need to obtain the AbstractXXXFactory class via the same classloader.
  • 3. Re: Custom ClassLoader
    843798 Newbie
    Currently Being Moderated
    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.