diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/Compiler.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/Compiler.java
new file mode 100644
index 0000000..eda2988
--- /dev/null
+++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/Compiler.java
@@ -0,0 +1,175 @@
+package com.moparisthebest.classgen;
+
+
+import javax.tools.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This let's you take compile and then run Java source code at runtime, this class only allows you to compile 1 class
+ * at a time.
+ *
+ * This is not thread safe, though it could be extended and made thread safe, or locked around.
+ *
+ * @author moparisthebest
+ * @see MultiCompiler for compiling multiple classes at once
+ *
+ * The original idea was taken from:
+ * http://mindprod.com/jgloss/javacompiler.html#SAMPLECODE
+ */
+public class Compiler {
+
+ private final JavaCompiler compiler;
+ private final MemoryJavaFileManager mjfm;
+ //private final ClassLoader classLoader;
+ private final List singleton = Arrays.asList(new JavaFileObject[1]);
+
+ public Compiler() {
+ this(false);
+ }
+
+ protected Compiler(final boolean multi) {
+ compiler = ToolProvider.getSystemJavaCompiler();
+ if (compiler == null)
+ throw new RuntimeException("tools.jar needs to be on classpath to compile code at runtime");
+ final JavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
+ mjfm = multi ? new MultiMemoryJavaFileManager(fileManager) : new SingleMemoryJavaFileManager(fileManager);
+ //classLoader = new MemoryClassLoader(mjfm);
+ }
+
+ protected ClassLoader compile(final Iterable extends JavaFileObject> source) {
+ //final MemoryJavaFileManager mjfm = new MemoryJavaFileManager(compiler.getStandardFileManager(null, null, null));
+ final JavaCompiler.CompilationTask task = compiler.getTask(null, mjfm, null, null, null, source);
+ return task.call() ?
+ new MemoryClassLoader(mjfm)
+ //classLoader
+ : null;
+ }
+
+ protected ClassLoader compile(final JavaFileObject... source) {
+ return compile(Arrays.asList(source));
+ }
+
+ public ClassLoader compile(final JavaFileObject source) {
+ singleton.set(0, source);
+ return compile(singleton);
+ }
+
+ protected T compile(final String className, final Iterable extends JavaFileObject> source) {
+ // compile item
+ final ClassLoader cl = compile(source);
+ if (cl == null)
+ throw new RuntimeException("Error compiling class, aborting...");
+ return instantiate(cl, className);
+ }
+
+ protected T compile(final String className, final JavaFileObject... source) {
+ return compile(className, Arrays.asList(source));
+ }
+
+ public T compile(final String className, final JavaFileObject source) {
+ singleton.set(0, source);
+ return compile(className, singleton);
+ }
+
+ public T compile(final String className, final CharSequence code) {
+ return compile(className, new StringJavaFileObject(className, code));
+ }
+
+ public T instantiate(final ClassLoader cl, final String className) {
+ try {
+ // Load class and create an instance.
+ final Class> calcClass = cl.loadClass(className);
+ @SuppressWarnings("unchecked") final T ret = (T) calcClass.newInstance();
+ return ret;
+ } catch (Exception e) {
+ throw new RuntimeException("Error finding or instantiating class, exiting...", e);
+ }
+ }
+
+}
+
+class MemoryJavaFileObject extends ForwardingJavaFileObject {
+
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ MemoryJavaFileObject(JavaFileObject fileObject) {
+ super(fileObject);
+ }
+
+ @Override
+ public OutputStream openOutputStream() throws IOException {
+ return baos;
+ }
+}
+
+interface MemoryJavaFileManager extends JavaFileManager {
+ MemoryJavaFileObject getMemoryJavaFileObject(final String name);
+}
+
+class SingleMemoryJavaFileManager extends ForwardingJavaFileManager implements MemoryJavaFileManager {
+
+ private MemoryJavaFileObject classes = null;
+
+ SingleMemoryJavaFileManager(JavaFileManager fileManager) {
+ super(fileManager);
+ }
+
+ @Override
+ public MemoryJavaFileObject getMemoryJavaFileObject(final String name) {
+ final MemoryJavaFileObject ret = classes;
+ classes = null;
+ return ret;
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
+ final MemoryJavaFileObject jfo = new MemoryJavaFileObject(super.getJavaFileForOutput(location, className, kind, sibling));
+ //System.out.printf("MemoryJavaFileManager.getJavaFileForOutput(%s, %s, %s, %s): %s\n", location, className, kind, sibling, jfo);
+ classes = jfo;
+ return jfo;
+ }
+}
+
+class MultiMemoryJavaFileManager extends ForwardingJavaFileManager implements MemoryJavaFileManager {
+
+ private Map classes = new HashMap();
+
+ MultiMemoryJavaFileManager(JavaFileManager fileManager) {
+ super(fileManager);
+ }
+
+ @Override
+ public MemoryJavaFileObject getMemoryJavaFileObject(final String name) {
+ return classes.remove(name);
+ }
+
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
+ final MemoryJavaFileObject jfo = new MemoryJavaFileObject(super.getJavaFileForOutput(location, className, kind, sibling));
+ //System.out.printf("MemoryJavaFileManager.getJavaFileForOutput(%s, %s, %s, %s): %s\n", location, className, kind, sibling, jfo);
+ classes.put(className, jfo);
+ return jfo;
+ }
+}
+
+class MemoryClassLoader extends ClassLoader {
+ final MemoryJavaFileManager jfm;
+
+ MemoryClassLoader(MemoryJavaFileManager jfm) {
+ this.jfm = jfm;
+ }
+
+ public Class findClass(String name) throws ClassNotFoundException {
+ final MemoryJavaFileObject jfo = jfm.getMemoryJavaFileObject(name);
+ if (jfo == null)
+ throw new ClassNotFoundException("Class '" + name + "' cannot be found in the MemoryJavaFileManager");
+ final byte[] b = jfo.baos.toByteArray();
+ return defineClass(name, b, 0, b.length);
+ }
+}
diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/MultiCompiler.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/MultiCompiler.java
new file mode 100644
index 0000000..be555f3
--- /dev/null
+++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/MultiCompiler.java
@@ -0,0 +1,35 @@
+package com.moparisthebest.classgen;
+
+import javax.tools.JavaFileObject;
+
+/**
+ * This lets you compile multiple source files at once
+ */
+public class MultiCompiler extends Compiler {
+
+ public static Compiler instance = new MultiCompiler();
+
+ public MultiCompiler() {
+ super(true);
+ }
+
+ @Override
+ public ClassLoader compile(final Iterable extends JavaFileObject> source) {
+ return super.compile(source);
+ }
+
+ @Override
+ public ClassLoader compile(final JavaFileObject... source) {
+ return super.compile(source);
+ }
+
+ @Override
+ public T compile(final String className, final JavaFileObject... source) {
+ return super.compile(className, source);
+ }
+
+ @Override
+ public T compile(final String className, final Iterable extends JavaFileObject> source) {
+ return super.compile(className, source);
+ }
+}
diff --git a/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/StringJavaFileObject.java b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/StringJavaFileObject.java
new file mode 100644
index 0000000..946effb
--- /dev/null
+++ b/beehive-jdbc-mapper/src/main/java/com/moparisthebest/classgen/StringJavaFileObject.java
@@ -0,0 +1,22 @@
+package com.moparisthebest.classgen;
+
+import javax.tools.SimpleJavaFileObject;
+import java.net.URI;
+
+/**
+ * For sending java source as strings to a Compiler
+ * @see Compiler
+ */
+public class StringJavaFileObject extends SimpleJavaFileObject {
+ private final CharSequence code;
+
+ public StringJavaFileObject(final String name, final CharSequence code) {
+ super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
+ this.code = code;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return code;
+ }
+}
diff --git a/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/Calculator.java b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/Calculator.java
new file mode 100644
index 0000000..40431d2
--- /dev/null
+++ b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/Calculator.java
@@ -0,0 +1,10 @@
+package com.moparisthebest.classgentest;
+
+/**
+ * Created by mopar on 5/15/17.
+ */
+public interface Calculator {
+ public double calc(double a, double b);
+
+ public java.util.Date whenGenerated();
+}
diff --git a/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/CalculatorCompiler.java b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/CalculatorCompiler.java
new file mode 100644
index 0000000..df9fd0a
--- /dev/null
+++ b/beehive-jdbc-mapper/src/test/java/com/moparisthebest/classgentest/CalculatorCompiler.java
@@ -0,0 +1,126 @@
+package com.moparisthebest.classgentest;
+
+import com.moparisthebest.classgen.Compiler;
+import com.moparisthebest.classgen.MultiCompiler;
+import com.moparisthebest.classgen.StringJavaFileObject;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This is a sample program that shows how to generate java source code, compile, and run it, all at runtime and
+ * without ever touching the filesystem. It can be used for much more complicated (and usefull) things, perhaps
+ * like creating Pojo instances from ResultSets. :)
+ *
+ * You need tools.jar on your classpath, and this class in the same package as well:
+ * public interface Calculator {
+ * public double calc(double a, double b);
+ * public java.util.Date whenGenerated();
+ * }
+ *
+ * The original idea was taken from:
+ * http://mindprod.com/jgloss/javacompiler.html#SAMPLECODE
+ *
+ * @author moparisthebest
+ */
+public class CalculatorCompiler {
+
+ @Test
+ public void testSingle() throws InterruptedException {
+ final Compiler compiler = new Compiler();
+ final Calculator mult1 = genCalc(compiler, "Multiply", "a * b");
+ final Calculator hyp = genCalc(compiler, "Hypotenuse", "Math.sqrt( a*a + b*b )");
+ final Calculator mult2 = genCalc(compiler, "Multiply", "a * b * 2");
+ final Calculator mult3 = genCalc(compiler, "Multiply", "a * b * 3");
+ final Calculator mult4 = genCalc(compiler, "Multiply", "a * b * 4");
+ assertEquals(12.0, mult1.calc(3.0, 4.0), 0.01);
+ assertEquals(5.0, hyp.calc(3.0, 4.0), 0.01);
+ assertEquals(24.0, mult2.calc(3.0, 4.0), 0.01);
+ assertEquals(48.0, mult3.calc(4.0, 4.0), 0.01);
+ assertEquals(48.0, mult4.calc(3.0, 4.0), 0.01);
+ }
+
+ @Test
+ public void testMulti() throws InterruptedException {
+ final MultiCompiler compiler = new MultiCompiler();
+ final Calculator mult1 = genCalc(compiler, "Multiply", "a * Var.var", "Var", "4.0");
+ final Calculator hyp = genCalc(compiler, "Hypotenuse", "Math.sqrt( a*a + Var.var*Var.var )", "Var", "4.0");
+ final Calculator mult2 = genCalc(compiler, "Multiply2", "a * Var.var * 2", "Var", "4.0");
+ final Calculator mult3 = genCalc(compiler, "Multiply3", "a * Var.var * 3", "Var", "4.0");
+ final Calculator mult4 = genCalc(compiler, "Multiply", "a * Var.var * 4", "Var", "4.0");
+ assertEquals(mult1.calc(3.0, 4.0), 12.0, 0.01);
+ assertEquals(hyp.calc(3.0, 4.0), 5.0, 0.01);
+ assertEquals(mult2.calc(3.0, 4.0), 24.0, 0.01);
+ assertEquals(mult3.calc(4.0, 4.0), 48.0, 0.01);
+ assertEquals(48.0, mult4.calc(3.0, 4.0), 0.01);
+ }
+
+ private static String writeCalculator(String className, String expression) {
+ String packageName = null;
+ final int lastIndex = className.lastIndexOf(".");
+ if (lastIndex != -1) {
+ packageName = className.substring(0, lastIndex);
+ className = className.substring(lastIndex + 1);
+ }
+
+ return (packageName == null ? "" : "package " + packageName + ";\n") +
+ "import java.util.Date;\n" +
+ "public final class " + className + " implements " + Calculator.class.getName() + " {\n" +
+ " public double calc(double a, double b) {\n" +
+ " return " + expression + ";\n" +
+ " }\n" +
+ " public Date whenGenerated() {\n" +
+ " return new Date(" + System.currentTimeMillis() + "L);\n" +
+ " }\n" +
+ "}\n";
+ }
+
+ private static StringJavaFileObject writeCalculatorOb(String className, String expression) {
+ return new StringJavaFileObject(className, writeCalculator(className, expression));
+ }
+
+ private static StringJavaFileObject writeVar(String className, String expression) {
+ String packageName = null;
+ final int lastIndex = className.lastIndexOf(".");
+ if (lastIndex != -1) {
+ packageName = className.substring(0, lastIndex);
+ className = className.substring(lastIndex + 1);
+ }
+
+ return new StringJavaFileObject(className, (packageName == null ? "" : "package " + packageName + ";\n") +
+ "public final class " + className + " {\n" +
+ " public static final double var = " + expression + ";\n" +
+ "}\n");
+ }
+
+ public static Calculator genCalc(final Compiler compiler, final String className, final String expression) {
+ // compose text of Java program on the fly.
+ final String calc = writeCalculator(className, expression);
+ /*
+ System.out.println("calc:");
+ System.out.println(calc);
+ */
+ return compiler.compile(className, calc);
+ }
+
+ public static Calculator genCalc(final MultiCompiler compiler, final String className, final String expression, final String varClass, final String varExpression) {
+ // compose text of Java program on the fly.
+ final StringJavaFileObject calc = writeCalculatorOb(className, expression);
+ final StringJavaFileObject var = writeVar(varClass, varExpression);
+ /*
+ System.out.println("calc:");
+ System.out.println(calc.getCharContent(true));
+ System.out.println("var:");
+ System.out.println(var.getCharContent(true));
+ */
+ return compiler.compile(className, calc, var);
+ }
+
+ public static double runCalc(final String className, final String expression, final double a, final double b) {
+ final Calculator calc = genCalc(new Compiler(), className, expression);
+ double ret = calc.calc(a, b);
+ System.out.printf("%s.calc( %f, %f ) is : %f\n", className, a, b, ret);
+ System.out.printf("%s generated on: %s\n", className, calc.whenGenerated());
+ return ret;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 1d37dfa..e3829f7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -241,8 +241,8 @@
maven-compiler-plugin3.1
-
- 1.5
+
+ 1.6true