package io.trygvis.container.compiler; import io.trygvis.container.compiler.model.ClassG; import io.trygvis.container.compiler.model.FieldRef; import io.trygvis.container.compiler.model.ParameterRef; import io.trygvis.container.compiler.model.Parameters; import io.trygvis.container.compiler.model.TypeRef; import io.trygvis.container.tx.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import static javax.lang.model.util.ElementFilter.constructorsIn; import static org.springframework.util.StringUtils.collectionToDelimitedString; public class TransactionalHandler { private final ProcessingEnvironment processingEnv; private final Elements elements; private final Types types; public TransactionalHandler(ProcessingEnvironment processingEnv) { this.processingEnv = processingEnv; this.elements = processingEnv.getElementUtils(); this.types = processingEnv.getTypeUtils(); } public void processTransactional(TypeElement element) throws IOException { Name targetClassName = element.getSimpleName(); System.out.println("Processing @Transactional " + targetClassName); Transactional transactional = element.getAnnotation(Transactional.class); String p = elements.getPackageOf(element).getQualifiedName().toString(); String className = targetClassName + "_Transactional"; JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(p + "." + className, element); try (PrintWriter w = new PrintWriter(sourceFile.openWriter())) { ClassG g = new ClassG(p, className, targetClassName.toString()); FieldRef transactionManager = g.addField(PlatformTransactionManager.class, "transactionManager"); for (ExecutableElement constructor : constructorsIn(elements.getAllMembers(element))) { if (!constructor.getModifiers().contains(Modifier.PUBLIC)) { continue; } constructor(g, constructor, transactionManager); } Name javaLangObjectName = elements.getTypeElement("java.lang.Object").getQualifiedName(); for (Element e : elements.getAllMembers(element)) { if (!(e instanceof ExecutableElement)) { continue; } ExecutableElement ee = (ExecutableElement) e; TypeElement enclosingElement = (TypeElement) ee.getEnclosingElement(); if (enclosingElement.getQualifiedName().equals(javaLangObjectName)) { continue; } if (ee.getSimpleName().toString().equals("")) { continue; } TypeMirror returnTypeMirror = ee.getReturnType(); TypeKind kind = returnTypeMirror.getKind(); boolean isVoid = kind == TypeKind.VOID; TypeRef returnType = isVoid ? TypeRef.VOID : g.addImport(returnTypeMirror); List parameters = new ArrayList<>(); List arguments = new ArrayList<>(); for (VariableElement ve : ee.getParameters()) { // parameters.add("final " + ve.asType().toString() + " " + ve.getSimpleName().toString()); TypeRef k = g.addImport(ve.asType()); parameters.add(new ParameterRef(k, ve.getSimpleName().toString())); arguments.add(ve.getSimpleName().toString()); } List body = new ArrayList<>(); body.add((isVoid ? "" : "return ") + "transactionManager.doInTransaction("); body.add(" PlatformTransactionManager.TransactionIsolation.ISOLATION_" + transactional.isolation() + ","); body.add(" PlatformTransactionManager.TransactionPropagation.PROPAGATION_" + transactional.propagation() + ","); body.add(" new PlatformTransactionManager.TransactionTemplate<" + (isVoid ? "Object" : returnTypeMirror) + ">() {"); body.add(" @Override"); body.add(" public " + (isVoid ? "Object" : returnTypeMirror) + " doInTransaction() {"); String targetInvocation = className + ".super." + ee.getSimpleName() + "(" + collectionToDelimitedString(arguments, ", ") + ");"; if (isVoid) { body.add(" " + targetInvocation); body.add(" return null;"); } else { body.add(" return " + targetInvocation); } body.add(" }"); body.add(" });"); g.addMethod(body, returnType, ee.getSimpleName().toString(), parameters.toArray(new ParameterRef[arguments.size()])); } g.write(w); } } private void constructor(ClassG g, ExecutableElement constructor, FieldRef platformTransactionManager) { Parameters parameters = new Parameters(); List goesToSuper = new ArrayList<>(); TypeRef k = g.addImport(PlatformTransactionManager.class); ParameterRef transactionManager = parameters.addParameter(k, "transactionManager"); for (VariableElement p : constructor.getParameters()) { k = g.addImport(p.asType()); String name = p.getSimpleName().toString(); parameters.addParameter(k, name); goesToSuper.add(name); } List body = new ArrayList<>(); body.add("super(" + collectionToDelimitedString(goesToSuper, ", ") + ");"); body.add("this." + platformTransactionManager.name + " = " + transactionManager.name + ";"); g.addConstructor(parameters, body); } }