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); }
/** * 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); }
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(); }