2010-06-08 16 views
5

En primer lugar, esto es Java 1.4 (restricciones de proyecto). Estoy tratando de crear un administrador de aplicaciones. Carga la clase principal de cada aplicación utilizando su propia instancia de un cargador de clases personalizado. Después de eso, crea una instancia de la clase principal usando reflexión. Cada aplicación implementa una interfaz común por lo que después de que se crea la instancia, ejecuta un método predefinido de la aplicación.ClassCastException al crear una instancia de una clase utilizando reflection y ClassLoaders

Sin embargo, estoy teniendo problemas en CRASH POINT 1 (ver código). La clase no se reconoce como una implementación de su interfaz. Si recibo este fragmento de código, obtengo ClassCastException en CRASH POINT 2.

Supongo que ambos errores están relacionados con el mismo problema (por supuesto).

¿Alguien me puede ayudar? La parte relevante del código sigue (se eliminan las importaciones) ...

Muchas gracias.

Marcus

// AppManager.java

public class AppManager { 
    public ThreadGroup threadGroup; 
    private Class appClass; 
    private AppInstance appInst; 
    public AppContextImpl context; 

    private AppManager(CustomClassLoader cl, String mainClass) throws ClassNotFoundException { 
     final String className = mainClass; 
     final CustomClassLoader finalLoader = cl; 

     appClass = cl.loadClass(mainClass); 

     // DEBUG CODE: 
     Class[] k1 = AppInstance.class.getInterfaces(); 
     System.out.println(k1.length + " interfaces for AppInstance.class:"); 
     for (int ii = 0; ii < k1.length; ii++) { 
      System.out.println(" " + ii + " - " + k1[ii].getName() + " (" + k1[ii].getClassLoader() + ")"); 
     } 

     Class[] k2 = appClass.getInterfaces(); 
     System.out.println(k2.length + " interfaces for appClass instance:"); 
     for (int ii = 0; ii < k2.length; ii++) { 
      System.out.println(" " + ii + " - " + k2[ii].getName() + " (" + k2[ii].getClassLoader() + ")"); 
     } 

     // CRASH POINT 1 
     if (!(AppInstance.class.isAssignableFrom(appClass))) { 
      throw new IllegalArgumentException("Attempt to run a non-AppInstance class: " + appClass); 
     } 

     context = new AppContextImpl(mainClass, this); 
     cl.setAppManager(this); 
     Constructor m; 
     try { 
      m = appClass.getConstructor(new Class[0]); 
      // CRASH POINT 2 
      appInst = (AppInstance) m.newInstance(new Object[0]); 
      appInst.init(context); 
     } catch (Exception e) { 
      System.out.println("Got ClassCastException here!\n"); 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 
     App app1; 

     String path1 = "/home/user/workspace/MultiTaskTest/bin/"; 
     String app1Name = "App1"; 

     Vector v1 = new Vector(); 
     try { 
      v1.add(new URL(path1)); 
     } catch (MalformedURLException e1) { 
      final File file1 = new File(path1); 
      try { 
       URL path1aux = (URL) AccessController.doPrivileged(
        new PrivilegedExceptionAction() { 
         public Object run() throws IOException { 
          if (!file1.exists()) { 
           System.out.println("Warning: \"" + file1.getPath() + "\" not found"); 
           return null; 
          } 
         return file1.toURI().toURL(); 
         } 
        }); 

       if (path1aux != null) { 
        v1.add(path1aux); 
       } 
      } catch (PrivilegedActionException e) { 
       e.getException().printStackTrace(); 
      } 
    } 

     final URL[] array1 = (URL[]) v1.toArray(new URL[v1.size()]); 
     CustomClassLoader cl1 = (CustomClassLoader) AccessController.doPrivileged(
      new PrivilegedAction() { public Object run() { 
       return new CustomClassLoader(array1); 
      }}); 
     System.out.println("ClassLoader 1 created: " + cl1); 
     try { 
      app1 = new App(cl1, app1Name); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
      System.out.println("Cannot find class App1."); 
     } 
    } 
} 

// AppInstance.java

public interface AppInstance { 
    public void init(ContextImpl context); 
} 

// App1.java

public class App1 implements AppInstance { 
    private AppContextImpl contextObj; 

    public void init(AppContextImpl context) { 
     this.contextObj = context; 
     System.out.println("Running App1..."); 
    } 
} 

// AppContextImpl.java

public class AppContextImpl { 
    public String mainClass; 
    public AppManager app; 

    public AppContextImpl(String mainClass, AppManager app) { 
     this.mainClass = mainClass; 
     this.app = app; 
    } 
} 

// CustomClassLoader.java

public class CustomClassLoader extends URLClassLoader { 
    AppManager appInst; 

    public CustomClassLoader(URL[] paths) { super(paths, null); } 
    public void setAppManager(AppManager app) { this.appInst = app; } 
} 

La salida para el código de depuración en el archivo AppManager.java es:

0 interfaces for AppInstance.class: 
1 interfaces for appClass instance: 
    0 - AppInstance ([email protected]) 
+1

parece que es un problema de cargador de clases. Debe asegurarse de que la clase que carga y la interfaz AppInstance misma se cargaron con el mismo cargador de clases, de lo contrario, Java considera que no tienen ninguna relación. –

+0

¿Cuál es la salida de Class.getGenericInterfaces() y Class.getInterfaces()? – TheLQ

+0

Lord.Quackstar: Vuelva a verificar la pregunta.He insertado algunos comentarios para proporcionar la información que solicitó. – Marcus

Respuesta

4

Su clase appInstance es, probablemente, cargado por separado por cada encargo cargador de clases Dado que los objetos de clase dependen de la clase real Y del cargador de clases, son realmente clases diferentes. Así appInstance del cargador de clase 1 no es lo mismo que appInstance del cargador de clases 2.

Lo que hay que hacer es usar la jerarquía del cargador de clases estándar: utilizar un cargador de clases raíz para la aplicación y masculina seguro de que appInstance se puede cargar por el cargador de clases . A continuación, cree sus hijos de carga de clases personalizados desde la raíz. Siempre que necesiten acceder a la clase AppInstance, usarán lo que se carga desde la raíz.

Así, en lugar de esto:

public CustomClassLoader(URL[] paths) { super(paths, null); } 

que necesita para dar un padre a su CustomClassLoader

+0

El aislamiento proporcionado por los cargadores de clase separados es exactamente el objetivo. Si configuré el cargador de clases principal para ellos, el aislamiento se rompería, ¿no? Investigaré un poco más sobre los cargadores de clases para asegurarme de no estar malinterpretando algo. Regresaré muy pronto Mientras tanto, gracias por su respuesta. – Marcus

+1

Debe decidir qué parte de su aplicación será el terreno común y qué parte será específica del módulo. Parece que estás tratando de reinventar OSGi, es posible que desees investigarlo;) – Guillaume

+0

He comprobado qué es OSGi. Lo que necesito hacer es solo eso. Sin embargo, no puedo usar OSGi porque mi aplicación se basa en una especificación pública definida que impone algunas restricciones. Necesito cargar cada aplicación usando su propio cargador de clases personalizado. Después de eso, mantendré un hilo de monitoreo en el terreno común. Las aplicaciones usarán una API especial para interactuar entre ellas. ¿Sabes si es posible resolver los problemas en CRASH POINT (1 y 2) sin configurar el cargador de clases principal para cada cargador de clases personalizado? Gracias de nuevo. – Marcus

Cuestiones relacionadas