@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(); }
/** * Filters the classes to be transformed. * * @param definition * @param classMetaData the meta-data for the class * @param cg the class to filter * @return boolean true if the method should be filtered away */ private boolean classFilter( final SystemDefinition definition, final ClassMetaData classMetaData, final CtClass cg) { if (cg.isInterface()) { return true; } String className = cg.getName().replace('/', '.'); if (definition.inExcludePackage(className)) { return true; } if (!definition.inIncludePackage(className)) { return true; } if (definition.hasCallPointcut(classMetaData)) { return false; } return true; }
@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(); }
private CtClass instrumentClass( AspectManager manager, AOPClassPool pool, CtClass clazz, boolean isLoadedClass) throws NotFoundException, Exception { if (pool.isClassLoadedButNotWoven(clazz.getName())) { return null; } try { CtClass superClass = clazz.getSuperclass(); if (superClass != null && !Instrumentor.implementsAdvised(clazz)) { ClassPool superPool = superClass.getClassPool(); if (superPool instanceof AOPClassPool) { AspectManager aspectManager = manager; if (manager instanceof Domain && superPool != pool) { // We are in a scoped classloader and the superclass is not aspectManager = AspectManager.instance(superPool.getClassLoader()); } instrumentClass(aspectManager, (AOPClassPool) superPool, superClass, false); } } if (manager.isNonAdvisableClassName(clazz.getName())) { return null; } if (clazz.isArray()) { if (verbose && logger.isDebugEnabled()) logger.debug("cannot compile, isArray: " + clazz.getName()); pool.flushClass(clazz.getName()); return null; } if (clazz.isInterface()) { if (verbose && logger.isDebugEnabled()) logger.debug("cannot compile, isInterface: " + clazz.getName()); // pool.flushClass(info.getClassName()); clazz.prune(); return null; } if (clazz.isFrozen()) { if (isAdvised(pool, clazz)) return null; if (verbose && logger.isDebugEnabled()) logger.debug("warning, isFrozen: " + clazz.getName() + " " + clazz.getClassPool()); if (!isLoadedClass) { // What's the point of this? clazz = obtainCtClassInfo(pool, clazz.getName(), null); } else return null; // info.getClazz().defrost(); } boolean transformed = clazz.isModified(); if (!transformed) { ClassAdvisor advisor = AdvisorFactory.getClassAdvisor(clazz, manager); Instrumentor instrumentor = InstrumentorFactory.getInstrumentor( pool, manager, manager.dynamicStrategy.getJoinpointClassifier(), manager.dynamicStrategy.getDynamicTransformationObserver(clazz)); if (!Instrumentor.isTransformable(clazz)) { if (verbose && logger.isDebugEnabled()) logger.debug("cannot compile, implements Untransformable: " + clazz.getName()); // Flushing the generated invocation classes breaks things further down the line // pool.flushClass(info.getClassName()); return null; } manager.attachMetaData(advisor, clazz, true); manager.applyInterfaceIntroductions(advisor, clazz); transformed = instrumentor.transform(clazz, advisor); } if (transformed) { return clazz; } if (isLoadedClass) { pool.setClassLoadedButNotWoven(clazz.getName()); } return null; } catch (Exception e) { throw new RuntimeException("Error converting class ", e); } finally { } }
@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(); }
/** * Given a bean-style interface, generate an instance of the interface by implementing getters and * setters for each property. It will also add implementations to support the {@link * SupportsRdfId} interface and generate simple, default equals, toString and hashCode methods. * * <p>If there are other non-bean style (getter and/or setter's for properties) methods on the * interface, this will likely fail to generate the instance. * * @param theInterface the interface to build an instance of * @param <T> the type of the interface * @return New dynamically generated bytecode of a class that implements the given interface. * @throws Exception if there is an error while generating the bytecode of the new class. */ public static synchronized <T> Class<T> generateInstanceClass(Class<T> theInterface) throws Exception { processedMethods.clear(); // TODO: can we use some sort of template language for this? ClassPool aPool = ClassPool.getDefault(); aPool.appendClassPath(new LoaderClassPath(theInterface.getClassLoader())); CtClass aInterface = aPool.get(theInterface.getName()); CtClass aSupportsRdfIdInterface = aPool.get(SupportsRdfId.class.getName()); CtClass aEmpireGeneratedInterface = aPool.get(EmpireGenerated.class.getName()); if (!Arrays.asList(aInterface.getInterfaces()).contains(aSupportsRdfIdInterface) && !SupportsRdfId.class.isAssignableFrom(theInterface)) { throw new IllegalArgumentException( "Class '" + theInterface.getName() + "' does not implement SupportsRdfId, cannot generate Empire suitable implementation."); } String aName = aInterface.getPackageName() + ".impl." + aInterface.getSimpleName() + "Impl"; CtClass aClass = null; try { // i had a good reason for doing this, but i dont remember what it is. when i do, i'll // explain it here =) aClass = aPool.get(aName); return (Class<T>) BeanReflectUtil.loadClass(aName); } catch (NotFoundException e) { aClass = aPool.makeClass( aInterface.getPackageName() + ".impl." + aInterface.getSimpleName() + "Impl"); } catch (ClassNotFoundException e) { throw new Exception("Previously created class cannot be loaded.", e); } if (aClass.isFrozen()) { aClass.defrost(); } if (aInterface.isInterface()) { aClass.addInterface(aInterface); } else { aClass.setSuperclass(aInterface); } aClass.addInterface(aSupportsRdfIdInterface); aClass.addInterface(aEmpireGeneratedInterface); CtField aInterfaceField = new CtField(aPool.get(Class.class.getName()), "mInterfaceClass", aClass); aClass.addField( aInterfaceField, CtField.Initializer.byExpr(theInterface.getName() + ".class;")); CtField aAllTriplesField = new CtField(aPool.get(Graph.class.getName()), "mAllTriples", aClass); aClass.addField( aAllTriplesField, CtField.Initializer.byExpr("new com.clarkparsia.openrdf.ExtGraph();")); CtField aInstanceTriplesField = new CtField(aPool.get(Graph.class.getName()), "mInstanceTriples", aClass); aClass.addField( aInstanceTriplesField, CtField.Initializer.byExpr("new com.clarkparsia.openrdf.ExtGraph();")); aClass.addConstructor(CtNewConstructor.defaultConstructor(aClass)); generateMethods(theInterface, aPool, aClass); generateMethodsForSuperInterfaces(theInterface, aPool, aClass); CtField aIdField = new CtField(aPool.get(SupportsRdfId.class.getName()), "supportsId", aClass); aClass.addField( aIdField, CtField.Initializer.byExpr("new com.clarkparsia.empire.annotation.SupportsRdfIdImpl();")); if (!hasMethod(aClass, "getRdfId")) { aClass.addMethod( CtNewMethod.make( "public com.clarkparsia.empire.SupportsRdfId.RdfKey getRdfId() { return supportsId.getRdfId(); } ", aClass)); } if (!hasMethod(aClass, "setRdfId")) { aClass.addMethod( CtNewMethod.make( "public void setRdfId(com.clarkparsia.empire.SupportsRdfId.RdfKey theURI) { supportsId.setRdfId(theURI); } ", aClass)); } if (!hasMethod(aClass, "getAllTriples")) { aClass.addMethod( CtNewMethod.make( "public org.openrdf.model.Graph getAllTriples() { return mAllTriples; } ", aClass)); } if (!hasMethod(aClass, "setAllTriples")) { aClass.addMethod( CtNewMethod.make( "public void setAllTriples(org.openrdf.model.Graph theGraph) { mAllTriples = theGraph; } ", aClass)); } if (!hasMethod(aClass, "getInstanceTriples")) { aClass.addMethod( CtNewMethod.make( "public org.openrdf.model.Graph getInstanceTriples() { return mInstanceTriples; } ", aClass)); } if (!hasMethod(aClass, "setInstanceTriples")) { aClass.addMethod( CtNewMethod.make( "public void setInstanceTriples(org.openrdf.model.Graph theGraph) { mInstanceTriples = theGraph; } ", aClass)); } String equalsMethodBody = "public boolean equals(Object theObj) {\n" + " if (theObj == this) return true;\n" + " if (!(theObj instanceof com.clarkparsia.empire.SupportsRdfId)) return false;\n" + " if (!(mInterfaceClass.isAssignableFrom(theObj.getClass()))) return false;\n" + " return getRdfId().equals( ((com.clarkparsia.empire.SupportsRdfId) theObj).getRdfId()) && super.equals(theObj);\n" + "}\n"; aClass.addMethod(CtNewMethod.make(equalsMethodBody, aClass)); if (theInterface.isInterface()) { aClass.addMethod( CtNewMethod.make( "public String toString() { return getRdfId() != null ? getRdfId().toString() : super.toString(); } ", aClass)); aClass.addMethod( CtNewMethod.make( "public int hashCode() { return getRdfId() != null ? getRdfId().hashCode() : 0; } ", aClass)); } aClass.freeze(); Class<T> aResult = (Class<T>) aClass.toClass(); try { // make sure this is a valid class, that is, we can create instances of it! aResult.newInstance(); } catch (Exception ex) { // TODO: log this? throw ex; } return aResult; }