Java代理无法转换项目中的所有类 [英] Java agent cannot transform all the classes in my project

查看:118
本文介绍了Java代理无法转换项目中的所有类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

长话短说:


  • 我需要转换程序中的每个类(甚至是Java < br>
    库,该库在我的代理之前加载)。

  • 我已经找到一种方法来完成它
    ,但是无法正常工作。我愿意接受新的想法。

  • 我的实际方法很奇怪:应该在文件和控制台中打印相同的名称,但事实并非如此。我确定这些类可以到达我的transform方法,因为如果我尝试对它们
    进行检测,则会出错。

完整的故事
我创建了一个代理,以便在加载到项目中的每个类中注入一些自定义代码。我在运行时使用选项-javaagent添加了该代理,并且工作正常。

Complete story: I have created an agent in order to inject some custom code in every class that is loaded into my project. I add this agent at runtime using the option -javaagent and this work just fine.

问题是,当我的代理连接到JVM时,很多类已经被加载(例如java.io. *类),因此我的转换错过了整个类。
所以我的问题是:有一种方法可以对所有我缺少的类进行检测?

现在,我尝试过这样的操作:

For now I have tried like this:

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从其重新传输。

So my first attempt is to take in the "premain" method every class already loaded and if it's possible reTransfrom it with the method "retransfromClasses".

现在我的transform方法中只有一个日志,每个文件将要转换的类。现在我的测试结果是:

Now in my transform method I have only a log that write into a file every class that is going to be transformed. Now my test results are:

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尝试转换的每个类的文件,我们会注意到没有通用条目,这真的很奇怪。

So you can notice that I can find every class (even the java.io) in which I interested. Now if we take a look at the file that is supposed to contain every class that MyTransformer tried to transform we notice that there are no common entries and that's really strange.

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(..)方法中插入一些字节码,将破坏我的执行。我的意思是我正在使用的转换操作非常好,因为我在连接代理后在加载的每个类上使用了转换操作。但是以某种方式,如果我尝试使用那些重新转换的类,我将出现此错误:

Another problem that I found is that if I try to insert in Mytransformer.transform(..) method some tampering in the bytecode it will break my execution. I mean the transformation operation that I am using is perfectly fine as I use it on every class that is loaded after my agent is attached. But somehow if I try to use it one those "retransformed" classes I will rise this error:

 ==> 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)

我知道本机类的转换受到限制,但是我的转换操作没有尝试更改模式,这并不重要。我将对此进行调查并发布更新。

I know that there are limitations on the transformation of native classes but I don't thing that my transform operation is attempting to change the schema. I will investigate on this point and post updates.

推荐答案

此链接类似于您所提出的问题。看来您可以修改本机方法的主体,但不能添加方法或字段

This link is similar to the question you are asking. It looks like you can modify the body of a native method, but cannot add methods or fields.

I可以在您的代码中看到您正在使用Javassist执行此操作。我花了几个月的时间试图使其工作,但对我而言却不起作用,所以我最终使用了ASM(它确实可以工作)。

I can see in your code that you are using Javassist to do it. I spent multiple months trying to get it to work but it wouldn't work for me, so I resulted to using ASM (which does work by the way).

所以您是怎么做的?首先,我将使用Attach API钩接到rt.jar上(因为它已经加载了,因此您需要将代理附加到Java进程。然后可以添加转换器从agentmain方法连接到Java代理,然后为要编辑的类创建一个类读取器,并接受扩展的ClassVisitor,然后覆盖一个方法,如果它是所需的方法,则替换代码和其他代码。您可以在我的Github上看到一个示例 。您需要分别对其进行转换

So how do you do it? First, I would use the Attach API to hook onto rt.jar (because it is already loaded you need to attach your agent to the java process. Then you can add your transformer to the java agent from the agentmain method. You then create a class reader for the class you want to edit, and accept a ClassVisitor, which you extend. Then, you override a method, and, if it is the method you want, replace the code with other code. You can see an example on my Github. You will need to transform it each individual time because it loads onto memory - you don't need to worry about removing the code.

我希望这会有所帮助!如果您有任何疑问,请发表评论:)

I hope this helps! Please comment if you have any questions :)

这篇关于Java代理无法转换项目中的所有类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆