2015-12-01 4 views
2

Возможно ли использовать классы инструментов для пакетов java.*?Возможно ли использовать классы инструментов из java. * Package

Я хотел бы заменить тело метода java.awt.print.PrinterJob.printDialog(PrintRequestAttributeSet), чтобы он всегда возвращался true.

При определении нового класса с java.*, я получаю SecurityException: Prohibited package name: java.awt.print. Очевидно, это происходит потому, что я пытаюсь определить класс с именем java. в названии пакета.

Итак, есть способ обойти эту проверку безопасности или решить эту проблему по-другому?

P.S Я также попытался переопределить системное свойство java.awt.printerjob, чтобы использовать собственную реализацию PrinterJob. Но я потерпел неудачу, потому что мой класс не был найден. Мне не удалось найти фактический загрузчик классов PrinterJob для загрузки моего класса.

@Test public void testInstrumentationOfJavaClasses() throws Exception 
{ 
    // replace printDialog(attrs) so that it always returns true 
    String typeName = "java.awt.print.PrinterJob"; 
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 
    MethodReplacer methodReplacer = new MethodReplacer(cw, "printDialog", "(Ljavax/print/attribute/PrintRequestAttributeSet;)Z"); 
    ClassReader classReader = createClassReader(typeName); 
    classReader.accept(methodReplacer, ClassReader.EXPAND_FRAMES); 
    loadClass(cw.toByteArray(), typeName); 

    // the actual call of PrinterJob which as I expect to always return "true" 
    PrinterJob printJob = PrinterJob.getPrinterJob(); 
    printJob.setPrintable(PRINTABLE); 
    PrintRequestAttributeSet printerSettings = new HashPrintRequestAttributeSet(); 
    if (printJob.printDialog(printerSettings)) 
     printJob.print(printerSettings); 
} 

public static class MethodReplacer extends ClassVisitor implements Opcodes 
{ 
    private final String methodName; 
    private final String methodDefenition; 

    public MethodReplacer(ClassVisitor classVisitor, String methodName, String methodDescr) 
    { 
     super(ASM5, classVisitor); 
     this.methodName = methodName; 
     this.methodDefenition = methodDescr; 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) 
    { 
     if (methodName.equals(name) && methodDefenition.equals(desc)) 
     { 
      System.out.println("visitMethod(" + name + ")"); 
      MethodVisitor mv = cv.visitMethod(ACC_PUBLIC, name, desc, null, new String[]{"java/awt/HeadlessException"}); 
      mv.visitCode(); 
      mv.visitInsn(ICONST_1); 
      mv.visitInsn(IRETURN); 
      mv.visitMaxs(1, 1); 
      mv.visitEnd(); 
      return mv; 
     } 
     return super.visitMethod(access, name, desc, signature, exceptions); 
    } 
} 

private static Class loadClass(byte[] bytecode, String typeName) throws Exception 
{ 
    ClassLoader baseClassLoader = TestPrint.class.getClassLoader(); 

    Class<?>[] types = new Class<?>[]{String.class, byte[].class, int.class, int.class}; 
    Method m = ClassLoader.class.getDeclaredMethod("defineClass", types); 
    m.setAccessible(true); 

    Object[] args = new Object[]{null, bytecode, 0, bytecode.length}; 
    Class definedClass = (Class<?>) m.invoke(baseClassLoader, args); 
    return definedClass; 
} 

ответ

3

Посмотрите на Java Instrumentation API. Вы можете вторгнуться в java.* пространства имен по-разному:

  1. Создание новых классов с префиксом java.*, связывайте эти файлы в банки и добавить эти классы в классе начальной загрузки загрузчика. Эти классы затем доступны из загрузчика класса загрузки (то есть глобально).
  2. Зарегистрируйте ClassFileTransformer, который вызывается при загрузке класса. Для некоторых классов начальной загрузки это позволяет изменить байтовый код класса до загрузки класса. Важно, чтобы эти классы не были необходимы для настройки агента. Для классов AWT это должно работать нормально.
  3. Переопределить классы java.* пространства имен. API-интерфейс инструментария позволяет ретрансформировать/переопределить некоторые классы даже после их загрузки.

Я написал библиотеку под названием Byte Buddy, которая намеревается упростить использование такого инструментария, если вы хотите избежать написания Java-агента вручную.

+0

Спасибо за ваш ответ. Я думаю, что этот API мне поможет. Я попробую и приму ваш ответ. Или попросите какую-нибудь помощь, если что-то пойдет не так =) – nixspirit

+0

Я зарегистрировал ClassFileTransformer и успешно переопределил PrinterJob.getPrinterJob, чтобы вернуть MyPrinterJob. Как вы сказали, я не могу использовать PrinterJob, прежде чем переопределять его в агенте, поэтому я хотел бы загрузить класс MyPrinterJob из основной библиотеки (из которой я запускаю агент). Мой код ASM вызывает Class.forName («my.print.MyPrinterJob»). NewInstance(), но java.lang.ClassNotFoundException. Возможно ли получить доступ к главному проекту ClassLoader? – nixspirit

+0

ClassFileTransformer предоставляет доступ к загрузчику классов инструментального класса. Агент Java всегда загружается загрузчиком системного класса. –