private CtMethod generateFieldWriter(
      CtClass managedCtClass, CtField persistentField, AttributeTypeDescriptor typeDescriptor) {
    final String fieldName = persistentField.getName();
    final String writerName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + fieldName;

    try {
      final CtMethod writer;

      if (!enhancementContext.isLazyLoadable(persistentField)) {
        writer = MethodWriter.addSetter(managedCtClass, fieldName, writerName);
      } else {
        writer =
            MethodWriter.write(
                managedCtClass,
                "public void %s(%s %s) {%n  %s%n}",
                writerName,
                persistentField.getType().getName(),
                fieldName,
                typeDescriptor.buildWriteInterceptionBodyFragment(fieldName));
      }

      if (enhancementContext.isCompositeClass(managedCtClass)) {
        writer.insertBefore(
            String.format(
                "if (%s != null) { %<s.callOwner(\".%s\"); }%n",
                EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME, fieldName));
      } else if (enhancementContext.doDirtyCheckingInline(managedCtClass)) {
        writer.insertBefore(
            typeDescriptor.buildInLineDirtyCheckingBodyFragment(
                enhancementContext, persistentField));
      }

      handleCompositeField(managedCtClass, persistentField, writer);

      if (enhancementContext.doBiDirectionalAssociationManagement(persistentField)) {
        handleBiDirectionalAssociation(managedCtClass, persistentField, writer);
      }
      return writer;
    } catch (CannotCompileException cce) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field writer method [%s]",
              managedCtClass.getName(), writerName);
      throw new EnhancementException(msg, cce);
    } catch (NotFoundException nfe) {
      final String msg =
          String.format(
              "Could not enhance entity class [%s] to add field writer method [%s]",
              managedCtClass.getName(), writerName);
      throw new EnhancementException(msg, nfe);
    }
  }
  private void changeMethod(CtClass ctClass, CtMethod ctMethod) throws CannotCompileException {
    // basically your before-advice...
    ctMethod.insertBefore("System.out.println(\"started method at \" + new java.util.Date());");
    // basically your after-advice...
    ctMethod.insertAfter("System.out.println(\"ended method at \" + new java.util.Date());");

    // basically your around-advice...
    String methodName = ctMethod.getName();
    String proxyName = methodName + "_$proxy";
    CtMethod proxy = CtNewMethod.copy(ctMethod, proxyName, ctClass, null);
    ctMethod.setName(ctMethod.getName() + "_orig");
    proxy.setBody(
        "{ System.out.println(\"hoot!\"); return $proceed($$);}", "this", ctMethod.getName());
    proxy.setName(methodName);
    ctClass.addMethod(proxy);
  }
Beispiel #3
0
  /**
   * Javassist是一个开源的分析、编辑和创建Java字节码的类库。 是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss
   * 应用服务器项目, 通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目.
   * 其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
   *
   * @param path
   * @throws Exception
   */
  public static void generateClassByJavassist(String path) throws Exception {
    ClassPool pool = ClassPool.getDefault();

    // 创建类
    CtClass clazz = pool.makeClass("Demo");
    // 创建方法
    CtMethod method = CtNewMethod.make("public void call(){}", clazz);

    // 插入方法代码
    method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding.....\");");

    // 将方法添加到类中
    clazz.addMethod(method);

    // 将class 写入文件
    clazz.writeFile(path);
  }
Beispiel #4
0
 private void preprocessMethods(CtClass cc, boolean insertLoad, boolean wrapFieldAccess)
     throws CannotCompileException {
   CtMethod[] methods = cc.getDeclaredMethods();
   for (int i = 0; i < methods.length; i++) {
     CtMethod m = methods[i];
     if (wrapFieldAccess) {
       m.instrument(
           new ExprEditor() {
             public void edit(FieldAccess fa) throws CannotCompileException {
               try {
                 if ((fa.getField().getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0
                     && fa.getField().getDeclaringClass().subtypeOf(persistentInterface)) {
                   if (fa.isWriter()) {
                     fa.replace("{ $0.loadAndModify(); $proceed($$); }");
                   }
                   // isSelfReader is my extension of JAssist, if you
                   // use original version of JAssist comment the
                   // branch below or replace "else if" with "else".
                   // In first case Perst will not be able to handle
                   // access to foreign (non-this) fields. You should use
                   // getter/setter methods instead.
                   // In second case access to foreign fields still will be possible,
                   // but with significant degradation of performance and
                   // increased code size, because in this case before ALL access
                   // to fields of persistent capable object call of load() method
                   // will be inserted.
                   else if (!fa.isSelfReader()) {
                     fa.replace("{ $0.load(); $_ = $proceed($$); }");
                   }
                 }
               } catch (NotFoundException x) {
               }
             }
           });
     }
     if (insertLoad
         && !"recursiveLoading".equals(m.getName())
         && (m.getModifiers() & (Modifier.STATIC | Modifier.ABSTRACT)) == 0) {
       m.insertBefore("load();");
     }
   }
 }
  private void handleCompositeField(
      CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
      throws NotFoundException, CannotCompileException {
    if (!persistentField.hasAnnotation(Embedded.class)) {
      return;
    }

    // make sure to add the CompositeOwner interface
    managedCtClass.addInterface(classPool.get(CompositeOwner.class.getName()));

    if (enhancementContext.isCompositeClass(managedCtClass)) {
      // if a composite have a embedded field we need to implement the TRACKER_CHANGER_NAME method
      // as well
      MethodWriter.write(
          managedCtClass,
          ""
              + "public void %1$s(String name) {%n"
              + "  if (%2$s != null) { %2$s.callOwner(\".\" + name) ; }%n}",
          EnhancerConstants.TRACKER_CHANGER_NAME,
          EnhancerConstants.TRACKER_COMPOSITE_FIELD_NAME);
    }

    // cleanup previous owner
    fieldWriter.insertBefore(
        String.format(
            "" + "if (%1$s != null) { ((%2$s) %1$s).%3$s(\"%1$s\"); }%n",
            persistentField.getName(),
            CompositeTracker.class.getName(),
            EnhancerConstants.TRACKER_COMPOSITE_CLEAR_OWNER));

    // trigger track changes
    fieldWriter.insertAfter(
        String.format(
            "" + "((%2$s) %1$s).%4$s(\"%1$s\", (%3$s) this);%n" + "%5$s(\"%1$s\");",
            persistentField.getName(),
            CompositeTracker.class.getName(),
            CompositeOwner.class.getName(),
            EnhancerConstants.TRACKER_COMPOSITE_SET_OWNER,
            EnhancerConstants.TRACKER_CHANGER_NAME));
  }
 private static void insertBefore(CtMethod m, String newBody) throws Exception {
   removeNativeModifier(m);
   m.insertBefore(newBody);
 }
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    // Don't transform system classes. This reason is:
    // - To improve performance
    // - To avoid unexpected behaviors.
    //   For example, if transforms java.lang.invoke.** classes in Java8
    //   even without any ctClass modification, the classes become broken
    //   and Java stream API call fails unexpectedly.
    //   Maybe this is because CtClass instance generated by ClassPool.makeClass method
    //   is cached on global default ClassPool instance.
    if (isJavaSystemClassName(className)) {
      return null;
    }

    // TODO don't need to do anything for java package classes
    ClassPool classPool = ClassPool.getDefault();
    String hookClassName = HookMethodDef.class.getCanonicalName();
    String initializeSrc = hookInitializeSrc();
    boolean transformed = false;
    InputStream stream = null;
    try {
      stream = new ByteArrayInputStream(classfileBuffer);
      CtClass ctClass = null;
      try {
        ctClass = classPool.makeClass(stream, true);
      } catch (RuntimeException e) {
        // makeClass raises RuntimeException when the existing class is frozen.
        // Since frozen classes are maybe system class, just ignore this exception
        return null;
      }

      for (Pair<CtMethod, TestMethod> pair : allSubMethods(srcTree, ctClass)) {
        CtMethod ctSubMethod = pair.getLeft();
        TestMethod subMethod = pair.getRight();
        if (ctSubMethod.isEmpty()) {
          logger.info("skip empty method: " + ctSubMethod.getLongName());
          continue; // cannot hook empty method
        }

        String subClassQualifiedName = subMethod.getTestClass().getQualifiedName();
        String subMethodSimpleName = subMethod.getSimpleName();
        String subMethodArgClassesStr =
            TestMethod.argClassQualifiedNamesToArgClassesStr(
                getArgClassQualifiedNames(ctSubMethod));
        for (int i = 0; i < subMethod.getCodeBody().size(); i++) {
          CodeLine codeLine = subMethod.getCodeBody().get(i);
          if (i + 1 < subMethod.getCodeBody().size()) {
            CodeLine nextCodeLine = subMethod.getCodeBody().get(i + 1);
            assert codeLine.getEndLine() <= nextCodeLine.getStartLine();
            if (codeLine.getEndLine() == nextCodeLine.getStartLine()) {
              // - if multiple statements exist on a line, insert hook only after the last statement
              // - avoid insertion at the middle of the statement.
              //   The problem happens when multi-line statements are like:
              //   method(1);method(
              //           2);
              continue;
            }
          }

          // Hook should be inserted just after the code has finished
          // since the code inserted by the insertAt method is inserted just before the specified
          // line.
          int insertedLine = subMethod.getCodeBody().get(i).getEndLine() + 1;
          int actualInsertedLine = ctSubMethod.insertAt(insertedLine, false, null);
          ctSubMethod.insertAt(
              insertedLine,
              String.format(
                  "%s%s.beforeCodeLineHook(\"%s\",\"%s\",\"%s\",\"%s\",%d, %d);",
                  initializeSrc,
                  hookClassName,
                  subClassQualifiedName,
                  subMethodSimpleName,
                  subMethodSimpleName,
                  subMethodArgClassesStr,
                  codeLine.getStartLine(),
                  actualInsertedLine));
          transformed = true;
        }
      }

      CtClass exceptionType = classPool.get(Throwable.class.getCanonicalName());
      for (Pair<CtMethod, TestMethod> pair : allRootMethods(srcTree, ctClass)) {
        CtMethod ctRootMethod = pair.getLeft();
        TestMethod rootMethod = pair.getRight();
        if (ctRootMethod.isEmpty()) {
          continue; // cannot hook empty method
        }

        String rootClassQualifiedName = rootMethod.getTestClass().getQualifiedName();
        String rootMethodSimpleName = rootMethod.getSimpleName();
        String rootMethodArgClassesStr =
            TestMethod.argClassQualifiedNamesToArgClassesStr(
                getArgClassQualifiedNames(ctRootMethod));
        for (int i = 0; i < rootMethod.getCodeBody().size(); i++) {
          CodeLine codeLine = rootMethod.getCodeBody().get(i);
          if (i + 1 < rootMethod.getCodeBody().size()) {
            CodeLine nextCodeLine = rootMethod.getCodeBody().get(i + 1);
            assert codeLine.getEndLine() <= nextCodeLine.getStartLine();
            if (codeLine.getEndLine() == nextCodeLine.getStartLine()) {
              // - if multiple statements exist on a line, insert hook only after the last statement
              // - avoid insertion at the middle of the statement.
              //   The problem happens when multi-line statements are like:
              //   method(1);method(
              //           2);
              continue;
              // TODO screen capture is not taken correctly for multiple statements in a line
            }
          }

          // Hook should be inserted just after the code has finished
          // since the code inserted by the insertAt method is inserted just before the specified
          // line.
          int insertedLine = rootMethod.getCodeBody().get(i).getEndLine() + 1;
          int actualInsertedLine = ctRootMethod.insertAt(insertedLine, false, null);
          ctRootMethod.insertAt(
              insertedLine,
              String.format(
                  "%s%s.beforeCodeLineHook(\"%s\",\"%s\",\"%s\",\"%s\",%d,%d);",
                  initializeSrc,
                  hookClassName,
                  rootClassQualifiedName,
                  rootMethodSimpleName,
                  rootMethodSimpleName,
                  rootMethodArgClassesStr,
                  codeLine.getStartLine(),
                  actualInsertedLine));
        }

        ctRootMethod.insertBefore(
            String.format(
                "%s%s.beforeMethodHook(\"%s\",\"%s\",\"%s\");",
                initializeSrc,
                hookClassName,
                rootClassQualifiedName,
                rootMethodSimpleName,
                rootMethodSimpleName));
        ctRootMethod.addCatch(
            String.format(
                "{ %s%s.methodErrorHook(\"%s\",\"%s\",$e); throw $e; }",
                initializeSrc, hookClassName, rootClassQualifiedName, rootMethodSimpleName),
            exceptionType);
        ctRootMethod.insertAfter(
            String.format(
                "%s%s.afterMethodHook(\"%s\",\"%s\");",
                initializeSrc, hookClassName, rootClassQualifiedName, rootMethodSimpleName),
            true);
        transformed = true;
      }

      // don't transform not changed ctClass
      // (to improve performance and avoid unexpected error)
      if (transformed) {
        logger.info("transform " + className);
        return ctClass.toBytecode();
      } else {
        return null;
      }
    } catch (CannotCompileException e) {
      // print error since exception in transform method is just ignored
      System.err.println("exception on " + className);
      e.printStackTrace();
      throw new IllegalClassFormatException(e.getLocalizedMessage());
    } catch (Exception e) {
      // print error since exception in transform method is just ignored
      System.err.println("exception on " + className);
      e.printStackTrace();
      throw new RuntimeException(e);
    } finally {
      IOUtils.closeQuietly(stream);
    }
  }
  private void handleBiDirectionalAssociation(
      CtClass managedCtClass, CtField persistentField, CtMethod fieldWriter)
      throws NotFoundException, CannotCompileException {
    if (!isPossibleBiDirectionalAssociation(persistentField)) {
      return;
    }
    final CtClass targetEntity = getTargetEntityClass(persistentField);
    if (targetEntity == null) {
      log.debugf(
          "Could not find type of bi-directional association for field [%s#%s]",
          managedCtClass.getName(), persistentField.getName());
      return;
    }
    final String mappedBy = getMappedBy(persistentField, targetEntity);
    if (mappedBy.isEmpty()) {
      log.warnf(
          "Could not find bi-directional association for field [%s#%s]",
          managedCtClass.getName(), persistentField.getName());
      return;
    }

    // create a temporary getter and setter on the target entity to be able to compile our code
    final String mappedByGetterName = EnhancerConstants.PERSISTENT_FIELD_READER_PREFIX + mappedBy;
    final String mappedBySetterName = EnhancerConstants.PERSISTENT_FIELD_WRITER_PREFIX + mappedBy;
    MethodWriter.addGetter(targetEntity, mappedBy, mappedByGetterName);
    MethodWriter.addSetter(targetEntity, mappedBy, mappedBySetterName);

    if (persistentField.hasAnnotation(OneToOne.class)) {
      // only unset when $1 != null to avoid recursion
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null && $1 != null) $0.%<s.%s(null);%n",
              persistentField.getName(), mappedBySetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null && $1.%s() != $0) $1.%s($0);%n",
              mappedByGetterName, mappedBySetterName));
    }
    if (persistentField.hasAnnotation(OneToMany.class)) {
      // only remove elements not in the new collection or else we would loose those elements
      // don't use iterator to avoid ConcurrentModException
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s(null); } }%n",
              persistentField.getName(), targetEntity.getName(), mappedBySetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if (target.%s() != $0) target.%s((%s)$0); } }%n",
              targetEntity.getName(),
              mappedByGetterName,
              mappedBySetterName,
              managedCtClass.getName()));
    }
    if (persistentField.hasAnnotation(ManyToOne.class)) {
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%1$s != null && $0.%1$s.%2$s() != null) $0.%1$s.%2$s().remove($0);%n",
              persistentField.getName(), mappedByGetterName));
      // check .contains($0) to avoid double inserts (but preventing duplicates)
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { java.util.Collection c = $1.%s(); if (c != null && !c.contains($0)) c.add($0); }%n",
              mappedByGetterName));
    }
    if (persistentField.hasAnnotation(ManyToMany.class)) {
      fieldWriter.insertBefore(
          String.format(
              "if ($0.%s != null) { Object[] array = $0.%<s.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; if ($1 == null || !$1.contains(target)) target.%s().remove($0); } }%n",
              persistentField.getName(), targetEntity.getName(), mappedByGetterName));
      fieldWriter.insertAfter(
          String.format(
              "if ($1 != null) { Object[] array = $1.toArray(); for (int i = 0; i < array.length; i++) { %s target = (%<s) array[i]; java.util.Collection c = target.%s(); if ( c != $0 && c != null) c.add($0); } }%n",
              targetEntity.getName(), mappedByGetterName));
    }
    // implementation note: association management @OneToMany and @ManyToMay works for add()
    // operations but for remove() a snapshot of the collection is needed so we know what
    // associations to break.
    // another approach that could force that behavior would be to return
    // Collections.unmodifiableCollection() ...
  }
  //    @Deprecated
  //    @Test
  public void interceptor()
      throws NotFoundException, CannotCompileException, IllegalAccessException,
          InstantiationException, IOException, ClassNotFoundException, NoSuchMethodException {
    AroundInterceptor aroundInterceptor =
        new AroundInterceptor() {

          @Override
          public void before(Object target, Object[] args) {
            logger.info("BEFORE target:" + target + " args:" + Arrays.toString(args));
          }

          @Override
          public void after(Object target, Object[] args, Object result, Throwable throwable) {
            logger.info(
                "AFTER target: "
                    + target
                    + " args:"
                    + Arrays.toString(args)
                    + " result:"
                    + result
                    + " throwable:"
                    + throwable);
          }
        };
    int interceptorId = INTERCEPTOR_REGISTRY_ADAPTOR.addInterceptor(aroundInterceptor);

    final ClassPool classPool = new ClassPool(true);
    CtClass throwable = classPool.get(Throwable.class.getName());

    CtClass ctClass = classPool.get("com.baidu.oped.apm.profiler.interceptor.JavaAssistTestObject");

    final CtMethod hello = ctClass.getMethod("hello", "(Ljava/lang/String;)Ljava/lang/String;");
    logger.debug("longName:{}", hello.getLongName());
    logger.debug("name:{}", hello.getName());

    String interceptorClassName = AroundInterceptor.class.getName();
    CtClass interceptor = classPool.get(interceptorClassName);
    hello.addLocalVariable("interceptor", interceptor);

    CtClass object = classPool.get(Object.class.getName());
    hello.addLocalVariable("result", object);

    //        hello.insertBefore("{ System.out.println(\"BEFORE\"); }");
    hello.insertBefore(
        "{"
            + "interceptor = ("
            + interceptorClassName
            + ") "
            + InterceptorRegistry.class.getName()
            + ".getSimpleInterceptor("
            + interceptorId
            + ");"
            + "interceptor.before(this, $args);"
            + "}");
    //        hello.addCatch("{" +
    ////            " interceptor.after(ctx);"+
    ////           " AroundInterceptor a = (AroundInterceptor) " +
    // InterceptorRegistry.class.getName() + ".getStaticInterceptor(\"a\");"+
    //                " throw $e;" +
    //                "}", throwable);
    //        hello.insertAfter("{" +
    //                "interceptor.after(this,  $args, ($w)$_, null); " +
    //                "}");

    //       hello.setBody(generatedAroundInterceptor("TestObject", "hello"));
    //       hello.setBody("{ System.out.println(\"ddd\");  }", ClassMap map );
    //       hello.insertBefore(" System.out.println(\" BEFORE +  \");");
    //       hello.insertAfter(" System.out.println($_);");
    //       hello.insertAfter(" System.out.println($r);");
    //       hello.insertAfter(" System.out.println($w);");
    //       hello.insertAfter(" System.out.println($sig);");
    //       hello.insertAfter(" System.out.println($type);");
    //       hello.insertAfter(" System.out.println($class);");
    //       hello.instrument(new ExprEditor() {
    //         public void edit(MethodCall m)
    //         throws CannotCompileException
    //         {
    //             try {
    //                 System.out.println("method call" + m.getMethod().getName());
    //             } catch (NotFoundException e) {
    //                 e.printStackTrace();  //To change body of catch statement use File | Settings
    // | File Templates.
    //             }
    //             String code = generatedAroundInterceptor("TestObject", "hello");
    //             m.replace(code);
    //         }

    //         });
    //        hello.addCatch("System.out.println(\"catch\"); throw $e;", throwable);

    //       hello.setName("__hello");
    //       CtMethod method = CtNewMethod.make("public void hello() { try {__hello(); }
    // catch(Throwable th){throw th;}}", ctClass);

    //         CtMethod method = CtNewMethod.make("public void hello() {
    // System.out.println(\"ddd\"); } catch(Throwable th){throw th;}}", ctClass);
    //       ctClass.addMethod(method);

    //        ctClass.freeze();
    //       ctClass.writeFile("./debug");
    //       ctClass.debugWriteFile("./debug");
    Loader loader = LoaderUtils.createLoader(classPool);
    loader.delegateLoadingOf("com.baidu.oped.apm.bootstrap.");

    Class aClass = loader.loadClass(ctClass.getName());
    Object testObject = aClass.newInstance();

    Method helloMethod = testObject.getClass().getDeclaredMethod("hello", String.class);

    try {
      helloMethod.invoke(testObject, "hello~~");
    } catch (Exception e) {
      Assert.fail(e.getMessage());
    }

    //       o.hello();
  }