diff --git a/build.gradle b/build.gradle index 0ed645e1..1adcc3b1 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,8 @@ subprojects { ext.jdbiVer = '2.72' ext.retrofitVer = '1.9.0' - ext.jooqVer = '3.6.4' // 3jooq.7.x support only Java 8+ + ext.jooqJdk6Ver = '3.6.4' // jOOQ versions >= 3.7 only support Java 8+ + ext.jooqJdk8Ver = '3.9.0' ext.slf4jApiVer = '1.7.21' ext.log4jVer = '1.2.17' ext.guavaVer = '19.0' @@ -403,7 +404,7 @@ project (':comsat-jersey-server') { project (':comsat-jooq') { dependencies { - compile "org.jooq:jooq:$jooqVer" + compile "org.jooq:jooq:$jooqJdk6Ver" compile project (':comsat-jdbc') testCompile project(':comsat-test-utils') } @@ -531,6 +532,7 @@ ext.javadocLinks = [ "http://square.github.io/retrofit/1.x/retrofit/", "http://jdbi.org/apidocs/", "http://www.jooq.org/javadoc/3.6.x/", + "http://www.jooq.org/javadoc/latest/", "http://docs.oracle.com/javase/7/docs/api/", "http://puniverse.github.io/quasar/javadoc/", "http://google.github.io/guava/releases/19.0/api/docs/", diff --git a/comsat-jooq-java8/build.gradle b/comsat-jooq-java8/build.gradle new file mode 100644 index 00000000..ad20b0d8 --- /dev/null +++ b/comsat-jooq-java8/build.gradle @@ -0,0 +1,64 @@ +sourceSets { + jdk8 { + java { + srcDir 'src/main/java' + } + + compileClasspath += main.compileClasspath + runtimeClasspath += main.runtimeClasspath + } +} + +compileJdk8Java { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' +} + +if (ext.java8) { + compileTestJava { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' + } +} +else { + sourceSets { + test { + java { + exclude 'co/paralleluniverse/fibers/jooq/**' + } + } + } +} + +dependencies { + compile "org.jooq:jooq:$jooqJdk8Ver" + compile project(':comsat-jdbc') + testCompile project(':comsat-test-utils') +} + +// remove default artifact +configurations.runtime.artifacts.with { archives -> + archives.each { + archives.remove(it) + } +} + +def ssets = [] +if (ext.java8) { + ssets += sourceSets.jdk8 +} + +ssets.each { set -> + def jarTask = task("${set.name}Jar", type: Jar) { + from set.output + from { project.configurations["${set.name}Runtime"].collect { it.isDirectory() ? it : zipTree(it) } } + } + + assemble.dependsOn jarTask + + artifacts { + archives jarTask + archives sourcesJar + archives javadocJar + } +} diff --git a/comsat-jooq-java8/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java b/comsat-jooq-java8/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java new file mode 100644 index 00000000..6afa64c8 --- /dev/null +++ b/comsat-jooq-java8/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java @@ -0,0 +1,121 @@ +/* + * COMSAT + * Copyright (C) 2014, Parallel Universe Software Co. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 3.0 + * as published by the Free Software Foundation. + */ +package co.paralleluniverse.fibers.jooq; + +import co.paralleluniverse.fibers.instrument.LogLevel; +import co.paralleluniverse.fibers.instrument.MethodDatabase; +import co.paralleluniverse.fibers.instrument.SimpleSuspendableClassifier; +import co.paralleluniverse.fibers.instrument.SuspendableClassifier; + +/** + * Given classes and methodRegexps, Instrumenting all the extending methods in + * the scope of given package prefix. + */ +public class JooqClassifier implements SuspendableClassifier { + private static final String PKG_PREFIX = "org/jooq"; + private String[][] methodsArray = { + + {"java/sql/Connection", ".*"}, + {"java/sql/Statement", ".*"}, + + {"java/util/Iterator", "hasNext"}, + {"java/util/concurrent/ForkJoinPool", "managedBlock"}, + {"java/util/concurrent/ForkJoinPool$ManagedBlocker", "block"}, + {"java/util/function/Supplier", "get"}, + {"java/util/function/Supplier", "get"}, + + {"org/jooq/BindContext", "bind", "bindValue", "bindValues"}, + {"org/jooq/Binding", "get.*"}, + {"org/jooq/Binding", "register", "get", "set"}, + {"org/jooq/ConnectionProvider", "acquire", "release"}, + {"org/jooq/Context", "visit", "bindValue"}, + {"org/jooq/Cursor", "fetch.*", "hasNext"}, + {"org/jooq/DSLContext", "fetch.*", "execute.*", "transaction.*"}, + {"org/jooq/DeleteResultStep", "fetch.*"}, + {"org/jooq/ExecuteContext", "connection"}, + {"org/jooq/InsertResultStep", "fetch.*"}, + {"org/jooq/Query", "execute"}, + {"org/jooq/QueryPartInternal", "accept", "bind"}, + {"org/jooq/ResultQuery", "getResult", "fetch.*"}, + {"org/jooq/TransactionProvider", "begin", "rollback", "commit"}, + {"org/jooq/TransactionalCallable", "run"}, + {"org/jooq/TransactionalRunnable", "run"}, + {"org/jooq/UpdateResultStep", "fetch.*"}, + + {"org/jooq/impl/AbstractBindContext", "bindValue0", "bindInternal"}, + {"org/jooq/impl/AbstractContext", "visit0"}, + {"org/jooq/impl/AbstractDMLQuery", "accept0", "selectReturning"}, + {"org/jooq/impl/AbstractField", "accept"}, + {"org/jooq/impl/AbstractQuery", "execute"}, + {"org/jooq/impl/AbstractQuery", "prepare"}, + {"org/jooq/impl/AbstractResultQuery", "getFields"}, + {"org/jooq/impl/AbstractStoreQuery", "accept0"}, + {"org/jooq/impl/CursorImpl", "close"}, + {"org/jooq/impl/CursorImpl$CursorIterator", "fetch.*"}, + {"org/jooq/impl/CursorImpl$CursorIterator", "hasNext"}, + {"org/jooq/impl/CursorImpl$CursorIterator$CursorRecordInitialiser", "setValue"}, + {"org/jooq/impl/CursorImpl$CursorResultSet", ".*"}, + {"org/jooq/impl/DSL", "using"}, + {"org/jooq/impl/DefaultConnectionProvider", "rollback", "commit", "getAutoCommit", "setAutoCommit", "setSavepoint", "releaseSavepoint"}, + {"org/jooq/impl/DefaultTransactionProvider", "connection", "brace", "autoCommit", "setSavepoint"}, + {"org/jooq/impl/InsertQueryImpl", "toSQLInsert"}, + {"org/jooq/impl/MetaDataFieldProvider", "init"}, + {"org/jooq/impl/RecordDelegate", "operate"}, + {"org/jooq/impl/RecordOperation", "operate"}, + {"org/jooq/impl/SelectQueryImpl", "toSQLReference0", "toSQLReferenceLimitDefault"}, + {"org/jooq/impl/Tools", "consumeWarnings", "safeClose", "fetch.*", }, + {"org/jooq/impl/Utils", "safeClose", "consumeWarnings", "fetch.*"}, + + {"org/jooq/tools/jdbc/JDBCUtils", "dialect", "safeClose", "wasNull"} + }; + + @Override + public MethodDatabase.SuspendableType isSuspendable ( + MethodDatabase db, + String sourceName, String sourceDebugInfo, + boolean isInterface, String className, String superClassName, String[] interfaces, + String methodName, String methodDesc, String methodSignature, String[] methodExceptions + ) { + // skipping ctors to avoid unnecessary instrumentation errors + if (methodName.charAt(0) == '<') + return null; + + // declares given methods as supers + for (String[] susExtendables : methodsArray) { + if (className.equals(susExtendables[0])) + for (int i = 1; i < susExtendables.length; i++) { + if (methodName.matches(susExtendables[i])) { + if (db.isVerbose()) + db.getLog().log(LogLevel.INFO, JooqClassifier.class.getName() + ": " + className + "." + methodName + " supersOrEqual " + susExtendables[0] + "." + susExtendables[i]); + return MethodDatabase.SuspendableType.SUSPENDABLE; + } + } + } + + // declares extending classes in jooq packages as suspendables + if (!className.startsWith(PKG_PREFIX)) + return null; + for (String[] susExtendables : methodsArray) { + if (SimpleSuspendableClassifier.extendsOrImplements(susExtendables[0], db, className, superClassName, interfaces)) + for (int i = 1; i < susExtendables.length; i++) { + if (methodName.matches(susExtendables[i])) { + if (db.isVerbose()) + db.getLog().log(LogLevel.INFO, JooqClassifier.class.getName() + ": " + className + "." + methodName + " extends " + susExtendables[0] + "." + susExtendables[i]); + return MethodDatabase.SuspendableType.SUSPENDABLE; + } + } + } + return null; + } +} diff --git a/comsat-jooq-java8/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier b/comsat-jooq-java8/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier new file mode 100644 index 00000000..6f3379a9 --- /dev/null +++ b/comsat-jooq-java8/src/main/resources/META-INF/services/co.paralleluniverse.fibers.instrument.SuspendableClassifier @@ -0,0 +1 @@ +co.paralleluniverse.fibers.jooq.JooqClassifier \ No newline at end of file diff --git a/comsat-jooq-java8/src/test/java/co/paralleluniverse/fibers/jooq/JooqContextTest.java b/comsat-jooq-java8/src/test/java/co/paralleluniverse/fibers/jooq/JooqContextTest.java new file mode 100644 index 00000000..17f61a54 --- /dev/null +++ b/comsat-jooq-java8/src/test/java/co/paralleluniverse/fibers/jooq/JooqContextTest.java @@ -0,0 +1,120 @@ +package co.paralleluniverse.fibers.jooq; + +/* + * COMSAT + * Copyright (C) 2014, Parallel Universe Software Co. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 3.0 + * as published by the Free Software Foundation. + */ +import co.paralleluniverse.embedded.db.H2JdbcDatasource; +import co.paralleluniverse.fibers.Fiber; +import co.paralleluniverse.fibers.SuspendExecution; +import co.paralleluniverse.fibers.jdbc.FiberDataSource; +import co.paralleluniverse.strands.SuspendableRunnable; +import java.io.IOException; +import java.sql.Connection; +import java.util.Arrays; +import java.util.Collection; +import javax.sql.DataSource; +import org.jooq.DSLContext; +import org.jooq.Record; +import org.jooq.RecordMapper; +import static org.jooq.impl.DSL.field; +import static org.jooq.impl.DSL.table; +import static org.jooq.impl.DSL.using; + +import org.jooq.impl.DSL; +import org.junit.After; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class JooqContextTest { + + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][]{ + {H2JdbcDatasource.class},}); + } + private final Class dsCls; + private Connection conn; + private DSLContext ctx; + + public JooqContextTest(Class cls) { + this.dsCls = cls; + } + + @Before + public void setUp() throws Exception { + DataSource dataSource = dsCls.newInstance(); + // snippet creation + this.conn = FiberDataSource.wrap(dataSource).getConnection(); + this.ctx = using(conn); + // end of snippet + conn.createStatement().execute("create table something (id int primary key, name varchar(100))"); + } + + @After + public void tearDown() throws Exception { + conn.createStatement().execute("drop table something"); + conn.close(); + } + + @Test + public void testInsertSelect() throws IOException, InterruptedException, Exception { + new Fiber(new SuspendableRunnable() { + @Override + public void run() throws SuspendExecution, InterruptedException { + // snippet usage + for (int i = 0; i < 100; i++) + ctx.insertInto(table("something"), field("id"), field("name")).values(i, "name" + i).execute(); + for (int i = 0; i < 50; i++) { + Something something = ctx.select(field("id"), field("name")).from(table("something")).where(field("id", Integer.class).eq(i)).fetchOne().map(Something.mapper); + assertEquals("name" + i, something.name); + } + // end of snippet + } + }).start().join(); + } + + @Test + public void testTransaction() throws IOException, InterruptedException, Exception { + new Fiber((SuspendableRunnable) () -> { + // snippet usage + ctx.transaction((config) -> { + DSLContext innerCtx = DSL.using(config); + for (int i = 0; i < 100; i++) + innerCtx.insertInto(table("something"), field("id"), field("name")).values(i, "name" + i).execute(); + }); + // end of snippet + }).start().join(); + } + + // snippet mapper + public static class Something { + public final int id; + public final String name; + public static RecordMapper mapper = new RecordMapper() { + @Override + public Something map(Record r) { + return new Something(r.getValue(field("id", Integer.class)), r.getValue(field("name", String.class))); + } + }; + + public Something(int id, String name) { + this.id = id; + this.name = name; + } + } + // end of snippet +} diff --git a/comsat-jooq/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java b/comsat-jooq/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java index 51232065..a6a26e18 100644 --- a/comsat-jooq/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java +++ b/comsat-jooq/src/main/java/co/paralleluniverse/fibers/jooq/JooqClassifier.java @@ -24,7 +24,7 @@ */ public class JooqClassifier implements SuspendableClassifier { private static final String PKG_PREFIX = "org/jooq"; - String[][] methodsArray = { + private String[][] methodsArray = { {"java/util/Iterator", "hasNext"}, {"java/sql/Statement", ".*"}, {"java/sql/Connection", ".*"}, @@ -72,6 +72,10 @@ public MethodDatabase.SuspendableType isSuspendable ( boolean isInterface, String className, String superClassName, String[] interfaces, String methodName, String methodDesc, String methodSignature, String[] methodExceptions ) { + // skipping ctors to avoid unnecessary instrumentation errors + if (methodName.charAt(0) == '<') + return null; + // declares given methods as supers for (String[] susExtendables : methodsArray) { if (className.equals(susExtendables[0])) diff --git a/docs/index.md b/docs/index.md index e7ebd668..61333bf1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -92,7 +92,8 @@ Then add the following Maven/Gradle dependencies: | [Retrofit](http://square.github.io/retrofit/) integration with fibers. | `co.paralleluniverse:comsat-retrofit:0.7.0` | [JDBI](http://jdbi.org/) integration with fibers. | `co.paralleluniverse:comsat-jdbi:0.7.0` | JDBC integration with fibers. | `co.paralleluniverse:comsat-jdbc:0.7.0` -| [jOOQ](http://www.jooq.org/) integration with fibers. | `co.paralleluniverse:comsat-jooq:0.7.0` +| [jOOQ](http://www.jooq.org) integration with fibers for using jOOQ with Java 7 and below. | `co.paralleluniverse:comsat-jooq:0.7.0` +| [jOOQ](http://www.jooq.org) integration with fibers for using jOOQ with Java 8. | `co.paralleluniverse:comsat-jooq-java8:0.7.0` | MongoDB fiber-blocking integration for the [Allanbank API](http://www.allanbank.com/mongodb-async-driver/index.html). | `co.paralleluniverse:comsat-mongodb-allanbank:0.7.0` | [OkHttp](https://github.com/square/okhttp) HTTP+SPDY client integration. | `co.paralleluniverse:comsat-okhttp:0.7.0` | The Web Actors API. | `co.paralleluniverse:comsat-actors-api:0.7.0` @@ -495,6 +496,8 @@ The interface now can be registered and used as usual from fibers: {% include_snippet usage ./comsat-jooq/src/test/java/co/paralleluniverse/fibers/jooq/JooqContextTest.java %} ~~~ +Starting with versions 3.7 jOOQ requires Java 8 and Comsat provides two separate jOOQ integrations: `comsat-jooq` supports Java 6+ and jOOQ 3.6.x while `comsat-jooq-java8` supports jOOQ 3.8.x with Java 8. + #### MongoDB Comsat integrates with MongoDB and offers a fiber-blocking [allanbank API](http://www.allanbank.com/mongodb-async-driver/index.html). diff --git a/settings.gradle b/settings.gradle index 0aab667b..89a6c5f0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,7 @@ include 'comsat-jetty-loader' include 'comsat-jdbc' include 'comsat-jdbi' include 'comsat-jooq' +include 'comsat-jooq-java8' include 'comsat-dropwizard' include 'comsat-retrofit' include 'comsat-httpclient'