2010-11-21 6 views
5

Contexte: Une nouvelle classe dit Bar, est injectée dans la JVM lors de l'exécution. Cette classe appartient à un package dit com.foo. Une référence à cette classe est injectée dans une autre classe appartenant au même package. La nouvelle classe peut avoir un nom différent chaque fois qu'elle est chargée. Elle ne peut donc pas être spécifiée dans le cadre d'un fichier de configuration - par ex. ne peut pas être spécifié dans build.xml pour être inclus dans un fichier jar. Au moment du chargement de la classe, jvm renvoie une erreur - java Résultat 1. Bien que je ne puisse pas déterminer de manière concluante la cause première, il semble que la classe nouvellement injectée ne soit pas trouvée par le chargeur de classe. Le serveur a été exécuté en mode verbeux qui affiche la liste des classes chargées par la JVM et cette classe nouvellement injectée est vue chargée.Définition du chemin de classe pour une classe nouvellement injectée

Question: La classe nouvellement injectée est-elle déjà dans le classpath? Si non, comment le définir? [Modifier] - ajouter du code à la question. Code segment - 1: Ce segment de code ci-dessous est appelé à partir de la méthode PreMain - La méthode Premain sera appelée par l'agent JVM et injectera la référence d'instrumentation lors de l'exécution. La méthode Premain injecte 1 nouvelle classe - Barre - et 1 référence à cette nouvelle classe à partir d'une méthode - returnsABool() - dans une classe existante - 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 
} 

code SEMENT 2: La méthode returnsABool() doit être injecté octets avec les commentaires lignes indiquées ci-dessous. Le code à byte injecter ceci est également appelé à partir de la méthode 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; 
    } 
} 

Code Byte injection pour ExistingClass - fait en utilisant la bibliothèque 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

Comment, spécifiquement, la classe est-elle "injectée"? Est-il créé dynamiquement et chargé par le chargeur de classe? –

+0

Un code java serait utile, sinon il est très difficile de vous aider. Aussi, peut-être que cette question est utile: http://stackoverflow.com/q/4210346/74694 –

+3

Montrer un exemple minimal démontrant le problème que vous avez. –

Répondre

1

je vous soupçonne avoir quelque chose de mal avec votre génération de bytecode, le code ASM suivant fonctionne pour moi:

 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(); 

Notez également que:

  • la façon dont vous comparez des chaînes sera très probablement conduire à des problèmes, vous devez utiliser str.equals(str2)
  • vous remplacez toute méthode, au lieu d'injecter votre code personnalisé au début (vos commentaires semblent indiquer que vous voulez injecter , au lieu de remplacer)
+0

Fantastique - Merci Neeme - ceci (utilisation d'égaux) a résolu le problème - leçon apprise à la dure! –

+0

Juste pour compléter la réponse, il semble que les classes injectées à l'exécution sont déjà dans le chemin de la classe. –

+0

quel outil avez-vous utilisé pour générer le code ci-dessus? –

Questions connexes