Короче говоря:Java агент не может преобразовать все классы в моем проекте
- Мне нужно преобразовать каждый класс в моей программе (даже 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» каждый класс уже загружен и, если это возможно reTransfrom его с помощью метода «retransfromClasses».
Теперь в моем методе преобразования у меня есть только журнал, который записывает в файл каждый класс, который будет преобразован. Теперь мои результаты испытаний:
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)
Я знаю, что существует ограничения на трансформации нативных классов, но я не вещь, что моя трансформация операция пытаясь изменить схему. Я буду исследовать этот вопрос и публиковать обновления.
Не могли бы вы рассказать нам, почему вы хотите, чтобы ввести код в нативных классов Java? Возможно, вам будет проще достичь своей цели, чем внедрить код в каждый класс вашей среды. – Aaron
Сложно, но я гарантирую, что мне нужно изменить байтовый код этих классов. (моя цель в простых словах заключается в подстановке оператора return некоторых методов, например, замена возврата байта вызова [] java.io.readFile, чтобы я мог вернуть свои пользовательские байты, а не читать их. происходят при каждом вызове этого метода, даже если я не знаю, где используется) – rakwaht