From f8cc907f24cc4df59e1d59f5058668fdd3649723 Mon Sep 17 00:00:00 2001 From: Brett Wooldridge Date: Mon, 18 Sep 2017 00:55:39 +0900 Subject: [PATCH] Test for javassist code generation. --- .../hikari/util/JavassistProxyFactory.java | 11 +++- .../java/com/zaxxer/hikari/pool/TestElf.java | 34 ++++++++++ .../hikari/pool/TestJavassistCodegen.java | 64 +++++++++++++++++++ .../util/TomcatConcurrentBagLeakTest.java | 38 +---------- 4 files changed, 109 insertions(+), 38 deletions(-) create mode 100644 src/test/java/com/zaxxer/hikari/pool/TestJavassistCodegen.java diff --git a/src/main/java/com/zaxxer/hikari/util/JavassistProxyFactory.java b/src/main/java/com/zaxxer/hikari/util/JavassistProxyFactory.java index c1b2cb0f..023d072b 100644 --- a/src/main/java/com/zaxxer/hikari/util/JavassistProxyFactory.java +++ b/src/main/java/com/zaxxer/hikari/util/JavassistProxyFactory.java @@ -55,6 +55,7 @@ import javassist.bytecode.ClassFile; public final class JavassistProxyFactory { private static ClassPool classPool; + private static String genDirectory = ""; public static void main(String... args) { @@ -62,6 +63,10 @@ public final class JavassistProxyFactory classPool.importPackage("java.sql"); classPool.appendClassPath(new LoaderClassPath(JavassistProxyFactory.class.getClassLoader())); + if (args.length > 0) { + genDirectory = args[0]; + } + try { // Cast is not needed for these String methodBody = "{ try { return delegate.method($$); } catch (SQLException e) { throw checkException(e); } }"; @@ -110,7 +115,7 @@ public final class JavassistProxyFactory } } - proxyCt.writeFile("target/classes"); + proxyCt.writeFile(genDirectory + "target/classes"); } /** @@ -189,8 +194,8 @@ public final class JavassistProxyFactory } } - targetCt.getClassFile().setMajorVersion(ClassFile.JAVA_7); - targetCt.writeFile("target/classes"); + targetCt.getClassFile().setMajorVersion(ClassFile.JAVA_8); + targetCt.writeFile(genDirectory + "target/classes"); } private static boolean isThrowsSqlException(CtMethod method) diff --git a/src/test/java/com/zaxxer/hikari/pool/TestElf.java b/src/test/java/com/zaxxer/hikari/pool/TestElf.java index 7ec29255..60dca65c 100644 --- a/src/test/java/com/zaxxer/hikari/pool/TestElf.java +++ b/src/test/java/com/zaxxer/hikari/pool/TestElf.java @@ -16,8 +16,11 @@ package com.zaxxer.hikari.pool; +import java.io.DataInputStream; +import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Field; +import java.net.URL; import java.sql.Connection; import org.apache.logging.log4j.Level; @@ -177,4 +180,35 @@ public final class TestElf stream.println(event.getMessage().getFormattedMessage()); } } + + public static class FauxWebClassLoader extends ClassLoader + { + static final byte[] classBytes = new byte[16_000]; + + @Override + public Class loadClass(String name) throws ClassNotFoundException + { + if (name.startsWith("java") || name.startsWith("org")) { + return super.loadClass(name, true); + } + + final String resourceName = "/" + name.replace('.', '/') + ".class"; + final URL resource = this.getClass().getResource(resourceName); + try (DataInputStream is = new DataInputStream(resource.openStream())) { + int read = 0; + while (read < classBytes.length) { + final int rc = is.read(classBytes, read, classBytes.length - read); + if (rc == -1) { + break; + } + read += rc; + } + + return defineClass(name, classBytes, 0, read); + } + catch (IOException e) { + throw new ClassNotFoundException(name); + } + } + } } diff --git a/src/test/java/com/zaxxer/hikari/pool/TestJavassistCodegen.java b/src/test/java/com/zaxxer/hikari/pool/TestJavassistCodegen.java new file mode 100644 index 00000000..138cb7de --- /dev/null +++ b/src/test/java/com/zaxxer/hikari/pool/TestJavassistCodegen.java @@ -0,0 +1,64 @@ +package com.zaxxer.hikari.pool; + +import com.zaxxer.hikari.mocks.StubConnection; +import com.zaxxer.hikari.pool.TestElf.FauxWebClassLoader; +import com.zaxxer.hikari.util.JavassistProxyFactory; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.Statement; +import java.util.stream.Stream; + +public class TestJavassistCodegen { + @Test + public void testCodegen() throws Exception { + String tmp = System.getProperty("java.io.tmpdir"); + JavassistProxyFactory.main(tmp + (tmp.endsWith("/") ? "" : "/")); + + Path base = Paths.get(tmp, "target/classes/com/zaxxer/hikari/pool".split("/")); + Assert.assertTrue("", Files.isRegularFile(base.resolve("HikariProxyConnection.class"))); + Assert.assertTrue("", Files.isRegularFile(base.resolve("HikariProxyStatement.class"))); + Assert.assertTrue("", Files.isRegularFile(base.resolve("HikariProxyCallableStatement.class"))); + Assert.assertTrue("", Files.isRegularFile(base.resolve("HikariProxyPreparedStatement.class"))); + Assert.assertTrue("", Files.isRegularFile(base.resolve("HikariProxyResultSet.class"))); + Assert.assertTrue("", Files.isRegularFile(base.resolve("ProxyFactory.class"))); + + FauxWebClassLoader fauxClassLoader = new FauxWebClassLoader(); + Class proxyFactoryClass = fauxClassLoader.loadClass("com.zaxxer.hikari.pool.ProxyFactory"); + + Connection connection = new StubConnection(); + + Class fastListClass = fauxClassLoader.loadClass("com.zaxxer.hikari.util.FastList"); + Object fastList = fastListClass.getConstructor(Class.class).newInstance(Statement.class); + + Object proxyConnection = getMethod(proxyFactoryClass, "getProxyConnection") + .invoke(null, + null /*poolEntry*/, + connection, + fastList, + null /*leakTask*/, + 0L /*now*/, + Boolean.FALSE /*isReadOnly*/, + Boolean.FALSE /*isAutoCommit*/); + Assert.assertNotNull(proxyConnection); + + Object proxyStatement = getMethod(proxyConnection.getClass(), "createStatement", 0) + .invoke(proxyConnection); + Assert.assertNotNull(proxyStatement); + } + + private Method getMethod(Class clazz, String methodName, Integer... parameterCount) + { + return Stream.of(clazz.getDeclaredMethods()) + .filter(method -> method.getName().equals(methodName)) + .filter(method -> (parameterCount.length == 0 || parameterCount[0] == method.getParameterCount())) + .peek(method -> method.setAccessible(true)) + .findFirst() + .orElseThrow(RuntimeException::new); + } +} diff --git a/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java b/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java index 1c25a8f2..49755720 100644 --- a/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java +++ b/src/test/java/com/zaxxer/hikari/util/TomcatConcurrentBagLeakTest.java @@ -16,6 +16,8 @@ package com.zaxxer.hikari.util; +import com.zaxxer.hikari.pool.TestElf; +import com.zaxxer.hikari.pool.TestElf.FauxWebClassLoader; import com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry; import org.junit.FixMethodOrder; import org.junit.Test; @@ -23,12 +25,9 @@ import org.junit.runners.MethodSorters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.DataInputStream; -import java.io.IOException; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.net.URL; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; @@ -80,37 +79,6 @@ public class TomcatConcurrentBagLeakTest assertNotNull(ex); } - static class FauxWebClassLoader extends ClassLoader - { - static final byte[] classBytes = new byte[16_000]; - - @Override - public Class loadClass(String name) throws ClassNotFoundException - { - if (name.startsWith("java") || name.startsWith("org")) { - return super.loadClass(name, true); - } - - final String resourceName = "/" + name.replace('.', '/') + ".class"; - final URL resource = this.getClass().getResource(resourceName); - try (DataInputStream is = new DataInputStream(resource.openStream())) { - int read = 0; - while (read < classBytes.length) { - final int rc = is.read(classBytes, read, classBytes.length - read); - if (rc == -1) { - break; - } - read += rc; - } - - return defineClass(name, classBytes, 0, read); - } - catch (IOException e) { - throw new ClassNotFoundException(name); - } - } - } - public static class PoolEntry implements IConcurrentBagEntry { private int state; @@ -143,7 +111,7 @@ public class TomcatConcurrentBagLeakTest @SuppressWarnings("WeakerAccess") public Exception failureException; - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "ResultOfMethodCallIgnored"}) public void createConcurrentBag() throws InterruptedException { try (ConcurrentBag bag = new ConcurrentBag<>((x) -> CompletableFuture.completedFuture(Boolean.TRUE))) {