簡體   English   中英

Java代理無法轉換項目中的所有類

[英]Java agent cannot transform all the classes in my project

長話短說:

  • 我需要轉換程序中的每個類(甚至是java
    在我的代理之前加載的庫)。
  • 我找到了一種方法,但並不完全正常工作。 我對新想法持開放態度。
  • 我的實際方法很奇怪:它應該在文件和控制台中打印相同的名稱,但它們不是。 我確信那些類到達了我的變換方法,因為如果我試圖檢測它們,我會收到錯誤。

完整的故事:我創建了一個代理,以便在每個加載到我的項目中的類中注入一些自定義代碼。 我在運行時使用選項-javaagent添加此代理,這工作正常。

問題是,當我的代理連接到JVM時,已經加載了很多類(例如java.io. *類),所以我的轉換錯過了一大堆類。 所以我的問題是:有一種方法可以記錄我所缺少的所有課程嗎? 每個建議都更受歡迎。

現在我試過這樣的事:

public class MyTransformer implements ClassFileTransformer {


 public static void premain(String agentArgs, Instrumentation inst) {
    final MyTransformer  t = MyTransformer .getInstance();
    inst.addTransformer(t, true);
    MyTransformer .log.info(t + " registered via JVM option -javaagent");

    // TEST
    // by the time we are attached, the classes to be
    // dumped may have been loaded already. So, check
    // for candidates in the loaded classes.
    Class[] classes = inst.getAllLoadedClasses();
    List<Class> candidates = new ArrayList<Class>();
    for (Class c : classes) {
        if (inst.isModifiableClass(c) && inst.isRetransformClassesSupported()){
            candidates.add(c);
        }
    }
    System.out.println("There are "+candidates.size()+" classes");
    try {
        // if we have matching candidates, then
        // retransform those classes so that we
        // will get callback to transform.
        if (! candidates.isEmpty()) {
            Iterator it = candidates.iterator();
            while(it.hasNext()){
                Class c = (Class)it.next();
                if(!c.getName().startsWith("javassist")){
                    System.out.println(" ========================> In Progress:"+c.getName());
                    inst.retransformClasses(c);
                }
            }
        }else{
            System.out.println("candidates.isEmpty()");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
  }

  public byte[] transform(ClassLoader loader, String className,
        Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
        byte[] classfileBuffer) throws IllegalClassFormatException {

    byte[] byteCode = classfileBuffer;

    final String dot_classname    = className.replace('/', '.');
    // log classes that are going throug the instrumentor
    // just log if the java.io. is coming here
    try {
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path, true)));
        out.println(dot_classname);
        out.close();
     } catch (IOException ex) {
        Logger.getLogger(MyTransformer.class.getName()).log(Level.SEVERE, null, ex);
     }
     return byteCode;
  }
}

所以我的第一次嘗試是接受已經加載的每個類的“premain”方法,如果可以使用方法“retransfromClasses”從它進行reTrans。

現在在我的轉換方法中,我只有一個日志,可以將每個要轉換的類寫入文件。 現在我的測試結果如下:

There are 1486 classes  
==> In Progress:com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser
==> In Progress:java.lang.Enum  
==> In Progress:java.lang.ThreadGroup  
==> In Progress:java.nio.file.FileSystem  
==> In Progress:java.util.regex.Pattern$Prolog  
==> In Progress:com.sun.org.apache.xerces.internal.dom.AttributeMap 
==> In Progress:java.util.regex.Matcher  
==> In Progress:org.apache.commons.beanutils.converters.ShortConverter 
==> In Progress:com.google.gson.JsonNull 
==> In Progress:java.util.concurrent.CopyOnWriteArrayList$COWIterator 
==> In Progress:java.util.concurrent.locks.ReentrantLock 
==> In Progress:java.lang.NoSuchMethodError 
==> In Progress:org.apache.commons.lang.BooleanUtils 
==> In Progress:java.lang.reflect.WeakCache$CacheValue 
==> In Progress:com.google.gson.internal.bind.TypeAdapters$33 
==> In Progress:java.lang.reflect.Type 
==> In Progress:sun.reflect.generics.scope.AbstractScope 
==> In Progress:org.apache.log4j.helpers.DateTimeDateFormat 
==> In Progress:sun.nio.cs.MS1252 
==> In Progress:java.lang.Integer$IntegerCache 
==> In Progress:com.sun.org.apache.xerces.internal.utils.SecuritySupport$3 
==> In Progress:org.apache.commons.configuration.MapConfiguration 
==> In Progress:org.apache.commons.beanutils.IntrospectionContext 
==> In Progress:java.io.Reader 
==> In Progress:java.util.WeakHashMap$Holder 
==> In Progress:java.util.ServiceLoader$LazyIterator 
==> In Progress:java.util.regex.Pattern$Branch 
==> In Progress:java.lang.IllegalMonitorStateException 
==> In Progress:java.util.regex.Pattern$Curly 
==> In Progress:org.apache.commons.configuration.resolver.EntityRegistry 
==> In Progress:java.io.IOException 
==> In Progress:java.io.FilterOutputStream 
==> In Progress:org.apache.log4j.LogManager 
==> In Progress:sun.util.logging.PlatformLogger$Level 
==> In Progress:java.nio.charset.CoderResult$1 
==> In Progress:com.google.gson.FieldNamingPolicy$5 
==> In Progress:com.google.gson.internal.ObjectConstructor 
==> In Progress:sun.util.calendar.BaseCalendar$Date

所以你可以注意到我可以找到我感興趣的每個類(甚至是java.io)。 現在,如果我們看看應該包含MyTransformer試圖轉換的每個類的文件,我們注意到沒有常見的條目,這真的很奇怪。

org.apache.commons.beanutils.converters.ShortConverter
com.google.gson.JsonNull
org.apache.commons.lang.BooleanUtils
com.google.gson.internal.bind.TypeAdapters$33
org.apache.log4j.helpers.DateTimeDateFormat
org.apache.commons.configuration.MapConfiguration
org.apache.commons.beanutils.IntrospectionContext
org.apache.commons.configuration.resolver.EntityRegistry
org.apache.log4j.LogManager
com.google.gson.FieldNamingPolicy$5
com.google.gson.internal.ObjectConstructor
org.apache.log4j.Layout
com.google.gson.internal.bind.TypeAdapters
org.apache.log4j.PropertyConfigurator
com.sap.psr.vulas.java.JavaEnumId
org.apache.commons.collections.collection.AbstractSerializableCollectionDecorator
org.apache.commons.logging.impl.LogFactoryImpl
org.apache.commons.lang.text.StrTokenizer

我發現的另一個問題是,如果我嘗試在Mytransformer.transform(..)方法中插入一些字節碼中的篡改,它將破壞我的執行。 我的意思是我正在使用的轉換操作非常好,因為我在附加代理程序后加載的每個類上使用它。 但不知何故,如果我嘗試使用它那些“重新轉換”的類,我會提出這個錯誤:

 ==> In Progress:org.apache.commons.lang.text.StrMatcher$TrimMatcher java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
        at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
        at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)

我知道本機類的轉換存在限制,但我並不認為我的轉換操作正在嘗試更改架構。 我會調查這一點並發布更新。

此鏈接類似於您要問的問題。 看起來您可以修改本機方法的主體 ,但無法添加方法或字段

我可以在您的代碼中看到您正在使用Javassist來執行此操作。 我花了幾個月試圖讓它工作,但它不適合我,所以我結果使用ASM(順便說一句,它工作)。

你怎么做的? 首先,我將使用Attach API掛鈎到rt.jar(因為它已經加載,你需要將你的代理連接到java進程。然后你可以從agentmain方法將你的轉換器添加到java代理。然后你創建一個您要編輯的類的類閱讀器,並接受您擴展的ClassVisitor。然后,您重寫一個方法,如果它是您想要的方法,請將代碼替換為其他代碼。您可以看到的示例Github 。你需要每次都轉換它,因為它加載到內存中 - 你不必擔心刪除代碼。

我希望這有幫助! 如果您有任何問題,請評論:)

你有沒有為你的代理jar manifest.mf添加所需的清單條目?

你的'manifest.mf'看起來怎么樣? 應該有類似的東西

Can-Redefine-Classes: true

Can-Retransform-Classes: true

問候,

Grzesiek

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM