summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--container-compiler-plugin/pom.xml19
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java246
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java157
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/FieldRef.java31
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/MethodRef.java16
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ParameterRef.java11
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/TypeRef.java83
-rw-r--r--container-core/pom.xml32
-rw-r--r--container-core/src/main/java/io/trygvis/container/log/Log.java4
-rw-r--r--container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java188
-rw-r--r--container-core/src/main/java/io/trygvis/container/tx/TransactionHolder.java7
-rw-r--r--container-core/src/main/java/io/trygvis/container/tx/Tx.java5
-rw-r--r--myapp/pom.xml36
-rw-r--r--myapp/src/main/java/io/trygvis/container/myapp/MyComponent.java40
-rw-r--r--myapp/src/main/java/io/trygvis/container/myapp/MyComponent_Tx.java26
-rw-r--r--myapp/src/main/java/io/trygvis/container/myapp/MyMain.java10
-rw-r--r--pom.xml31
18 files changed, 945 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..612c5bc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+target
+.idea
+*.iml
diff --git a/container-compiler-plugin/pom.xml b/container-compiler-plugin/pom.xml
new file mode 100644
index 0000000..beb4b55
--- /dev/null
+++ b/container-compiler-plugin/pom.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>container-playground</artifactId>
+ <groupId>io.trygvis.container</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>container-compiler-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>io.trygvis.container</groupId>
+ <artifactId>container-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java
new file mode 100644
index 0000000..0ec7001
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java
@@ -0,0 +1,246 @@
+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.TypeRef;
+import io.trygvis.container.log.Log;
+import io.trygvis.container.tx.PlatformTransactionManager;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.processing.Completion;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+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.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptySet;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+import static org.springframework.util.StringUtils.collectionToDelimitedString;
+
+public class MyProcessor implements Processor {
+
+ private ProcessingEnvironment processingEnv;
+ private Elements elements;
+ private Types types;
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return emptySet();
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.RELEASE_5;
+ }
+
+ @Override
+ public void init(ProcessingEnvironment processingEnv) {
+ this.processingEnv = processingEnv;
+ elements = processingEnv.getElementUtils();
+ types = processingEnv.getTypeUtils();
+ }
+
+ @Override
+ public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
+ return emptyList();
+ }
+
+ @Override
+ public Set<String> getSupportedAnnotationTypes() {
+ return new HashSet<>(asList(Transactional.class.getName(), Log.class.getName()));
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ Set<? extends Element> transactional = roundEnv.getElementsAnnotatedWith(Transactional.class);
+
+ TypeElement tx = elements.getTypeElement(Transactional.class.getCanonicalName());
+ TypeElement log = elements.getTypeElement(Transactional.class.getCanonicalName());
+
+ for (Element element : transactional) {
+ try {
+ System.out.println("Processing: " + element);
+ for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+
+ System.out.println("annotationMirror.getAnnotationType().asElement().getSimpleName() = " + annotationMirror.getAnnotationType().asElement().getSimpleName());
+
+ if (types.isSameType(tx.asType(), annotationMirror.getAnnotationType())) {
+ processTransactional((TypeElement) element);
+ }
+// if (types.isSameType(log.asType(), annotationMirror.getAnnotationType())) {
+// processLog((TypeElement) element);
+// }
+ }
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return true;
+ }
+
+ private 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());
+// writer.println("package " + p + ";");
+// writer.println();
+// writer.println("import io.trygvis.container.tx.PlatformTransactionManager;");
+// writer.println();
+// writer.println("public class " + className + " extends " + targetClassName + " {");
+// writer.println(" private final PlatformTransactionManager transactionManager;");
+// writer.println();
+
+ TypeRef platformTransactionManager = g.addImport(PlatformTransactionManager.class);
+ FieldRef transactionManager = g.addField(PlatformTransactionManager.class, "transactionManager");
+
+ for (ExecutableElement constructor : constructorsIn(elements.getAllMembers(element))) {
+ if (!constructor.getModifiers().contains(Modifier.PUBLIC)) {
+ continue;
+ }
+
+ constructor(g, constructor);
+ }
+
+// System.out.println("elements.getTypeElement(\"Object\") = " + elements.getTypeElement("java.lang.Object"));
+ 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("<init>")) {
+ continue;
+ }
+
+// System.out.println("ee.getSimpleName() = " + ee.getSimpleName());
+// System.out.println("ee.getEnclosingElement() = " + enclosingElement);
+// System.out.println("ee.getEnclosingElement().getQualifiedName() = " + enclosingElement.getQualifiedName());
+// System.out.println("ee.getEnclosingElement().asType().getKind().getDeclaringClass() = " + enclosingElement.asType().getKind().getDeclaringClass());
+
+ TypeMirror returnTypeMirror = ee.getReturnType();
+
+ TypeKind kind = returnTypeMirror.getKind();
+
+ boolean isVoid = kind == TypeKind.VOID;
+ TypeRef returnType = isVoid ? TypeRef.VOID : g.addImport(returnTypeMirror);
+// String returnString;
+// if (isVoid) {
+// returnString = "void";
+// } else {
+// returnString = returnTypeMirror.toString();
+// }
+
+ List<ParameterRef> parameters = new ArrayList<>();
+ List<String> 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());
+ }
+
+ CharArrayWriter buffer = new CharArrayWriter();
+ PrintWriter m = new PrintWriter(buffer);
+ g.addMethod(buffer.toString(), returnType, ee.getSimpleName().toString(), parameters.toArray(new ParameterRef[arguments.size()]));
+
+// writer.println();
+// writer.println(" public " + returnString + " " + ee.getSimpleName() + "(" + collectionToDelimitedString(parameters, ", ") + ") {");
+
+/*
+ writer.println(" " + (isVoid ? "" : "return ") + "transactionManager.doInTransaction(");
+ writer.println(" PlatformTransactionManager.TransactionIsolation.ISOLATION_" + transactional.isolation() + ",");
+ writer.println(" PlatformTransactionManager.TransactionPropagation.PROPAGATION_" + transactional.propagation() + ",");
+ writer.println(" new PlatformTransactionManager.TransactionTemplate<" + (isVoid ? "Object" : returnTypeMirror) + ">() {");
+ writer.println(" @Override");
+ writer.println(" public " + (isVoid ? "Object" : returnTypeMirror) + " doInTransaction() {");
+ String targetInvocation = className + ".super." + ee.getSimpleName() + "(" + collectionToDelimitedString(arguments, ", ") + ");";
+
+ if (isVoid) {
+ writer.println(" " + targetInvocation);
+ writer.println(" return null;");
+ } else {
+ writer.print(" return ");
+ writer.println(targetInvocation);
+ }
+
+ writer.println(" }");
+ writer.println(" });");
+ writer.println(" }");
+*/
+
+ elements.printElements(new PrintWriter(System.out), ee);
+ }
+// writer.println("}");
+ g.write(w);
+ }
+ }
+
+ private void constructor(ClassG g, ExecutableElement constructor) {
+ List<ParameterRef> parameters = new ArrayList<>();
+ List<String> goesToSuper = new ArrayList<>();
+
+ TypeRef k = g.addImport(PlatformTransactionManager.class);
+ parameters.add(new ParameterRef(k, "_transactionManager_"));
+
+ for (VariableElement p : constructor.getParameters()) {
+// parameters.add(p.asType().toString() + " " + p.getSimpleName().toString());
+// arguments.add(p.getSimpleName().toString());
+ k = g.addImport(p.asType());
+ String name = p.getSimpleName().toString();
+ parameters.add(new ParameterRef(k, name));
+ goesToSuper.add(name);
+ }
+
+// String params = parameters.size() == 0 ? "" : ", " + collectionToDelimitedString(parameters, ", ");
+
+ g.simpleConstructor(parameters, goesToSuper);
+
+// writer.println(" public " + className + "(PlatformTransactionManager _transactionManager_" + params + ") {");
+// writer.println(" super(" + collectionToDelimitedString(arguments, ", ") + ");");
+// writer.println(" this.transactionManager = _transactionManager_;");
+// writer.println(" }");
+ }
+}
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java
new file mode 100644
index 0000000..6c0e246
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java
@@ -0,0 +1,157 @@
+package io.trygvis.container.compiler.model;
+
+import javax.lang.model.type.TypeMirror;
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static org.springframework.util.StringUtils.collectionToDelimitedString;
+
+public class ClassG {
+ private final String packageName;
+ private final String className;
+ private final String extendsClass;
+ private final Set<TypeRef> imports = new TreeSet<>();
+ private final Set<FieldRef> fields = new TreeSet<>();
+ private final List<MethodRef> methods = new ArrayList<>();
+
+// public ClassG(String packageName, String className) {
+// this(packageName, className, null);
+// }
+
+ public ClassG(String packageName, String className, String extendsClass) {
+ this.packageName = packageName;
+ this.className = className;
+ this.extendsClass = extendsClass;
+ }
+
+ public TypeRef addImport(final TypeMirror type) {
+ if (type.getKind().isPrimitive()) {
+ return TypeRef.find(type.getKind());
+ }
+
+ String canonicalName = type.toString();
+ System.out.println("canonicalName = " + canonicalName);
+
+ for (TypeRef i : imports) {
+ if (i.canonicalName().equals(canonicalName)) {
+ return i;
+ }
+ }
+
+ TypeRef ref = new TypeRef(canonicalName, canonicalName);
+ imports.add(ref);
+ return ref;
+ }
+
+ public TypeRef addImport(Class<?> klass) {
+// if (klass.isPrimitive()) {
+// return TypeRef.PRIMITIVE;
+// }
+
+ String canonicalName = klass.getCanonicalName();
+ String simpleName = klass.getSimpleName();
+ String name = simpleName;
+ for (TypeRef i : imports) {
+ if (i.canonicalName().equals(canonicalName)) {
+ return i;
+ }
+
+ if (i.name.equals(simpleName)) {
+ name = canonicalName;
+ break;
+ }
+ }
+
+ TypeRef ref = new TypeRef(name, canonicalName);
+ imports.add(ref);
+
+ return ref;
+ }
+
+ public FieldRef addField(Class<?> klass, String name) {
+ TypeRef type = addImport(klass);
+ FieldRef ref = new FieldRef(type, name);
+ fields.add(ref);
+ return ref;
+ }
+
+ public MethodRef addMethod(String body, TypeRef returnType, String name, ParameterRef... parameters) {
+ MethodRef ref = new MethodRef(returnType, name, parameters, body);
+ methods.add(ref);
+ return ref;
+ }
+
+ public void write(PrintWriter writer) {
+ writer.println("package " + packageName + ";");
+ writer.println();
+ for (TypeRef i : imports) {
+ if (i.isPrimitive()) {
+ continue;
+ }
+ writer.println("import " + i.canonicalName() + ";");
+ }
+ writer.println();
+ writer.println("public class " + className + " extends " + extendsClass + " {");
+ for (FieldRef field : fields) {
+ writer.println(" private final " + field.klass.name + " " + field.name + ";");
+ }
+
+ for (MethodRef method : methods) {
+ write(writer, method);
+ }
+
+ writer.println("}");
+ }
+
+ private void write(PrintWriter writer, MethodRef method) {
+ String returnString;
+ if (method.returnType == TypeRef.VOID) {
+ returnString = "void";
+ } else {
+ returnString = method.returnType.name;
+ }
+
+ List<String> parameters = new ArrayList<>();
+ for (ParameterRef p : method.parameters) {
+ parameters.add(p.klass.name + " " + p.name);
+ }
+
+ writer.println(" public " + returnString + " " + method.name + "(" + collectionToDelimitedString(parameters, ", ") + ") {");
+ writer.println(method.body);
+ writer.println(" }");
+ }
+
+ public void simpleConstructor(List<ParameterRef> parameters, List<String> goesToSuper) {
+ List<String> ps = new ArrayList<>();
+ for (ParameterRef p : parameters) {
+ ps.add(p.klass.name + " " + p.name);
+ }
+ List<String> ss = new ArrayList<>();
+ List<String> local = new ArrayList<>();
+ for (String s : goesToSuper) {
+ for (ParameterRef parameter : parameters) {
+ if (parameter.name.equals(s)) {
+ ss.add(parameter.name);
+ continue;
+ }
+ }
+ local.add(s);
+ }
+ String params = ps.size() == 0 ? "" : ", " + collectionToDelimitedString(ps, ", ");
+
+ CharArrayWriter buffer = new CharArrayWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+
+ writer.println(" public " + className + "(" + params + ") {");
+ writer.println(" super(" + collectionToDelimitedString(ss, ", ") + ");");
+// writer.println(" this.transactionManager = _transactionManager_;");
+ for (String s : local) {
+ writer.println(" this." + s + " = " + s);
+ }
+ writer.println(" }");
+ }
+}
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/FieldRef.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/FieldRef.java
new file mode 100644
index 0000000..29a2c51
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/FieldRef.java
@@ -0,0 +1,31 @@
+package io.trygvis.container.compiler.model;
+
+public class FieldRef implements Comparable<FieldRef> {
+ public final TypeRef klass;
+ public final String name;
+
+ public FieldRef(TypeRef klass, String name) {
+ this.klass = klass;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof FieldRef)) return false;
+
+ FieldRef fieldRef = (FieldRef) o;
+
+ return name.equals(fieldRef.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public int compareTo(FieldRef o) {
+ return name.compareTo(o.name);
+ }
+}
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/MethodRef.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/MethodRef.java
new file mode 100644
index 0000000..0b2cf89
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/MethodRef.java
@@ -0,0 +1,16 @@
+package io.trygvis.container.compiler.model;
+
+public class MethodRef {
+ public final TypeRef returnType;
+ public final String name;
+ public final ParameterRef[] parameters;
+ public final String body;
+
+ public
+ MethodRef(TypeRef returnType, String name, ParameterRef[] parameters, String body) {
+ this.returnType = returnType;
+ this.name = name;
+ this.parameters = parameters;
+ this.body = body;
+ }
+}
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ParameterRef.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ParameterRef.java
new file mode 100644
index 0000000..fc69cb1
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ParameterRef.java
@@ -0,0 +1,11 @@
+package io.trygvis.container.compiler.model;
+
+public class ParameterRef {
+ public final TypeRef klass;
+ public final String name;
+
+ public ParameterRef(TypeRef klass, String name) {
+ this.klass = klass;
+ this.name = name;
+ }
+}
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/TypeRef.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/TypeRef.java
new file mode 100644
index 0000000..aabfb45
--- /dev/null
+++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/TypeRef.java
@@ -0,0 +1,83 @@
+package io.trygvis.container.compiler.model;
+
+import javax.lang.model.type.TypeKind;
+
+public class TypeRef implements Comparable<TypeRef> {
+
+ public static final TypeRef VOID = new TypeRef("void", "void");
+ public static final TypeRef BOOLEAN = new TypeRef("boolean", null);
+ public static final TypeRef BYTE = new TypeRef("byte", null);
+ public static final TypeRef SHORT = new TypeRef("short", null);
+ public static final TypeRef CHAR = new TypeRef("char", null);
+ public static final TypeRef INT = new TypeRef("int", null);
+ public static final TypeRef LONG = new TypeRef("long", null);
+ public static final TypeRef FLOAT = new TypeRef("float", null);
+ public static final TypeRef DOUBLE = new TypeRef("double", null);
+
+ /**
+ * The name of a class used within a class file. Is either the simple name or the canonical name.
+ */
+ public final String name;
+
+ private final String canonicalName;
+
+ public String canonicalName() {
+ if (canonicalName == null) {
+ throw new RuntimeException("This type doesn't have a canonical name");
+ }
+
+ return canonicalName;
+ }
+
+ public boolean isPrimitive() {
+ return canonicalName == null;
+ }
+
+ public TypeRef(String name, String canonicalName) {
+ this.name = name;
+ this.canonicalName = canonicalName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TypeRef)) return false;
+
+ TypeRef classRef = (TypeRef) o;
+
+ return canonicalName.equals(classRef.canonicalName);
+ }
+
+ @Override
+ public int hashCode() {
+ return canonicalName.hashCode();
+ }
+
+ @Override
+ public int compareTo(TypeRef o) {
+ return canonicalName.compareTo(o.canonicalName);
+ }
+
+ public static TypeRef find(TypeKind kind) {
+ switch (kind) {
+ case BOOLEAN:
+ return BOOLEAN;
+ case BYTE:
+ return BYTE;
+ case SHORT:
+ return SHORT;
+ case CHAR:
+ return CHAR;
+ case INT:
+ return INT;
+ case LONG:
+ return LONG;
+ case FLOAT:
+ return FLOAT;
+ case DOUBLE:
+ return DOUBLE;
+ }
+
+ throw new RuntimeException("Unknown kind: " + kind);
+ }
+}
diff --git a/container-core/pom.xml b/container-core/pom.xml
new file mode 100644
index 0000000..e16c761
--- /dev/null
+++ b/container-core/pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>container-playground</artifactId>
+ <groupId>io.trygvis.container</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>container-core</artifactId>
+ <properties>
+ <version.spring>3.2.2.RELEASE</version.spring>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${version.spring}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-jdbc</artifactId>
+ <version>${version.spring}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ <version>${version.spring}</version>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/container-core/src/main/java/io/trygvis/container/log/Log.java b/container-core/src/main/java/io/trygvis/container/log/Log.java
new file mode 100644
index 0000000..71e81d1
--- /dev/null
+++ b/container-core/src/main/java/io/trygvis/container/log/Log.java
@@ -0,0 +1,4 @@
+package io.trygvis.container.log;
+
+public @interface Log {
+}
diff --git a/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java b/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java
new file mode 100644
index 0000000..3ab7b6f
--- /dev/null
+++ b/container-core/src/main/java/io/trygvis/container/tx/PlatformTransactionManager.java
@@ -0,0 +1,188 @@
+package io.trygvis.container.tx;
+
+public class PlatformTransactionManager {
+
+ public static Tx currentTx() {
+ return tx.get().tx();
+ }
+
+ public static interface TxFactory {
+ Tx create(TransactionIsolation isolation);
+ }
+
+ static final ThreadLocal<TxEntry> tx = new ThreadLocal<TxEntry>() {
+ @Override
+ protected TxEntry initialValue() {
+ return new RootTxState();
+ }
+ };
+
+ public abstract static class TxEntry {
+ abstract Tx tx();
+
+ abstract boolean isActive();
+
+ abstract TxEntry pop();
+ }
+
+ public static class NewTxTxEntry extends TxEntry {
+ private final TxEntry parent;
+ private final Tx tx;
+
+ public NewTxTxEntry(TxEntry parent, Tx tx) {
+ this.parent = parent;
+ this.tx = tx;
+ }
+
+ @Override
+ Tx tx() {
+ return tx;
+ }
+
+ public boolean isActive() {
+ return true;
+ }
+
+ @Override
+ TxEntry pop() {
+ return parent;
+ }
+ }
+
+ public static class NestedTxEntry extends TxEntry {
+ private final NestedTxEntry parent;
+
+ public NestedTxEntry(NestedTxEntry parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ Tx tx() {
+ return parent.tx();
+ }
+
+ public boolean isActive() {
+ return parent.isActive();
+ }
+
+ @Override
+ TxEntry pop() {
+ return parent;
+ }
+ }
+
+ private final static class InactiveTxEntry extends TxEntry {
+ private final TxEntry parent;
+
+ private InactiveTxEntry(TxEntry parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public Tx tx() {
+ throw new RuntimeException("No transaction currently running.");
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ TxEntry pop() {
+ return parent;
+ }
+ }
+
+ private final static class RootTxState extends TxEntry {
+ @Override
+ public Tx tx() {
+ throw new RuntimeException("No transaction currently running.");
+ }
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
+ @Override
+ TxEntry pop() {
+ throw new RuntimeException("Trying to pop root entry");
+ }
+ }
+
+ public enum TransactionIsolation {
+ ISOLATION_DEFAULT,
+ ISOLATION_READ_COMMITTED,
+ ISOLATION_READ_UNCOMMITTED,
+ ISOLATION_REPEATABLE_READ,
+ ISOLATION_SERIALIZABLE
+ }
+
+ public enum TransactionPropagation {
+ PROPAGATION_NESTED,
+ // PROPAGATION_NEVER,
+ PROPAGATION_NOT_SUPPORTED,
+ PROPAGATION_REQUIRED,
+ PROPAGATION_REQUIRES_NEW,
+ PROPAGATION_SUPPORTS
+ }
+
+ private final TxFactory txFactory;
+ private final TransactionPropagation defaultPropagation;
+
+ public PlatformTransactionManager(TxFactory txFactory, TransactionPropagation defaultPropagation) {
+ this.txFactory = txFactory;
+ this.defaultPropagation = defaultPropagation;
+ }
+
+ public <T> T doInTransaction(TransactionIsolation isolation, TransactionPropagation propagation, TransactionTemplate<T> transactionTemplate) {
+ final TxEntry prevEntry = PlatformTransactionManager.tx.get();
+ final TxEntry currentEntry = nextTxEntry(prevEntry, isolation, propagation);
+ PlatformTransactionManager.tx.set(currentEntry);
+
+ try {
+ return transactionTemplate.doInTransaction();
+ } finally {
+ PlatformTransactionManager.tx.set(prevEntry);
+ }
+ }
+
+ private TxEntry nextTxEntry(TxEntry prevEntry, TransactionIsolation isolation, TransactionPropagation propagation) {
+ TxEntry currentEntry;
+ switch (propagation) {
+ case PROPAGATION_NESTED:
+ throw new RuntimeException("Propagation 'nested' not supported.");
+ case PROPAGATION_NOT_SUPPORTED:
+ currentEntry = new InactiveTxEntry(prevEntry);
+ PlatformTransactionManager.tx.set(currentEntry);
+ break;
+ default:
+ throw new RuntimeException("Unknown transaction propagation: " + propagation);
+ case PROPAGATION_REQUIRED:
+ if (prevEntry.isActive()) {
+ currentEntry = new NestedTxEntry((NestedTxEntry) prevEntry);
+ } else {
+ Tx tx = txFactory.create(isolation);
+ currentEntry = new NewTxTxEntry(prevEntry, tx);
+ }
+ break;
+ case PROPAGATION_REQUIRES_NEW:
+ Tx tx = txFactory.create(isolation);
+ currentEntry = new NewTxTxEntry(prevEntry, tx);
+ break;
+ case PROPAGATION_SUPPORTS:
+ if (prevEntry.isActive()) {
+ currentEntry = new NestedTxEntry((NestedTxEntry) prevEntry);
+ } else {
+ currentEntry = new InactiveTxEntry(prevEntry);
+ }
+ break;
+ }
+ return currentEntry;
+ }
+
+ public interface TransactionTemplate<T> {
+ T doInTransaction();
+ }
+}
diff --git a/container-core/src/main/java/io/trygvis/container/tx/TransactionHolder.java b/container-core/src/main/java/io/trygvis/container/tx/TransactionHolder.java
new file mode 100644
index 0000000..9f7e441
--- /dev/null
+++ b/container-core/src/main/java/io/trygvis/container/tx/TransactionHolder.java
@@ -0,0 +1,7 @@
+package io.trygvis.container.tx;
+
+public class TransactionHolder {
+ public static Tx currentTx() {
+ return PlatformTransactionManager.currentTx();
+ }
+}
diff --git a/container-core/src/main/java/io/trygvis/container/tx/Tx.java b/container-core/src/main/java/io/trygvis/container/tx/Tx.java
new file mode 100644
index 0000000..8ff9d05
--- /dev/null
+++ b/container-core/src/main/java/io/trygvis/container/tx/Tx.java
@@ -0,0 +1,5 @@
+package io.trygvis.container.tx;
+
+public interface Tx {
+ void rollback();
+}
diff --git a/myapp/pom.xml b/myapp/pom.xml
new file mode 100644
index 0000000..7253422
--- /dev/null
+++ b/myapp/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>container-playground</artifactId>
+ <groupId>io.trygvis.container</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>myapp</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>io.trygvis.container</groupId>
+ <artifactId>container-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.trygvis.container</groupId>
+ <artifactId>container-compiler-plugin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <compilerArguments>
+ <processor>io.trygvis.container.compiler.MyProcessor</processor>
+ </compilerArguments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/myapp/src/main/java/io/trygvis/container/myapp/MyComponent.java b/myapp/src/main/java/io/trygvis/container/myapp/MyComponent.java
new file mode 100644
index 0000000..cf30c3b
--- /dev/null
+++ b/myapp/src/main/java/io/trygvis/container/myapp/MyComponent.java
@@ -0,0 +1,40 @@
+package io.trygvis.container.myapp;
+
+import io.trygvis.container.log.Log;
+import io.trygvis.container.tx.Tx;
+import org.springframework.transaction.annotation.Transactional;
+
+import static io.trygvis.container.tx.TransactionHolder.currentTx;
+
+@Transactional
+@Log
+public class MyComponent {
+ private final String myString = "123";
+ private String myVar;
+
+ public static class Person {
+ public final String name;
+
+ public Person(String name) {
+ this.name = name;
+ }
+ }
+
+ protected MyComponent() {
+ }
+
+ public MyComponent(String myVar) {
+ this.myVar = myVar;
+ }
+
+ public void doProcessing(int x) {
+ Tx tx = currentTx();
+ }
+
+ public Person addPerson(String name) {
+ return new Person(name);
+ }
+
+ public void deletePerson(int a, int b) {
+ }
+}
diff --git a/myapp/src/main/java/io/trygvis/container/myapp/MyComponent_Tx.java b/myapp/src/main/java/io/trygvis/container/myapp/MyComponent_Tx.java
new file mode 100644
index 0000000..445d236
--- /dev/null
+++ b/myapp/src/main/java/io/trygvis/container/myapp/MyComponent_Tx.java
@@ -0,0 +1,26 @@
+package io.trygvis.container.myapp;
+
+import io.trygvis.container.tx.PlatformTransactionManager;
+
+public class MyComponent_Tx extends MyComponent {
+ private final PlatformTransactionManager transactionManager;
+ private final MyComponent target;
+
+ public MyComponent_Tx(PlatformTransactionManager tm) {
+ this.transactionManager = tm;
+ this.target = new MyComponent();
+ }
+
+ public void doProcessing(final int x) {
+ transactionManager.doInTransaction(
+ PlatformTransactionManager.TransactionIsolation.ISOLATION_DEFAULT,
+ PlatformTransactionManager.TransactionPropagation.PROPAGATION_REQUIRED,
+ new PlatformTransactionManager.TransactionTemplate<Object>() {
+ @Override
+ public Object doInTransaction() {
+ target.doProcessing(x);
+ return null;
+ }
+ });
+ }
+}
diff --git a/myapp/src/main/java/io/trygvis/container/myapp/MyMain.java b/myapp/src/main/java/io/trygvis/container/myapp/MyMain.java
new file mode 100644
index 0000000..a54e486
--- /dev/null
+++ b/myapp/src/main/java/io/trygvis/container/myapp/MyMain.java
@@ -0,0 +1,10 @@
+package io.trygvis.container.myapp;
+
+import org.springframework.transaction.annotation.Transactional;
+
+public class MyMain {
+
+ public static void main(String[] args) {
+ System.out.println("io.trygvis.container.myapp.MyMain.main");
+ }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9997562
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>pom</packaging>
+ <parent>
+ <groupId>io.trygvis</groupId>
+ <artifactId>trygvis-parent</artifactId>
+ <version>1</version>
+ </parent>
+ <groupId>io.trygvis.container</groupId>
+ <artifactId>container-playground</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <modules>
+ <module>container-compiler-plugin</module>
+ <module>myapp</module>
+ <module>container-core</module>
+ </modules>
+</project>