2010-11-21 7 views
5

Contesto: Una nuova classe dice Bar, viene iniettata nella JVM in fase di esecuzione. Questa classe appartiene a un pacchetto dice com.foo. Un riferimento a questa classe viene iniettato in un'altra classe che appartiene allo stesso pacchetto. La nuova classe può avere un nome diverso ogni volta che viene caricata, quindi non può essere specificata come parte di alcun file di configurazione, ad es. non può essere specificato in build.xml per essere incluso come parte di un file jar.impostazione del percorso di classe per una classe di nuova iniezione

Problema: Al momento del caricamento della classe, JVM genera un errore - java Risultato 1. Anche se non posso definitivamente determinare la causa principale, sembra che la classe appena iniettato non viene trovato da class loader. Il server è stato eseguito in modalità dettagliata che mostra l'elenco delle classi caricate dalla JVM e questa classe appena iniettata viene vista caricata.

Domanda: La classe appena iniettata è già nel classpath? Se non come impostarlo?

[Modifica] - aggiunta di codice alla domanda.

Segmento di codice - 1: questo segmento di codice di seguito viene chiamato dal metodo PreMain - Il metodo Premain verrà chiamato dall'agente JVM e inietterà il riferimento della strumentazione in fase di esecuzione. Il metodo Premain inietta 1 nuova classe - Barra - e 1 riferimento a questa nuova classe da un metodo - returnsABool() - in una classe esistente - ExistingClass.

public static void premain(String agentArgs, Instrumentation inst) { 

     // 1. Create and load the new class - Bar 
     String className = "Bar"; 
     byte [] b = getBytesForNewClass(); 
     //override classDefine (as it is protected) and define the class. 
     Class clazz = null; 
     try { 
      ClassLoader loader = ClassLoader.getSystemClassLoader(); 
      Class cls = Class.forName("java.lang.ClassLoader"); 
      java.lang.reflect.Method method = 
      cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class }); 
      // protected method invocation 
      method.setAccessible(true); 
      try { 
      Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)}; 
      clazz = (Class) method.invoke(loader, args); 
      } finally { 
      method.setAccessible(false); 
      } 
     } catch (Exception e) { 
     System.err.println(
      "AllocationInstrumenter was unable to create new class" + e.getMessage()); 
     e.printStackTrace(); 
     } 

     // 2. Inject some lines of code into the returnsABool method in ExistingClass class that references Bar 
     inst.addTransformer(new CustomInstrumenter(), true); 

     // end of premain method 
} 

Codice Sement 2: Il metodo returnsABool() deve essere byte-iniettati con le commentati linee mostrate sotto. Il codice per iniettare il byte viene anche chiamato dal metodo PreMain.

public class ExistingClass{ 

    public static boolean returnsABool() { 
    // Code within comments is byte-injected, again as part of the pre-main method 

    /* 
    String str = Bar.get(); 
    if (str != "someValue") { 
     return true; 
    } 
    */ 

     return false; 
    } 
} 

Byte iniezione di codice per ExistingClass - fatto utilizzando Biblioteca ASM

{ 
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); 
    mv.visitCode(); 
    Label l0 = new Label(); 
    mv.visitLabel(l0); 
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");   
    mv.visitLdcInsn("some constant here"); 
    Label l1 = new Label(); 
    mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1); 
    mv.visitInsn(Opcodes.ICONST_0); Label l2 = new Label(); 
    mv.visitJumpInsn(Opcodes.GOTO, l2); 
    mv.visitLabel(l1); 
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 
    mv.visitInsn(Opcodes.ICONST_1); 
    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER}); 
    mv.visitInsn(Opcodes.IRETURN); 
    mv.visitMaxs(2, 0); 
    mv.visitEnd(); 
} 
+0

In che modo, in particolare, la classe viene "iniettata"? Viene creato e caricato dinamicamente dal classloader? –

+0

Qualche codice java sarebbe utile, altrimenti è molto difficile aiutarti. Inoltre, forse questa domanda è utile: http://stackoverflow.com/q/4210346/74694 –

+3

Mostra un esempio minimo che dimostra il problema che hai. –

risposta

1

mi avrebbe sospettato di avere qualcosa di sbagliato con la vostra generazione bytecode, il seguente codice ASM funziona per me:

 mv.visitCode(); 
     Label l0 = new Label(); 
     mv.visitLabel(l0); 
     mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;"); 
     Label l1 = new Label(); 
     mv.visitLdcInsn("some constant here"); 
     mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1); 
     mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 
     mv.visitInsn(Opcodes.ICONST_1); 
     mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER}); 
     mv.visitInsn(Opcodes.IRETURN); 
     mv.visitLabel(l1); 
     mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); 
     mv.visitInsn(Opcodes.ICONST_0); 
     mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER}); 
     mv.visitInsn(Opcodes.IRETURN); 
     mv.visitMaxs(2, 1); 
     mv.visitEnd(); 

Si noti inoltre che:

  • il modo in cui si confrontano le stringhe sarà molto probabilmente portare a problemi, è necessario utilizzare str.equals(str2)
  • si sta sostituendo l'intero metodo, invece di iniettare il codice personalizzato in principio (i tuoi commenti sembrano indicare che si desidera iniettare , invece di sostituire)
+0

Fantastico - Grazie a Neeme - questo (uso di uguali) ha risolto il problema: la lezione appresa nel modo più difficile! –

+0

Solo per completare la risposta, sembra che le classi immesse in fase di esecuzione siano già nel percorso classe. –

+0

quale strumento hai usato per generare il codice sopra? –