static TypeParameterMatcher generate(Class<?> type, ClassLoader classLoader) {
    final String className = "io.netty.util.internal.__matchers__." + type.getName() + "Matcher";
    try {
      try {
        return (TypeParameterMatcher) Class.forName(className, true, classLoader).newInstance();
      } catch (Exception e) {
        // Not defined in the specified class loader.
      }

      CtClass c = classPool.getAndRename(NoOpTypeParameterMatcher.class.getName(), className);
      c.setModifiers(c.getModifiers() | Modifier.FINAL);
      c.getDeclaredMethod("match").setBody("{ return $1 instanceof " + type.getName() + "; }");
      byte[] byteCode = c.toBytecode();
      c.detach();
      Method method =
          ClassLoader.class.getDeclaredMethod(
              "defineClass", String.class, byte[].class, int.class, int.class);
      method.setAccessible(true);

      Class<?> generated =
          (Class<?>) method.invoke(classLoader, className, byteCode, 0, byteCode.length);
      logger.debug("Generated: {}", generated.getName());
      return (TypeParameterMatcher) generated.newInstance();
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
예제 #2
0
 private void processCompositeClassFile(File javaClassFile, CtClass ctClass) {
   try {
     byte[] result = enhancer.enhanceComposite(ctClass.getName(), ctClass.toBytecode());
     if (result != null) {
       writeEnhancedClass(javaClassFile, result);
     }
   } catch (Exception e) {
     log("Unable to enhance class [" + ctClass.getName() + "]", e, Project.MSG_WARN);
   }
 }
예제 #3
0
 static void nullJavassistAdapt(ClassPool pool, final byte[] b) throws Exception {
   CtClass cc = pool.makeClass(new ByteArrayInputStream(b));
   CtMethod[] ms = cc.getDeclaredMethods();
   for (int j = 0; j < ms.length; ++j) {
     if (skipDebug) {
       // is there a mean to remove the debug attributes?
     }
     if (compute) {
       // how to force recomputation of maxStack and maxLocals?
     }
   }
   cc.toBytecode();
 }
  @Override
  public void enhanceThisClass(final ApplicationClass applicationClass) throws Exception {
    final CtClass ctClass = makeClass(applicationClass);
    if (ctClass.isInterface()) {
      return;
    }

    final Map<CtField, InjectionInfo> fieldsToInject = scanForInjections(ctClass);

    // in all methods, replace the field accesses with a call to spring
    for (final CtMethod ctMethod : ctClass.getDeclaredMethods()) {
      ctMethod.instrument(
          new ExprEditor() {
            @Override
            public void edit(final FieldAccess fieldAccess) {
              try {
                final InjectionInfo injectionInfo = fieldsToInject.get(fieldAccess.getField());

                if (injectionInfo != null && fieldAccess.isReader()) {

                  switch (injectionInfo.injectionMethod) {
                    case BY_NAME:
                      fieldAccess.replace(
                          "$_ = ($r)play.utils.Java.invokeStatic(play.modules.spring.Spring.class, \"getBean\", new Object[] {\""
                              + injectionInfo.beanName
                              + "\"});");
                      break;
                    case BY_TYPE:
                      fieldAccess.replace(
                          "$_ = ($r)play.utils.Java.invokeStatic(play.modules.spring.Spring.class, \"getBeanOfType\", new Object[] {$type});");
                      break;
                  }
                }
              } catch (final Exception e) {
                Logger.error(
                    e,
                    "Error in SpringEnhancer. %s.%s has not been properly enhanced (fieldAccess %s).",
                    applicationClass.name,
                    ctMethod.getName(),
                    fieldAccess);
                throw new UnexpectedException("Error enhancing injected field", e);
              }
            }
          });
    }

    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
  private Class<?> injectToPlainClassLoader(
      ClassPool pool, ClassLoader classLoader, String className)
      throws NotFoundException, IOException, CannotCompileException, IllegalArgumentException,
          IllegalAccessException, InvocationTargetException {
    Class<?> c = null;

    try {
      c = classLoader.loadClass(className);
    } catch (ClassNotFoundException ignore) {

    }

    if (c != null) {
      return c;
    }

    CtClass ct = pool.get(className);

    if (ct == null) {
      throw new NotFoundException(className);
    }

    CtClass superClass = ct.getSuperclass();

    if (superClass != null) {
      injectToPlainClassLoader(pool, classLoader, superClass.getName());
    }

    CtClass[] interfaces = ct.getInterfaces();

    for (CtClass i : interfaces) {
      injectToPlainClassLoader(pool, classLoader, i.getName());
    }

    Collection<String> refs = ct.getRefClasses();

    for (String ref : refs) {
      try {
        injectToPlainClassLoader(pool, classLoader, ref);
      } catch (NotFoundException e) {
        logger.warn("Skip a referenced class because of NotFoundException : ", e);
      }
    }

    byte[] bytes = ct.toBytecode();
    return (Class<?>) DEFINE_CLASS.invoke(classLoader, ct.getName(), bytes, 0, bytes.length);
  }
예제 #6
0
  public static Getter generateGetter(OgnlContext context, String code) throws OgnlException {
    String className = NAME_FACTORY.getNewClassName();

    try {
      ClassPool pool = (ClassPool) pools.get(context.getClassResolver());
      EnhancedClassLoader loader = (EnhancedClassLoader) loaders.get(context.getClassResolver());
      CtClass newClass;
      CtClass ognlContextClass;
      CtClass objectClass;
      CtClass stringClass;
      CtMethod method;
      byte[] byteCode;
      Class compiledClass;

      if ((pool == null) || (loader == null)) {
        ClassLoader classLoader =
            new ContextClassLoader(OgnlContext.class.getClassLoader(), context);

        pool = ClassPool.getDefault();
        pool.insertClassPath(new LoaderClassPath(classLoader));
        pools.put(context.getClassResolver(), pool);

        loader = new EnhancedClassLoader(classLoader);
        loaders.put(context.getClassResolver(), loader);
      }

      newClass = pool.makeClass(className);
      ognlContextClass = pool.get(OgnlContext.class.getName());
      objectClass = pool.get(Object.class.getName());
      stringClass = pool.get(String.class.getName());

      newClass.addInterface(pool.get(Getter.class.getName()));
      method =
          new CtMethod(
              objectClass,
              "get",
              new CtClass[] {ognlContextClass, objectClass, stringClass},
              newClass);
      method.setBody("{" + code + "}");
      newClass.addMethod(method);
      byteCode = newClass.toBytecode();
      compiledClass = loader.defineClass(className, byteCode);
      return (Getter) compiledClass.newInstance();
    } catch (Throwable ex) {
      throw new OgnlException("Cannot create class", ex);
    }
  }
 private void enhanceClass(Enhancer enhancer, File file) {
   byte[] enhancedBytecode = null;
   InputStream is = null;
   CtClass clas = null;
   try {
     is = new FileInputStream(file.toString());
     clas = getClassPool().makeClass(is);
     if (!clas.hasAnnotation(Entity.class)) {
       getLog().debug("Class $file not an annotated Entity class. skipping...");
     } else {
       enhancedBytecode = enhancer.enhance(clas.getName(), clas.toBytecode());
     }
   } catch (Exception e) {
     getLog().error("Unable to enhance class [${file.toString()}]", e);
     return;
   } finally {
     try {
       if (null != is) is.close();
     } catch (IOException ioe) {
     }
   }
   if (null != enhancedBytecode) {
     if (file.delete()) {
       try {
         if (!file.createNewFile()) {
           getLog().error("Unable to recreate class file [" + clas.getName() + "]");
         }
       } catch (IOException ioe) {
       }
     } else {
       getLog().error("Unable to delete class file [" + clas.getName() + "]");
     }
     FileOutputStream outputStream = null;
     try {
       outputStream = new FileOutputStream(file, false);
       outputStream.write(enhancedBytecode);
       outputStream.flush();
     } catch (IOException ioe) {
     } finally {
       try {
         if (outputStream != null) outputStream.close();
         clas.detach(); // release memory
       } catch (IOException ignore) {
       }
     }
   }
 }
  public void rewrite(final CtClass clazz) throws CannotCompileException {
    try {
      ClassPool cp = ClassPool.getDefault();

      byte[] b1 = clazz.toBytecode();
      clazz.defrost();
      ClassReader cr = new ClassReader(b1);
      ClassWriter cw = new ClassWriter(0); // new ClassWriter(cr, 0);
      // TraceClassVisitor ca = new TraceClassVisitor( cw, new PrintWriter(System.out, true) );
      // CheckClassAdapter cca = new CheckClassAdapter(ca);
      ConcurrencyControlRewriter.Adapter ca2 = new ConcurrencyControlRewriter.Adapter(cw, clazz);
      cr.accept(ca2, 0);
      byte[] b2 = cw.toByteArray();
      cp.makeClass(new ByteArrayInputStream(b2));
    } catch (IOException e) {
      throw new CannotCompileException(e);
    }
  }
  public byte[] translate(
      AspectManager manager, String className, ClassLoader loader, byte[] classfileBuffer)
      throws Exception {
    if (isReEntry()) {
      return null;
    }
    setReEntry();
    super.setTransformationStarted();
    try {
      if (manager.isNonAdvisableClassName(className)) {
        return null;
      }

      AOPClassPool pool = (AOPClassPool) manager.registerClassLoader(loader);

      CtClass clazz = obtainCtClassInfo(pool, className, classfileBuffer);

      CtClass woven = instrumentClass(manager, pool, clazz, true);
      if (woven != null) {
        pool.lockInCache(woven);
        if (AspectManager.debugClasses) {
          SecurityActions.debugWriteFile(clazz);
        }
        byte[] rtn = woven.toBytecode();
        if (AspectManager.getPrune()) woven.prune();
        return rtn;
      } else {
        pool.soften(clazz);
      }
      return null;
    } catch (Exception ex) {
      if (!(ex instanceof NotFoundException)) {
        if (verbose) logger.error(ex);
        else logger.error(ex.getMessage() + ".. Do verbose mode if you want full stack trace.");
      }
      throw ex;
    } finally {
      clearReEntry();
    }
  }
예제 #10
0
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    byte[] bytes = null;
    try {
      final ClassPool pool = ClassPool.getDefault();
      pool.appendClassPath(new LoaderClassPath(getClass().getClassLoader()));
      final CtClass ctClass = pool.getCtClass(className.replaceAll("/", "."));
      if (ctClass != null) {
        try {
          for (CtMethod ctMethod : ctClass.getMethods()) {
            if (ctMethod.hasAnnotation(Retryable.class)) {
              try {
                changeMethod(ctClass, ctMethod);
                bytes = ctClass.toBytecode();
                writeClassFile(bytes);

              } catch (CannotCompileException | IOException e) {
                e.printStackTrace();
              }
              System.out.println("found it!");
            }
          }
        } finally {
          ctClass.detach();
        }
      }
    } catch (NotFoundException e) {
      e.printStackTrace();
    }
    return bytes;
  }
  public byte[] transform(String className) {
    if (transformer.filterClassName(className)) {
      try {
        CtClass classToTransform = classPool.get(className);
        if (!classToTransform.isFrozen() && transformer.filterCtClass(classToTransform)) {
          transformer.applyTransformations(classPool, classToTransform);
          classToTransform.getClassFile().compact();
          classToTransform.rebuildClassFile();

          try (ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
              DataOutputStream out = new DataOutputStream(new BufferedOutputStream(baos))) {
            classToTransform.toBytecode(out);
            out.flush();
            return baos.toByteArray();
          } catch (IOException | CannotCompileException e) {
            throw new IllegalStateException(e);
          }
        }
      } catch (NotFoundException e) {
        throw new IllegalStateException(e);
      }
    }
    return null;
  }
  @Override
  public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {
    CtClass ctClass = makeClass(applicationClass);
    String entityName = ctClass.getName();
    Logger.debug("Enhance class " + entityName);

    // Only enhance Neo4jModel classes.
    if (!ctClass.subtypeOf(classPool.get("play.modules.neo4j.model.Neo4jModel"))) {
      return;
    }

    // Add a default constructor if needed
    try {
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          ctClass.removeConstructor(constructor);
        }
        if (constructor.getParameterTypes().length == 1
            && constructor.getParameterTypes()[0].getClass().isInstance(Node.class)) {
          ctClass.removeConstructor(constructor);
        }
      }
      if (!ctClass.isInterface()) {
        Logger.debug("Adding default constructor");
        CtConstructor defaultConstructor =
            CtNewConstructor.make("public " + ctClass.getSimpleName() + "() { super();}", ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    // for all field, we add getter / setter
    for (CtField ctField : ctClass.getDeclaredFields()) {
      try {
        // Property name
        String propertyName =
            ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
        String getter = "get" + propertyName;
        String setter = "set" + propertyName;

        Logger.debug("Field " + ctField.getName() + " is a property ?");
        if (isProperty(ctField)) {
          Logger.debug("true");

          // ~~~~~~~~~
          // GETTER
          // ~~~~~~~
          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
            if (!ctMethod.getName().equalsIgnoreCase("getShouldBeSave")) {
              ctClass.removeMethod(ctMethod);
              throw new NotFoundException("it's not a true getter !");
            }
          } catch (NotFoundException noGetter) {
            // create getter
            Logger.debug("Adding getter " + getter + " for class " + entityName);
            // @formatter:off
            String code =
                "public "
                    + ctField.getType().getName()
                    + " "
                    + getter
                    + "() {"
                    + "if(this.shouldBeSave == Boolean.FALSE && this.node != null){"
                    + "return (("
                    + ctField.getType().getName()
                    + ") play.modules.neo4j.util.Binder.bindFromNeo4jFormat(this.node.getProperty(\""
                    + ctField.getName()
                    + "\", null),"
                    + ctField.getType().getName()
                    + ".class ));"
                    + "}else{"
                    + "return "
                    + ctField.getName()
                    + ";"
                    + "}"
                    + "}";
            // @formatter:on
            Logger.debug(code);
            CtMethod getMethod = CtMethod.make(code, ctClass);
            ctClass.addMethod(getMethod);
          }

          // ~~~~~~~~~
          // SETTER
          // ~~~~~~~
          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
            if (ctMethod.getParameterTypes().length != 1
                || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
                || Modifier.isStatic(ctMethod.getModifiers())
                || hasPlayPropertiesAccessorAnnotation(ctMethod)) {
              if (hasPlayPropertiesAccessorAnnotation(ctMethod)) {
                ctClass.removeMethod(ctMethod);
              }
              throw new NotFoundException("it's not a true setter !");
            }
          } catch (NotFoundException noSetter) {
            // create setter
            Logger.debug("Adding setter " + setter + " for class " + entityName);
            // @formatter:off
            String code =
                "public void "
                    + setter
                    + "("
                    + ctField.getType().getName()
                    + " value) { "
                    + "this."
                    + ctField.getName()
                    + " = value;"
                    + "this.shouldBeSave = Boolean.TRUE;"
                    + "}";
            // formatter:on
            CtMethod setMethod = CtMethod.make(code, ctClass);
            Logger.debug(code);
            ctClass.addMethod(setMethod);
          }
        } else {
          // ~~~~~~~~~
          // GETTER for neo4j relation property
          // ~~~~~~~
          if (hasNeo4jRelationAnnotation(ctField)) {
            // test for related annotation
            Neo4jRelatedTo relatedTo = getRelatedAnnotation(ctField);
            if (relatedTo != null) {
              CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
              ctClass.removeMethod(ctMethod);
              String code;
              if (relatedTo.lazy()) {
                // @formatter:off
                code =
                    "public "
                        + ctField.getType().getName()
                        + " "
                        + getter
                        + "() {"
                        + "if(this."
                        + ctField.getName()
                        + " == null){"
                        + "java.lang.reflect.Field field = this.getClass().getField(\""
                        + ctField.getName()
                        + "\");"
                        + "this."
                        + ctField.getName()
                        + "=play.modules.neo4j.relationship.Neo4jRelationFactory.getModelsFromRelation(\""
                        + relatedTo.value()
                        + "\", \""
                        + relatedTo.direction()
                        + "\", field, this.node);"
                        + "}"
                        + "return "
                        + ctField.getName()
                        + ";"
                        + "}";
                // @formatter:on
              } else {
                // @formatter:off
                code =
                    "public "
                        + ctField.getType().getName()
                        + " "
                        + getter
                        + "() {"
                        + "return "
                        + ctField.getName()
                        + ";"
                        + "}";
                // @formatter:on
              }
              Logger.debug(code);
              CtMethod method = CtMethod.make(code, ctClass);
              ctClass.addMethod(method);
            }
            // test for unique relation annotation
            Neo4jUniqueRelation uniqueRelation = getUniqueRelationAnnotation(ctField);
            if (uniqueRelation != null) {
              CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
              ctClass.removeMethod(ctMethod);
              String code;
              // @formatter:off
              code =
                  "public "
                      + ctField.getType().getName()
                      + " "
                      + getter
                      + "() {"
                      + "return ("
                      + ctField.getType().getName()
                      + ")"
                      + ctField.getName()
                      + ";"
                      + "}";
              // @formatter:on
              Logger.debug(code);
              CtMethod method = CtMethod.make(code, ctClass);
              ctClass.addMethod(method);
            }
          }
        }
      } catch (Exception e) {
        Logger.error(e, "Error in PropertiesEnhancer");
        throw new UnexpectedException("Error in PropertiesEnhancer", e);
      }
    }

    // Adding getByKey() method
    Logger.debug("Adding getByKey() method for class " + entityName);
    // @formatter:off
    String codeGetByKey =
        "public static play.modules.neo4j.model.Neo4jModel getByKey(Long key) throws play.modules.neo4j.exception.Neo4jException {"
            + "return ("
            + entityName
            + ")_getByKey(key, \""
            + entityName
            + "\");"
            + "}";
    // @formatter:on
    Logger.debug(codeGetByKey);
    CtMethod getByKeyMethod = CtMethod.make(codeGetByKey, ctClass);
    ctClass.addMethod(getByKeyMethod);

    // ~~~~~~~~~~~~~~~
    // Adding findAll() method
    // @formatter:off
    String codeFindAll =
        "public static java.util.List findAll() {"
            + "return "
            + entityName
            + "._findAll(\""
            + entityName
            + "\");"
            + "}";
    // @formatter:on
    Logger.debug(codeFindAll);
    CtMethod findAllMethod = CtMethod.make(codeFindAll, ctClass);
    ctClass.addMethod(findAllMethod);

    // ~~~~~~~~~~~~~~~
    // Adding queryIndex() method
    // @formatter:off
    String queryIndex =
        "public static java.util.List queryIndex(String indexname, String query) {"
            + "return "
            + entityName
            + "._queryIndex(indexname, query);"
            + "}";
    // @formatter:on
    Logger.debug(queryIndex);
    CtMethod queryIndexMethod = CtMethod.make(queryIndex, ctClass);
    ctClass.addMethod(queryIndexMethod);

    // Done.
    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
예제 #13
0
  @Override
  public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {

    final CtClass ctClass = makeClass(applicationClass);
    if (ctClass.isInterface()) {
      return;
    }
    if (ctClass.getName().endsWith(".package")) {
      return;
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor && !ctClass.isInterface()) {
        CtConstructor defaultConstructor =
            CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    if (isScala(applicationClass)) {
      // Temporary hack for Scala. Done.
      applicationClass.enhancedByteCode = ctClass.toBytecode();
      ctClass.defrost();
      return;
    }

    for (CtField ctField : ctClass.getDeclaredFields()) {
      try {

        if (isProperty(ctField)) {

          // Property name
          String propertyName =
              ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1);
          String getter = "get" + propertyName;
          String setter = "set" + propertyName;

          try {
            CtMethod ctMethod = ctClass.getDeclaredMethod(getter);
            if (ctMethod.getParameterTypes().length > 0
                || Modifier.isStatic(ctMethod.getModifiers())) {
              throw new NotFoundException("it's not a getter !");
            }
          } catch (NotFoundException noGetter) {

            // Créé le getter
            String code =
                "public "
                    + ctField.getType().getName()
                    + " "
                    + getter
                    + "() { return this."
                    + ctField.getName()
                    + "; }";
            CtMethod getMethod = CtMethod.make(code, ctClass);
            getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC);
            ctClass.addMethod(getMethod);
          }

          if (!isFinal(ctField)) {
            try {
              CtMethod ctMethod = ctClass.getDeclaredMethod(setter);
              if (ctMethod.getParameterTypes().length != 1
                  || !ctMethod.getParameterTypes()[0].equals(ctField.getType())
                  || Modifier.isStatic(ctMethod.getModifiers())) {
                throw new NotFoundException("it's not a setter !");
              }
            } catch (NotFoundException noSetter) {
              // Créé le setter
              CtMethod setMethod =
                  CtMethod.make(
                      "public void "
                          + setter
                          + "("
                          + ctField.getType().getName()
                          + " value) { this."
                          + ctField.getName()
                          + " = value; }",
                      ctClass);
              setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC);
              ctClass.addMethod(setMethod);
              createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class);
            }
          }
        }

      } catch (Exception e) {
        Logger.error(e, "Error in PropertiesEnhancer");
        throw new UnexpectedException("Error in PropertiesEnhancer", e);
      }
    }

    // Add a default constructor if needed
    try {
      boolean hasDefaultConstructor = false;
      for (CtConstructor constructor : ctClass.getDeclaredConstructors()) {
        if (constructor.getParameterTypes().length == 0) {
          hasDefaultConstructor = true;
          break;
        }
      }
      if (!hasDefaultConstructor) {
        CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass);
        ctClass.addConstructor(defaultConstructor);
      }
    } catch (Exception e) {
      Logger.error(e, "Error in PropertiesEnhancer");
      throw new UnexpectedException("Error in PropertiesEnhancer", e);
    }

    // Intercept all fields access
    for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) {
      ctMethod.instrument(
          new ExprEditor() {

            @Override
            public void edit(FieldAccess fieldAccess) throws CannotCompileException {
              try {

                // Acces à une property ?
                if (isProperty(fieldAccess.getField())) {

                  // TODO : vérifier que c'est bien un champ d'une classe de l'application
                  // (fieldAccess.getClassName())

                  // Si c'est un getter ou un setter
                  String propertyName = null;
                  if (fieldAccess
                          .getField()
                          .getDeclaringClass()
                          .equals(ctMethod.getDeclaringClass())
                      || ctMethod
                          .getDeclaringClass()
                          .subclassOf(fieldAccess.getField().getDeclaringClass())) {
                    if ((ctMethod.getName().startsWith("get")
                            || (!isFinal(fieldAccess.getField())
                                && ctMethod.getName().startsWith("set")))
                        && ctMethod.getName().length() > 3) {
                      propertyName = ctMethod.getName().substring(3);
                      propertyName =
                          propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
                    }
                  }

                  // On n'intercepte pas le getter de sa propre property
                  if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) {

                    String invocationPoint =
                        ctClass.getName()
                            + "."
                            + ctMethod.getName()
                            + ", line "
                            + fieldAccess.getLineNumber();

                    if (fieldAccess.isReader()) {

                      // Réécris l'accés en lecture à la property
                      fieldAccess.replace(
                          "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");

                    } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) {

                      // Réécris l'accés en ecriture à la property
                      fieldAccess.replace(
                          "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \""
                              + fieldAccess.getFieldName()
                              + "\", "
                              + fieldAccess.getField().getType().getName()
                              + ".class, $1, \""
                              + fieldAccess.getClassName()
                              + "\", \""
                              + invocationPoint
                              + "\");");
                    }
                  }
                }

              } catch (Exception e) {
                throw new UnexpectedException("Error in PropertiesEnhancer", e);
              }
            }
          });
    }

    // Done.
    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.defrost();
  }
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    // Be careful with Apache library usage in this class (e.g. ArrayUtils). Usage will likely cause
    // a ClassCircularityError
    // under JRebel. Favor not including outside libraries and unnecessary classes.
    CtClass clazz = null;
    try {
      boolean mySkipOverlaps = skipOverlaps;
      boolean myRenameMethodOverlaps = renameMethodOverlaps;
      String convertedClassName = className.replace('/', '.');
      ClassPool classPool = null;
      String xformKey = convertedClassName;
      String[] xformVals = null;
      Boolean[] xformSkipOverlaps = null;
      Boolean[] xformRenameMethodOverlaps = null;
      if (!xformTemplates.isEmpty()) {
        if (xformTemplates.containsKey(xformKey)) {
          xformVals = xformTemplates.get(xformKey).split(",");
          classPool = ClassPool.getDefault();
          clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
        }
      } else {
        if (annotationTransformedClasses.contains(convertedClassName)) {
          logger.warn(
              convertedClassName
                  + " has already been transformed by a previous instance of DirectCopyTransfomer. "
                  + "Skipping this annotation based transformation. Generally, annotation-based transformation is handled "
                  + "by bean id blAnnotationDirectCopyClassTransformer with template tokens being added to "
                  + "blDirectCopyTransformTokenMap via EarlyStageMergeBeanPostProcessor.");
        }
        boolean isValidPattern = true;
        List<DirectCopyIgnorePattern> matchedPatterns = new ArrayList<DirectCopyIgnorePattern>();
        for (DirectCopyIgnorePattern pattern : ignorePatterns) {
          boolean isPatternMatch = false;
          for (String patternString : pattern.getPatterns()) {
            isPatternMatch = convertedClassName.matches(patternString);
            if (isPatternMatch) {
              break;
            }
          }
          if (isPatternMatch) {
            matchedPatterns.add(pattern);
          }
          isValidPattern = !(isPatternMatch && pattern.getTemplateTokenPatterns() == null);
          if (!isValidPattern) {
            return null;
          }
        }
        if (isValidPattern) {
          classPool = ClassPool.getDefault();
          clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
          List<?> attributes = clazz.getClassFile().getAttributes();
          Iterator<?> itr = attributes.iterator();
          List<String> templates = new ArrayList<String>();
          List<Boolean> skips = new ArrayList<Boolean>();
          List<Boolean> renames = new ArrayList<Boolean>();
          check:
          {
            while (itr.hasNext()) {
              Object object = itr.next();
              if (AnnotationsAttribute.class.isAssignableFrom(object.getClass())) {
                AnnotationsAttribute attr = (AnnotationsAttribute) object;
                Annotation[] items = attr.getAnnotations();
                for (Annotation annotation : items) {
                  String typeName = annotation.getTypeName();
                  if (typeName.equals(DirectCopyTransform.class.getName())) {
                    ArrayMemberValue arrayMember =
                        (ArrayMemberValue) annotation.getMemberValue("value");
                    for (MemberValue arrayMemberValue : arrayMember.getValue()) {
                      AnnotationMemberValue member = (AnnotationMemberValue) arrayMemberValue;
                      Annotation memberAnnot = member.getValue();
                      ArrayMemberValue annot =
                          (ArrayMemberValue) memberAnnot.getMemberValue("templateTokens");
                      for (MemberValue memberValue : annot.getValue()) {
                        String val = ((StringMemberValue) memberValue).getValue();
                        if (val != null && templateTokens.containsKey(val)) {
                          templateCheck:
                          {
                            for (DirectCopyIgnorePattern matchedPattern : matchedPatterns) {
                              for (String ignoreToken : matchedPattern.getTemplateTokenPatterns()) {
                                if (val.matches(ignoreToken)) {
                                  break templateCheck;
                                }
                              }
                            }
                            templates.add(templateTokens.get(val));
                          }
                        }
                      }
                      BooleanMemberValue skipAnnot =
                          (BooleanMemberValue) memberAnnot.getMemberValue("skipOverlaps");
                      if (skipAnnot != null) {
                        skips.add(skipAnnot.getValue());
                      } else {
                        skips.add(mySkipOverlaps);
                      }
                      BooleanMemberValue renameAnnot =
                          (BooleanMemberValue) memberAnnot.getMemberValue("renameMethodOverlaps");
                      if (renameAnnot != null) {
                        renames.add(renameAnnot.getValue());
                      } else {
                        renames.add(myRenameMethodOverlaps);
                      }
                    }
                    xformVals = templates.toArray(new String[templates.size()]);
                    xformSkipOverlaps = skips.toArray(new Boolean[skips.size()]);
                    xformRenameMethodOverlaps = renames.toArray(new Boolean[renames.size()]);
                    break check;
                  }
                }
              }
            }
          }
        }
      }
      if (xformVals != null && xformVals.length > 0) {
        logger.lifecycle(
            LifeCycleEvent.START,
            String.format(
                "Transform - Copying into [%s] from [%s]",
                xformKey, StringUtils.join(xformVals, ",")));
        // Load the destination class and defrost it so it is eligible for modifications
        clazz.defrost();

        int index = 0;
        for (String xformVal : xformVals) {
          // Load the source class
          String trimmed = xformVal.trim();
          classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader()));
          CtClass template = classPool.get(trimmed);

          // Add in extra interfaces
          CtClass[] interfacesToCopy = template.getInterfaces();
          for (CtClass i : interfacesToCopy) {
            checkInterfaces:
            {
              CtClass[] myInterfaces = clazz.getInterfaces();
              for (CtClass myInterface : myInterfaces) {
                if (myInterface.getName().equals(i.getName())) {
                  if (xformSkipOverlaps[index]) {
                    break checkInterfaces;
                  } else {
                    throw new RuntimeException(
                        "Duplicate interface detected " + myInterface.getName());
                  }
                }
              }
              logger.debug(String.format("Adding interface [%s]", i.getName()));
              clazz.addInterface(i);
            }
          }

          // copy over any EntityListeners
          ClassFile classFile = clazz.getClassFile();
          ClassFile templateFile = template.getClassFile();
          ConstPool constantPool = classFile.getConstPool();
          buildClassLevelAnnotations(classFile, templateFile, constantPool);

          // Copy over all declared fields from the template class
          // Note that we do not copy over fields with the @NonCopiedField annotation
          CtField[] fieldsToCopy = template.getDeclaredFields();
          for (CtField field : fieldsToCopy) {
            if (field.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding field [%s]", field.getName()));
            } else {
              try {
                CtField ctField = clazz.getDeclaredField(field.getName());
                String originalSignature = ctField.getSignature();
                String mySignature = field.getSignature();
                if (!originalSignature.equals(mySignature)) {
                  throw new IllegalArgumentException(
                      "Field with name ("
                          + field.getName()
                          + ") and signature "
                          + "("
                          + field.getSignature()
                          + ") is targeted for weaving into ("
                          + clazz.getName()
                          + "). "
                          + "An incompatible field of the same name and signature of ("
                          + ctField.getSignature()
                          + ") "
                          + "already exists. The field in the target class should be updated to a different name, "
                          + "or made to have a matching type.");
                }
                if (xformSkipOverlaps[index]) {
                  logger.debug(String.format("Skipping overlapped field [%s]", field.getName()));
                  continue;
                }
              } catch (NotFoundException e) {
                // do nothing -- field does not exist
              }
              logger.debug(String.format("Adding field [%s]", field.getName()));
              CtField copiedField = new CtField(field, clazz);

              boolean defaultConstructorFound = false;

              String implClass = getImplementationType(field.getType().getName());

              // Look through all of the constructors in the implClass to see
              // if there is one that takes zero parameters
              try {
                CtConstructor[] implConstructors = classPool.get(implClass).getConstructors();
                if (implConstructors != null) {
                  for (CtConstructor cons : implConstructors) {
                    if (cons.getParameterTypes().length == 0) {
                      defaultConstructorFound = true;
                      break;
                    }
                  }
                }
              } catch (NotFoundException e) {
                // Do nothing -- if we don't find this implementation, it's probably because it's
                // an array. In this case, we will not initialize the field.
              }

              if (defaultConstructorFound) {
                clazz.addField(copiedField, "new " + implClass + "()");
              } else {
                clazz.addField(copiedField);
              }
            }
          }

          // Copy over all declared methods from the template class
          CtMethod[] methodsToCopy = template.getDeclaredMethods();
          for (CtMethod method : methodsToCopy) {
            if (method.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding method [%s]", method.getName()));
            } else {
              try {
                CtClass[] paramTypes = method.getParameterTypes();
                CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes);

                if (xformSkipOverlaps[index]) {
                  logger.debug(
                      String.format(
                          "Skipping overlapped method [%s]", methodDescription(originalMethod)));
                  continue;
                }

                if (transformedMethods.contains(methodDescription(originalMethod))) {
                  throw new RuntimeException(
                      "Method already replaced " + methodDescription(originalMethod));
                } else {
                  logger.debug(
                      String.format("Marking as replaced [%s]", methodDescription(originalMethod)));
                  transformedMethods.add(methodDescription(originalMethod));
                }

                logger.debug(String.format("Removing method [%s]", method.getName()));
                if (xformRenameMethodOverlaps[index]) {
                  originalMethod.setName(renameMethodPrefix + method.getName());
                } else {
                  clazz.removeMethod(originalMethod);
                }
              } catch (NotFoundException e) {
                // Do nothing -- we don't need to remove a method because it doesn't exist
              }

              logger.debug(String.format("Adding method [%s]", method.getName()));
              CtMethod copiedMethod = new CtMethod(method, clazz, null);
              clazz.addMethod(copiedMethod);
            }
          }
          index++;
        }

        if (xformTemplates.isEmpty()) {
          annotationTransformedClasses.add(convertedClassName);
        }
        logger.lifecycle(
            LifeCycleEvent.END,
            String.format(
                "Transform - Copying into [%s] from [%s]",
                xformKey, StringUtils.join(xformVals, ",")));
        return clazz.toBytecode();
      }
    } catch (ClassCircularityError error) {
      error.printStackTrace();
      throw error;
    } catch (Exception e) {
      throw new RuntimeException("Unable to transform class", e);
    } finally {
      if (clazz != null) {
        clazz.detach();
      }
    }

    return null;
  }
  @Override
  public byte[] transform(
      ClassLoader loader,
      String className,
      Class<?> classBeingRedefined,
      ProtectionDomain protectionDomain,
      byte[] classfileBuffer)
      throws IllegalClassFormatException {
    String convertedClassName = className.replace('/', '.');

    if (xformTemplates.containsKey(convertedClassName)) {
      String xformKey = convertedClassName;
      String[] xformVals = xformTemplates.get(xformKey).split(",");
      logger.lifecycle(
          LifeCycleEvent.START,
          String.format(
              "Transform - Copying into [%s] from [%s]",
              xformKey, StringUtils.join(xformVals, ",")));

      try {
        // Load the destination class and defrost it so it is eligible for modifications
        ClassPool classPool = ClassPool.getDefault();
        CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
        clazz.defrost();

        for (String xformVal : xformVals) {
          // Load the source class
          String trimmed = xformVal.trim();
          classPool.appendClassPath(new LoaderClassPath(Class.forName(trimmed).getClassLoader()));
          CtClass template = classPool.get(trimmed);

          // Add in extra interfaces
          CtClass[] interfacesToCopy = template.getInterfaces();
          for (CtClass i : interfacesToCopy) {
            logger.debug(String.format("Adding interface [%s]", i.getName()));
            clazz.addInterface(i);
          }

          // Copy over all declared fields from the template class
          // Note that we do not copy over fields with the @NonCopiedField annotation
          CtField[] fieldsToCopy = template.getDeclaredFields();
          for (CtField field : fieldsToCopy) {
            if (field.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding field [%s]", field.getName()));
            } else {
              logger.debug(String.format("Adding field [%s]", field.getName()));
              CtField copiedField = new CtField(field, clazz);

              boolean defaultConstructorFound = false;

              String implClass = getImplementationType(field.getType().getName());

              // Look through all of the constructors in the implClass to see
              // if there is one that takes zero parameters
              try {
                CtConstructor[] implConstructors = classPool.get(implClass).getConstructors();
                if (implConstructors != null) {
                  for (CtConstructor cons : implConstructors) {
                    if (cons.getParameterTypes().length == 0) {
                      defaultConstructorFound = true;
                      break;
                    }
                  }
                }
              } catch (NotFoundException e) {
                // Do nothing -- if we don't find this implementation, it's probably because it's
                // an array. In this case, we will not initialize the field.
              }

              if (defaultConstructorFound) {
                clazz.addField(copiedField, "new " + implClass + "()");
              } else {
                clazz.addField(copiedField);
              }
            }
          }

          // Copy over all declared methods from the template class
          CtMethod[] methodsToCopy = template.getDeclaredMethods();
          for (CtMethod method : methodsToCopy) {
            if (method.hasAnnotation(NonCopied.class)) {
              logger.debug(String.format("Not adding method [%s]", method.getName()));
            } else {
              try {
                CtClass[] paramTypes = method.getParameterTypes();
                CtMethod originalMethod = clazz.getDeclaredMethod(method.getName(), paramTypes);

                if (transformedMethods.contains(methodDescription(originalMethod))) {
                  throw new RuntimeException(
                      "Method already replaced " + methodDescription(originalMethod));
                } else {
                  logger.debug(
                      String.format("Marking as replaced [%s]", methodDescription(originalMethod)));
                  transformedMethods.add(methodDescription(originalMethod));
                }

                logger.debug(String.format("Removing method [%s]", method.getName()));
                clazz.removeMethod(originalMethod);
              } catch (NotFoundException e) {
                // Do nothing -- we don't need to remove a method because it doesn't exist
              }

              logger.debug(String.format("Adding method [%s]", method.getName()));
              CtMethod copiedMethod = new CtMethod(method, clazz, null);
              clazz.addMethod(copiedMethod);
            }
          }
        }

        logger.lifecycle(
            LifeCycleEvent.END,
            String.format(
                "Transform - Copying into [%s] from [%s]",
                xformKey, StringUtils.join(xformVals, ",")));
        return clazz.toBytecode();
      } catch (Exception e) {
        throw new RuntimeException("Unable to transform class", e);
      }
    }

    return null;
  }
예제 #16
0
  private void enhance_(ApplicationClass applicationClass, boolean buildAuthorityRegistryOnly)
      throws Exception {
    Plugin.trace("about to enhance applicationClass: %s", applicationClass);
    CtClass ctClass = makeClass(applicationClass);
    Set<CtBehavior> s = new HashSet<CtBehavior>();
    s.addAll(Arrays.asList(ctClass.getDeclaredMethods()));
    s.addAll(Arrays.asList(ctClass.getMethods()));
    s.addAll(Arrays.asList(ctClass.getConstructors()));
    s.addAll(Arrays.asList(ctClass.getDeclaredConstructors()));
    for (final CtBehavior ctBehavior : s) {
      if (!Modifier.isPublic(ctBehavior.getModifiers())
          || javassist.Modifier.isAbstract(ctBehavior.getModifiers())) {
        continue;
      }

      boolean needsEnhance = false;
      RequireRight rr = null;
      RequirePrivilege rp = null;
      RequireAccounting ra = null;
      boolean allowSystem = false;
      Object[] aa = ctBehavior.getAnnotations();
      for (Object o : aa) {
        if (o instanceof RequirePrivilege) {
          needsEnhance = true;
          rp = (RequirePrivilege) o;
          continue;
        }
        if (o instanceof RequireRight) {
          needsEnhance = true;
          rr = (RequireRight) o;
          continue;
        }
        if (o instanceof AllowSystemAccount) {
          allowSystem = true;
          continue;
        }
        if (o instanceof RequireAccounting) {
          needsEnhance = true;
          ra = (RequireAccounting) o;
        }
      }
      if (!needsEnhance) continue;

      String key = ctBehavior.getLongName();
      String errMsg = String.format("Error enhancing class %s.%s: ", ctClass, ctBehavior);
      // process rr & rp
      if (null != rr || null != rp) {
        // check before/after enhancement
        Authority.registAuthoriable_(key, rr, rp);
        if (!buildAuthorityRegistryOnly) {
          // verify if before attribute of rr and rp is consistent
          if (null != rr && null != rp && (rr.before() != rp.before())) {
            String reason = "The before setting of RequireRight and RequirePrivilege doesn't match";
            throw new RuntimeException(errMsg + reason);
          }
          boolean before = true;
          if (null != rr) before = rr.before();
          if (null != rp) before = rp.before();
          // try best to guess the target object
          String curObj = "";
          if (null != rr) {
            // target object only impact dynamic access checking, hence rr shall not be null
            boolean isConstructor = ctBehavior instanceof CtConstructor;
            boolean isStatic = false;
            if (!isConstructor) isStatic = Modifier.isStatic(ctBehavior.getModifiers());
            int paraCnt = ctBehavior.getParameterTypes().length;
            int id = rr.target();
            // calibrate target id
            if (0 == id) {
              if (isConstructor) {
                id = -1;
              } else if (isStatic) {
                if (paraCnt > 0) id = 1;
                else id = -1;
              }
            } else if (id > paraCnt) {
              id = paraCnt;
            }
            // speculate cur target statement
            String sid = null;
            if (id == -1) sid = "_";
            if (id > -1) sid = String.valueOf(id);
            if (null != sid) {
              curObj =
                  "play.modules.aaa.PlayDynamicRightChecker.setObjectIfNoCurrent($" + sid + ");";
            }

            if (-1 == id) before = false;
          }
          // check permission enhancement
          if (before) {
            ctBehavior.insertBefore(
                curObj
                    + " play.modules.aaa.enhancer.Enhancer.Authority.checkPermission(\""
                    + key
                    + "\", "
                    + Boolean.toString(allowSystem)
                    + ");");
          } else {
            ctBehavior.insertAfter(
                curObj
                    + " play.modules.aaa.enhancer.Enhancer.Authority.checkPermission(\""
                    + key
                    + "\", "
                    + Boolean.toString(allowSystem)
                    + ");");
          }
        }
      }

      if (buildAuthorityRegistryOnly) continue;

      // process ra
      if (null != ra) {
        CtClass[] paraTypes = ctBehavior.getParameterTypes();
        String sParam = null;
        if (0 < paraTypes.length) {
          sParam = "new Object[0]";
        } else {
          sParam = "{$$}";
        }
        String msg = ra.value();
        if (null == msg || "".equals(msg)) msg = key;
        if (ra.before()) {
          ctBehavior.insertBefore(
              "play.modules.aaa.utils.Accounting.info(\""
                  + msg
                  + "\", "
                  + Boolean.toString(allowSystem)
                  + ", "
                  + sParam
                  + ");");
        } else {
          ctBehavior.insertAfter(
              "play.modules.aaa.utils.Accounting.info(\""
                  + msg
                  + "\", "
                  + Boolean.toString(allowSystem)
                  + ", "
                  + sParam
                  + ");");
        }
        CtClass etype = ClassPool.getDefault().get("java.lang.Exception");
        ctBehavior.addCatch(
            "{play.modules.aaa.utils.Accounting.error($e, \""
                + msg
                + "\", "
                + Boolean.toString(allowSystem)
                + ", "
                + sParam
                + "); throw $e;}",
            etype);
      }
    }

    if (buildAuthorityRegistryOnly) return;

    applicationClass.enhancedByteCode = ctClass.toBytecode();
    ctClass.detach();
  }
  @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);
    }
  }
  public byte[] injectReactive(String classname) throws Exception {
    init();

    CtClass droolsPojo = cp.get(classname);
    if (collectReactiveFields(droolsPojo).size() == 0) {
      LOG.info(
          "Skipped bytecode injection in class "
              + droolsPojo.getName()
              + " because no fields candidated for reactivity.");
      return droolsPojo.toBytecode();
    }

    droolsPojo.addInterface(cp.get(ReactiveObject.class.getName()));

    CtField ltsCtField =
        new CtField(cp.get(Collection.class.getName()), DROOLS_LIST_OF_TUPLES, droolsPojo);
    ltsCtField.setModifiers(Modifier.PRIVATE);
    ClassType listOfTuple =
        new SignatureAttribute.ClassType(
            Collection.class.getName(),
            new TypeArgument[] {
              new TypeArgument(new SignatureAttribute.ClassType(Tuple.class.getName()))
            });
    ltsCtField.setGenericSignature(listOfTuple.encode());
    // Do not use the Initializer.byNew... as those method always pass at least 1 parameter which is
    // "this".
    droolsPojo.addField(ltsCtField, Initializer.byExpr("new java.util.HashSet();"));

    final CtMethod getLeftTuplesCtMethod =
        CtNewMethod.make(
            "public java.util.Collection getLeftTuples() {\n"
                + "    return this.$$_drools_lts != null ? this.$$_drools_lts : java.util.Collections.emptyList();\n"
                + "}",
            droolsPojo);
    MethodSignature getLeftTuplesSignature = new MethodSignature(null, null, listOfTuple, null);
    getLeftTuplesCtMethod.setGenericSignature(getLeftTuplesSignature.encode());
    droolsPojo.addMethod(getLeftTuplesCtMethod);

    final CtMethod addLeftTupleCtMethod =
        CtNewMethod.make(
            "public void addLeftTuple("
                + Tuple.class.getName()
                + " leftTuple) {\n"
                + "    if ($$_drools_lts == null) {\n"
                + "        $$_drools_lts = new java.util.HashSet();\n"
                + "    }\n"
                + "    $$_drools_lts.add(leftTuple);\n"
                + "}",
            droolsPojo);
    droolsPojo.addMethod(addLeftTupleCtMethod);

    final CtMethod removeLeftTupleCtMethod =
        CtNewMethod.make(
            "public void removeLeftTuple("
                + Tuple.class.getName()
                + " leftTuple) {\n"
                + "    $$_drools_lts.remove(leftTuple);\n"
                + "}",
            droolsPojo);
    droolsPojo.addMethod(removeLeftTupleCtMethod);

    Map<String, CtField> fieldsMap = collectReactiveFields(droolsPojo);
    for (CtField f : fieldsMap.values()) {
      LOG.debug("Preparing field writer method for field: {}.", f);
      writeMethods.put(f.getName(), makeWriter(droolsPojo, f));
    }

    enhanceAttributesAccess(fieldsMap, droolsPojo);

    // first call CtClass.toClass() before the original class is loaded, it will persist the
    // bytecode instrumentation changes in the classloader.
    return droolsPojo.toBytecode();
  }
예제 #19
0
  public Class<?> toClass(
      ToClassInvokerPoolReference pool,
      CtClass cc,
      String classFileName,
      ClassLoader loader,
      ProtectionDomain domain)
      throws CannotCompileException {
    boolean trace = logger.isTraceEnabled();
    pool.lockInCache(cc);
    final ClassLoader myloader = pool.getClassLoader();
    if (myloader == null || tmpDir == null) {
      if (trace)
        logger.trace(
            this
                + " "
                + pool
                + ".toClass() myloader:"
                + myloader
                + " tmpDir:"
                + tmpDir
                + " default to superPool.toClass for "
                + cc.getName());
      Class<?> clazz = pool.superPoolToClass(cc, loader, domain);
      if (trace)
        logger.trace(this + " " + pool + " myloader:" + myloader + " created class:" + clazz);
      return clazz;
    }
    Class<?> dynClass = null;
    try {
      File classFile = null;
      // Write the clas file to the tmpdir
      synchronized (tmplock) {
        classFile = new File(tmpDir, classFileName);
        if (trace)
          logger.trace(
              this
                  + " "
                  + pool
                  + ".toClass() myloader:"
                  + myloader
                  + " writing bytes to "
                  + classFile);
        File pkgDirs = classFile.getParentFile();
        pkgDirs.mkdirs();
        FileOutputStream stream = new FileOutputStream(classFile);
        stream.write(cc.toBytecode());
        stream.flush();
        stream.close();
        classFile.deleteOnExit();
      }
      // We have to clear Blacklist caches or the class will never
      // be found
      // ((UnifiedClassLoader)dcl).clearBlacklists();
      // To be backward compatible
      RepositoryClassLoader rcl = (RepositoryClassLoader) myloader;
      rcl.clearClassBlackList();
      rcl.clearResourceBlackList();

      // Now load the class through the cl
      dynClass = myloader.loadClass(cc.getName());
      if (trace)
        logger.trace(this + " " + pool + " myloader:" + myloader + " created class:" + dynClass);
      return dynClass;
    } catch (Exception ex) {
      ClassFormatError cfe = new ClassFormatError("Failed to load dyn class: " + cc.getName());
      cfe.initCause(ex);
      throw cfe;
    }
  }