2010-06-08 4 views
5

Prima di tutto, questo è Java 1.4 (limitazioni del progetto). Sto provando a creare un gestore applicazioni. Carica la classe principale di ogni applicazione utilizzando la propria istanza di un classloader personalizzato. Successivamente, crea un'istanza della classe principale utilizzando la riflessione. Ogni applicazione implementa un'interfaccia comune, quindi dopo aver creato l'istanza, esegue un metodo predefinito dell'applicazione.ClassCastException durante la creazione di un'istanza di una classe utilizzando reflection e ClassLoaders

Tuttavia, sto riscontrando dei problemi in CRASH POINT 1 (vedere codice). La classe non è riconosciuta come un'implementazione della sua interfaccia. Se inserisco questo chunk di codice, ottengo ClassCastException in CRASH POINT 2.

Suppongo che entrambi gli errori siano correlati allo stesso problema (ovviamente).

Qualcuno può aiutarmi? Segue la parte pertinente del codice (le importazioni vengono rimosse) ...

Grazie mille.

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; } 
} 

L'uscita per il codice di debug nel file AppManager.java è:

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

sembra che sia un problema di class loader. È necessario assicurarsi che la classe che si carica e l'interfaccia di AppInstance stessa siano stati caricati con lo stesso programma di caricamento classe, altrimenti Java li considera totalmente indipendenti. –

+0

Che cosa è l'output di Class.getGenericInterfaces() e Class.getInterfaces()? – TheLQ

+0

Lord.Quackstar: Controlla di nuovo la domanda.Ho inserito alcuni commenti per fornire le informazioni richieste. – Marcus

risposta

4

La classe AppInstance probabilmente è caricata separatamente da ciascun personalizzato classloader. Poiché gli oggetti di classe dipendono dalla classe effettiva AND sul classloader, sono classi molto diverse. Così AppInstance dal classloader 1 non è lo stesso di AppInstance dal classloader 2.

Quello che dovete fare è utilizzare la gerarchia dei classloader di serie: utilizzare un classloader principale per l'applicazione, e maschile in modo che AppInstance è caricabile dal classloader . Quindi crea i tuoi figli personalizzati del classloader dalla radice. Ogni volta che hanno bisogno di accedere alla classe AppInstance, useranno ciò che viene caricato dalla root.

Così, invece di questo:

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

È necessario dare un genitore al tuo CustomClassLoader

+0

L'isolamento fornito da classloader separati è esattamente l'obiettivo. Se imposto il genitore di classi genitore per loro, l'isolamento sarebbe rotto, ferito? Analizzerò un po 'di più sui classloader per assicurarmi di non fraintendere qualcosa. Tornerò molto presto. Nel frattempo, grazie per la tua risposta. – Marcus

+1

È necessario decidere quale parte della propria applicazione sarà il terreno comune e quale parte sarà specifica del modulo. Sembra che tu stia cercando di reinventare OSGi, potresti volerlo esaminare;) – Guillaume

+0

Ho controllato che cos'è OSGi. Quello che devo fare è proprio questo. Tuttavia, non posso utilizzare OSGi perché la mia applicazione si basa su una specifica definita pubblica che impone alcune restrizioni. Ho bisogno di caricare ogni applicazione utilizzando il proprio classloader personalizzato. Dopodiché terrò un thread di monitoraggio nel terreno comune. Le app utilizzano un'API speciale per interagire tra loro. Sai se è possibile risolvere i problemi nei CRASH POINT (1 e 2) senza impostare il classloader genitore per ogni classloader personalizzato? Grazie ancora. – Marcus