diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java index 63747071a..d247f8ef4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonScheduleThreadPool.java @@ -1,79 +1,79 @@ -package com.avaje.ebeaninternal.server.lib; - -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Daemon based ScheduleThreadPool. - *
- * Uses Daemon threads and hooks into shutdown event. - *
- * - * @author rbygrave - */ -public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor { - - private static final Logger logger = LoggerFactory.getLogger(DaemonScheduleThreadPool.class); - - private final String namePrefix; - - private int shutdownWaitSeconds; - - /** - * Construct the DaemonScheduleThreadPool. - */ - public DaemonScheduleThreadPool(int coreSize, int shutdownWaitSeconds, String namePrefix) { - - super(coreSize, new DaemonThreadFactory(namePrefix)); - this.namePrefix = namePrefix; - this.shutdownWaitSeconds = shutdownWaitSeconds; - } - - /** - * Register a shutdown hook with the JVM Runtime. - */ - public void registerShutdownHook() { - Runtime.getRuntime().addShutdownHook(new ShutdownHook()); - } - - /** - * Shutdown this thread pool nicely if possible. - *- * This will wait a maximum of 20 seconds before terminating any threads still - * working. - *
- */ - public void shutdown() { - synchronized (this) { - if (super.isShutdown()) { - logger.debug("DaemonScheduleThreadPool {} already shut down", namePrefix); - return; - } - try { - logger.debug("DaemonScheduleThreadPool {} shutting down...", namePrefix); - super.shutdown(); - if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { - logger.info("DaemonScheduleThreadPool shut down timeout exceeded. Terminating running threads."); - super.shutdownNow(); - } - - } catch (Exception e) { - logger.error("Error during shutdown of " + namePrefix, e); - e.printStackTrace(); - } - } - } - - /** - * Fired by the JVM Runtime shutdown. - */ - private class ShutdownHook extends Thread { - @Override - public void run() { - shutdown(); - } - }; -} +package com.avaje.ebeaninternal.server.lib; + +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Daemon based ScheduleThreadPool. + *+ * Uses Daemon threads and hooks into shutdown event. + *
+ * + * @author rbygrave + */ +public final class DaemonScheduleThreadPool extends ScheduledThreadPoolExecutor { + + private static final Logger logger = LoggerFactory.getLogger(DaemonScheduleThreadPool.class); + + private final String namePrefix; + + private int shutdownWaitSeconds; + + /** + * Construct the DaemonScheduleThreadPool. + */ + public DaemonScheduleThreadPool(int coreSize, int shutdownWaitSeconds, String namePrefix) { + + super(coreSize, new DaemonThreadFactory(namePrefix)); + this.namePrefix = namePrefix; + this.shutdownWaitSeconds = shutdownWaitSeconds; + } + + /** + * Register a shutdown hook with the JVM Runtime. + */ + public void registerShutdownHook() { + Runtime.getRuntime().addShutdownHook(new ShutdownHook()); + } + + /** + * Shutdown this thread pool nicely if possible. + *+ * This will wait a maximum of 20 seconds before terminating any threads still + * working. + *
+ */ + public void shutdown() { + synchronized (this) { + if (super.isShutdown()) { + logger.debug("DaemonScheduleThreadPool {} already shut down", namePrefix); + return; + } + try { + logger.debug("DaemonScheduleThreadPool {} shutting down...", namePrefix); + super.shutdown(); + if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { + logger.info("DaemonScheduleThreadPool shut down timeout exceeded. Terminating running threads."); + super.shutdownNow(); + } + + } catch (Exception e) { + logger.error("Error during shutdown of " + namePrefix, e); + e.printStackTrace(); + } + } + } + + /** + * Fired by the JVM Runtime shutdown. + */ + private class ShutdownHook extends Thread { + @Override + public void run() { + shutdown(); + } + }; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java index d96ce19bc..70ca67fe5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadFactory.java @@ -1,49 +1,49 @@ -package com.avaje.ebeaninternal.server.lib; - - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * ThreadFactory for Daemon threads. - *- * Daemon threads do not stop a JVM stopping. If an application only has Daemon - * threads left it will shutdown. - *
- *- * In using Daemon threads you need to either not care about being interrupted - * on shutdown or register with the JVM shutdown hook to perform a nice shutdown - * of the daemon threads etc. - *
- * - * @author rbygrave - */ -public class DaemonThreadFactory implements ThreadFactory { - - private static final AtomicInteger poolNumber = new AtomicInteger(1); - - private final ThreadGroup group; - - private final AtomicInteger threadNumber = new AtomicInteger(1); - - private final String namePrefix; - - public DaemonThreadFactory(String namePrefix) { - SecurityManager s = System.getSecurityManager(); - this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-"; - } - - public Thread newThread(Runnable r) { - - Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); - - t.setDaemon(true); - - if (t.getPriority() != Thread.NORM_PRIORITY) { - t.setPriority(Thread.NORM_PRIORITY); - } - - return t; - } +package com.avaje.ebeaninternal.server.lib; + + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * ThreadFactory for Daemon threads. + *+ * Daemon threads do not stop a JVM stopping. If an application only has Daemon + * threads left it will shutdown. + *
+ *+ * In using Daemon threads you need to either not care about being interrupted + * on shutdown or register with the JVM shutdown hook to perform a nice shutdown + * of the daemon threads etc. + *
+ * + * @author rbygrave + */ +public class DaemonThreadFactory implements ThreadFactory { + + private static final AtomicInteger poolNumber = new AtomicInteger(1); + + private final ThreadGroup group; + + private final AtomicInteger threadNumber = new AtomicInteger(1); + + private final String namePrefix; + + public DaemonThreadFactory(String namePrefix) { + SecurityManager s = System.getSecurityManager(); + this.group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); + this.namePrefix = namePrefix != null ? namePrefix : "pool-" + poolNumber.getAndIncrement() + "-thread-"; + } + + public Thread newThread(Runnable r) { + + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); + + t.setDaemon(true); + + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + + return t; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java index 75174344d..7ce19ae24 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/DaemonThreadPool.java @@ -1,86 +1,86 @@ -package com.avaje.ebeaninternal.server.lib; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The Thread Pool based on Daemon threads. - * - * @author rbygrave - */ -public final class DaemonThreadPool extends ThreadPoolExecutor { - - private static final Logger logger = LoggerFactory.getLogger(DaemonThreadPool.class); - - private final String namePrefix; - - private final int shutdownWaitSeconds; - - /** - * Construct the DaemonThreadPool. - * - * @param coreSize - * the core size of the thread pool. - * @param keepAliveSecs - * the time in seconds idle threads are keep alive - * @param shutdownWaitSeconds - * the time in seconds allowed for the pool to shutdown nicely. After - * this the pool is forced to shutdown. - */ - public DaemonThreadPool(int coreSize, int maximumPoolSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) { - - super(coreSize, maximumPoolSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue- * This will wait a maximum of 20 seconds before terminating any threads still - * working. - *
- */ - public void shutdown() { - synchronized (this) { - if (super.isShutdown()) { - logger.debug("DaemonThreadPool[" + namePrefix + "] already shut down"); - return; - } - try { - logger.debug("DaemonThreadPool[" + namePrefix + "] shutting down..."); - super.shutdown(); - if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { - logger.info("DaemonThreadPool[" + namePrefix+ "] shut down timeout exceeded. Terminating running threads."); - super.shutdownNow(); - } - - } catch (Exception e) { - logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e); - e.printStackTrace(); - } - } - } - - /** - * Fired by the JVM Runtime shutdown. - */ - private class ShutdownHook extends Thread { - @Override - public void run() { - shutdown(); - } - }; -} +package com.avaje.ebeaninternal.server.lib; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Thread Pool based on Daemon threads. + * + * @author rbygrave + */ +public final class DaemonThreadPool extends ThreadPoolExecutor { + + private static final Logger logger = LoggerFactory.getLogger(DaemonThreadPool.class); + + private final String namePrefix; + + private final int shutdownWaitSeconds; + + /** + * Construct the DaemonThreadPool. + * + * @param coreSize + * the core size of the thread pool. + * @param keepAliveSecs + * the time in seconds idle threads are keep alive + * @param shutdownWaitSeconds + * the time in seconds allowed for the pool to shutdown nicely. After + * this the pool is forced to shutdown. + */ + public DaemonThreadPool(int coreSize, int maximumPoolSize, long keepAliveSecs, int shutdownWaitSeconds, String namePrefix) { + + super(coreSize, maximumPoolSize, keepAliveSecs, TimeUnit.SECONDS, new LinkedBlockingQueue+ * This will wait a maximum of 20 seconds before terminating any threads still + * working. + *
+ */ + public void shutdown() { + synchronized (this) { + if (super.isShutdown()) { + logger.debug("DaemonThreadPool[" + namePrefix + "] already shut down"); + return; + } + try { + logger.debug("DaemonThreadPool[" + namePrefix + "] shutting down..."); + super.shutdown(); + if (!super.awaitTermination(shutdownWaitSeconds, TimeUnit.SECONDS)) { + logger.info("DaemonThreadPool[" + namePrefix+ "] shut down timeout exceeded. Terminating running threads."); + super.shutdownNow(); + } + + } catch (Exception e) { + logger.error("Error during shutdown of DaemonThreadPool[" + namePrefix + "]", e); + e.printStackTrace(); + } + } + } + + /** + * Fired by the JVM Runtime shutdown. + */ + private class ShutdownHook extends Thread { + @Override + public void run() { + shutdown(); + } + }; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java index 7961434e3..772b25d34 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownHook.java @@ -1,22 +1,22 @@ -package com.avaje.ebeaninternal.server.lib; - - - -/** - * This is the ShutdownHook that gets added to Runtime. - * It will try to shutdown the system cleanly when the JVM exits. - * It is best to add your own shutdown hooks to StartStop. - */ -class ShutdownHook extends Thread { - - ShutdownHook() { - } - - /** - * Fired by the JVM Runtime on shutdown. - */ - public void run() { - ShutdownManager.shutdown(); - } - -}; +package com.avaje.ebeaninternal.server.lib; + + + +/** + * This is the ShutdownHook that gets added to Runtime. + * It will try to shutdown the system cleanly when the JVM exits. + * It is best to add your own shutdown hooks to StartStop. + */ +class ShutdownHook extends Thread { + + ShutdownHook() { + } + + /** + * Fired by the JVM Runtime on shutdown. + */ + public void run() { + ShutdownManager.shutdown(); + } + +}; diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java index 718af5d6a..70bd654ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/ShutdownManager.java @@ -1,187 +1,187 @@ -package com.avaje.ebeaninternal.server.lib; - -import com.avaje.ebean.common.SpiContainer; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; - -/** - * Manages the shutdown of the JVM Runtime. - *- * Makes sure all the resources are shutdown properly and in order. - *
- */ -public final class ShutdownManager { - - private static final Logger logger = LoggerFactory.getLogger(ShutdownManager.class); - - static final List- * For running in a Servlet Container a redeploy will cause a shutdown, and - * for that case we need to make sure the shutdown hook is deregistered. - *
- */ - protected static void deregisterShutdownHook() { - synchronized (servers) { - try { - Runtime.getRuntime().removeShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } - } - } - } - - /** - * Register the shutdown hook with the Runtime. - */ - protected static void registerShutdownHook() { - synchronized (servers) { - try { - Runtime.getRuntime().addShutdownHook(shutdownHook); - } catch (IllegalStateException ex) { - if (!ex.getMessage().equals("Shutdown in progress")) { - throw ex; - } - } - } - } - - /** - * Shutdown gracefully cleaning up any resources as required. - *- * This is typically invoked via JVM shutdown hook. - *
- */ - public static void shutdown() { - synchronized (servers) { - if (stopping) { - // Already run shutdown... - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Shutting down"); - } - - stopping = true; - - deregisterShutdownHook(); - - String shutdownRunner = System.getProperty("ebean.shutdown.runnable"); - if (shutdownRunner != null) { - try { - // A custom runnable executed at the start of shutdown - Runnable r = (Runnable) ClassUtil.newInstance(shutdownRunner); - r.run(); - } catch (Exception e) { - logger.error("Error running custom shutdown runnable", e); - } - } - - if (container != null) { - // shutdown cluster networking if active - container.shutdown(); - } - - // shutdown any registered servers that have not - // already been shutdown manually - for (SpiEbeanServer server : servers) { - try { - server.shutdownManaged(); - } catch (Exception ex) { - logger.error("Error executing shutdown runnable", ex); - ex.printStackTrace(); - } - } - - if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) { - deregisterAllJdbcDrivers(); - } - } - } - - private static void deregisterAllJdbcDrivers() { - // This manually deregisters all JDBC drivers - Enumeration- * This is done when the ebeanServer is shutdown manually. - *
- */ - public static void unregisterEbeanServer(SpiEbeanServer server) { - synchronized (servers) { - servers.remove(server); - } - } -} +package com.avaje.ebeaninternal.server.lib; + +import com.avaje.ebean.common.SpiContainer; +import com.avaje.ebeaninternal.api.ClassUtil; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * Manages the shutdown of the JVM Runtime. + *+ * Makes sure all the resources are shutdown properly and in order. + *
+ */ +public final class ShutdownManager { + + private static final Logger logger = LoggerFactory.getLogger(ShutdownManager.class); + + static final List+ * For running in a Servlet Container a redeploy will cause a shutdown, and + * for that case we need to make sure the shutdown hook is deregistered. + *
+ */ + protected static void deregisterShutdownHook() { + synchronized (servers) { + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; + } + } + } + } + + /** + * Register the shutdown hook with the Runtime. + */ + protected static void registerShutdownHook() { + synchronized (servers) { + try { + Runtime.getRuntime().addShutdownHook(shutdownHook); + } catch (IllegalStateException ex) { + if (!ex.getMessage().equals("Shutdown in progress")) { + throw ex; + } + } + } + } + + /** + * Shutdown gracefully cleaning up any resources as required. + *+ * This is typically invoked via JVM shutdown hook. + *
+ */ + public static void shutdown() { + synchronized (servers) { + if (stopping) { + // Already run shutdown... + return; + } + + if (logger.isDebugEnabled()) { + logger.debug("Shutting down"); + } + + stopping = true; + + deregisterShutdownHook(); + + String shutdownRunner = System.getProperty("ebean.shutdown.runnable"); + if (shutdownRunner != null) { + try { + // A custom runnable executed at the start of shutdown + Runnable r = (Runnable) ClassUtil.newInstance(shutdownRunner); + r.run(); + } catch (Exception e) { + logger.error("Error running custom shutdown runnable", e); + } + } + + if (container != null) { + // shutdown cluster networking if active + container.shutdown(); + } + + // shutdown any registered servers that have not + // already been shutdown manually + for (SpiEbeanServer server : servers) { + try { + server.shutdownManaged(); + } catch (Exception ex) { + logger.error("Error executing shutdown runnable", ex); + ex.printStackTrace(); + } + } + + if ("true".equalsIgnoreCase(System.getProperty("ebean.datasource.deregisterAllDrivers", "false"))) { + deregisterAllJdbcDrivers(); + } + } + } + + private static void deregisterAllJdbcDrivers() { + // This manually deregisters all JDBC drivers + Enumeration+ * This is done when the ebeanServer is shutdown manually. + *
+ */ + public static void unregisterEbeanServer(SpiEbeanServer server) { + synchronized (servers) { + servers.remove(server); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java index 090d06809..0755ba6c8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java @@ -1,108 +1,108 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * Helper object used to find directories typically from the current working - * directory. - */ -public class DirectoryFinder { - - private static final Logger logger = LoggerFactory.getLogger(DirectoryFinder.class); - - /** - * Find a directory by search through subdirectories. - *- * For example, used to find the WEB-INF directory starting from the current - * working directory. - *
- * - *
- *
- * // search to a depth of 3 from the current working directory
- * // looking for a directory WEB-INF that contains the subdirectory
- * // data
- *
- * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
- * if (dir != null) {
- * //found the directory
- * }
- *
- */
- public static File find(File startDir, String match, int maxDepth) {
-
- String matchSub = null;
- int slashPos = match.indexOf('/');
- if (slashPos > -1) {
- // match has sub directories
- matchSub = match.substring(slashPos + 1);
- match = match.substring(0, slashPos);
- }
-
- // search for the directory
- File found = find(startDir, match, matchSub, 0, maxDepth);
-
- if (found != null && matchSub != null) {
- // match has sub directories
- return new File(found, matchSub);
- }
- return found;
- }
-
- private static File find(File dir, String match, String matchSub, int depth, int maxDepth) {
-
- if (dir == null) {
- String curDir = System.getProperty("user.dir");
- dir = new File(curDir);
- }
-
- if (dir.exists()) {
- File[] list = dir.listFiles();
- if (list != null){
- for (int i = 0; i < list.length; i++) {
- if (isMatch(list[i], match, matchSub)) {
- return list[i];
- }
- }
-
- // go through the directories again
- // Aka *NOT* a depth first search
- if (depth < maxDepth) {
- for (int i = 0; i < list.length; i++) {
- if (list[i].isDirectory()) {
- File found = find(list[i], match, matchSub, depth + 1, maxDepth);
- if (found != null) {
- return found;
- }
- }
- }
- }
- }
- }
- return null;
- }
-
- private static boolean isMatch(File f, String match, String matchSub) {
- if (f == null) {
- return false;
- }
- if (!f.isDirectory()) {
- return false;
- }
- if (!f.getName().equalsIgnoreCase(match)) {
- return false;
- }
- if (matchSub == null) {
- return true;
- }
- File sub = new File(f, matchSub);
- if (logger.isTraceEnabled()){
- logger.trace("search; " + f.getPath());
- }
- return sub.exists();
-
- }
-}
+package com.avaje.ebeaninternal.server.lib.resource;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+
+/**
+ * Helper object used to find directories typically from the current working
+ * directory.
+ */
+public class DirectoryFinder {
+
+ private static final Logger logger = LoggerFactory.getLogger(DirectoryFinder.class);
+
+ /**
+ * Find a directory by search through subdirectories.
+ * + * For example, used to find the WEB-INF directory starting from the current + * working directory. + *
+ * + *
+ *
+ * // search to a depth of 3 from the current working directory
+ * // looking for a directory WEB-INF that contains the subdirectory
+ * // data
+ *
+ * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
+ * if (dir != null) {
+ * //found the directory
+ * }
+ *
+ */
+ public static File find(File startDir, String match, int maxDepth) {
+
+ String matchSub = null;
+ int slashPos = match.indexOf('/');
+ if (slashPos > -1) {
+ // match has sub directories
+ matchSub = match.substring(slashPos + 1);
+ match = match.substring(0, slashPos);
+ }
+
+ // search for the directory
+ File found = find(startDir, match, matchSub, 0, maxDepth);
+
+ if (found != null && matchSub != null) {
+ // match has sub directories
+ return new File(found, matchSub);
+ }
+ return found;
+ }
+
+ private static File find(File dir, String match, String matchSub, int depth, int maxDepth) {
+
+ if (dir == null) {
+ String curDir = System.getProperty("user.dir");
+ dir = new File(curDir);
+ }
+
+ if (dir.exists()) {
+ File[] list = dir.listFiles();
+ if (list != null){
+ for (int i = 0; i < list.length; i++) {
+ if (isMatch(list[i], match, matchSub)) {
+ return list[i];
+ }
+ }
+
+ // go through the directories again
+ // Aka *NOT* a depth first search
+ if (depth < maxDepth) {
+ for (int i = 0; i < list.length; i++) {
+ if (list[i].isDirectory()) {
+ File found = find(list[i], match, matchSub, depth + 1, maxDepth);
+ if (found != null) {
+ return found;
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean isMatch(File f, String match, String matchSub) {
+ if (f == null) {
+ return false;
+ }
+ if (!f.isDirectory()) {
+ return false;
+ }
+ if (!f.getName().equalsIgnoreCase(match)) {
+ return false;
+ }
+ if (matchSub == null) {
+ return true;
+ }
+ File sub = new File(f, matchSub);
+ if (logger.isTraceEnabled()){
+ logger.trace("search; " + f.getPath());
+ }
+ return sub.exists();
+
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java
index 893603aff..7c0e9fff4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java
@@ -1,71 +1,71 @@
-package com.avaje.ebeaninternal.server.lib.resource;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Date;
-
-/**
- * Content from a file system file.
- */
-public class FileResourceContent implements ResourceContent {
-
- /**
- * The underlying file.
- */
- File file;
-
- String entryName;
-
- /**
- * Create with a File and the entryName.
- */
- public FileResourceContent(File file, String entryName) {
- this.file = file;
- this.entryName = entryName;
- }
-
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("[").append(getName());
- sb.append("] size[").append(size());
- sb.append("] lastModified[").append(new Date(lastModified()));
- sb.append("]");
- return sb.toString();
- }
-
- /**
- * Returns the entry name which contains the path from the base directory.
- * - * This does not return the full path of the file, but the path relative to - * the FileIoSource directory. - *
- */ - public String getName() { - return entryName; - } - - /** - * Return the time the file was last modified. - */ - public long lastModified() { - return file.lastModified(); - } - - /** - * Return the size of the file. - */ - public long size() { - return file.length(); - } - - /** - * Return the input stream for this file. - */ - public InputStream getInputStream() throws IOException { - - FileInputStream is = new FileInputStream(file); - return is; - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; + +/** + * Content from a file system file. + */ +public class FileResourceContent implements ResourceContent { + + /** + * The underlying file. + */ + File file; + + String entryName; + + /** + * Create with a File and the entryName. + */ + public FileResourceContent(File file, String entryName) { + this.file = file; + this.entryName = entryName; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("[").append(getName()); + sb.append("] size[").append(size()); + sb.append("] lastModified[").append(new Date(lastModified())); + sb.append("]"); + return sb.toString(); + } + + /** + * Returns the entry name which contains the path from the base directory. + *+ * This does not return the full path of the file, but the path relative to + * the FileIoSource directory. + *
+ */ + public String getName() { + return entryName; + } + + /** + * Return the time the file was last modified. + */ + public long lastModified() { + return file.lastModified(); + } + + /** + * Return the size of the file. + */ + public long size() { + return file.length(); + } + + /** + * Return the input stream for this file. + */ + public InputStream getInputStream() throws IOException { + + FileInputStream is = new FileInputStream(file); + return is; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java index 1a9a9aeda..1f9a7461d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java @@ -1,51 +1,51 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.File; - -/** - * A file system directory represented as a FileSource. - */ -public class FileResourceSource extends AbstractResourceSource implements ResourceSource { - - /** - * The directory name. - */ - String directory; - - String baseDir; - - /** - * Create the source based on a directory name. - */ - public FileResourceSource(String directory){ - this.directory = directory; - this.baseDir = directory+File.separator; - } - - /** - * Create the source based on a directory file. - */ - public FileResourceSource(File dir){ - this(dir.getPath()); - } - - - public String getRealPath() { - return directory; - } - - /** - * Search for the given file and return as IoContent. - */ - public ResourceContent getContent(String entry) { - - String fullPath = baseDir+entry; - - File f = new File(fullPath); - if (f.exists()){ - FileResourceContent content = new FileResourceContent(f, entry); - return content; - } - return null; - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.File; + +/** + * A file system directory represented as a FileSource. + */ +public class FileResourceSource extends AbstractResourceSource implements ResourceSource { + + /** + * The directory name. + */ + String directory; + + String baseDir; + + /** + * Create the source based on a directory name. + */ + public FileResourceSource(String directory){ + this.directory = directory; + this.baseDir = directory+File.separator; + } + + /** + * Create the source based on a directory file. + */ + public FileResourceSource(File dir){ + this(dir.getPath()); + } + + + public String getRealPath() { + return directory; + } + + /** + * Search for the given file and return as IoContent. + */ + public ResourceContent getContent(String entry) { + + String fullPath = baseDir+entry; + + File f = new File(fullPath); + if (f.exists()){ + FileResourceContent content = new FileResourceContent(f, entry); + return content; + } + return null; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java index 42b6811e6..ebc844371 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java @@ -1,34 +1,34 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Represents content that can be read via the ResourceManager. - *- * Typically either content from a File or a URL. - *
- */ -public interface ResourceContent { - - /** - * The name of the content. - */ - public String getName(); - - /** - * The size of the content in bytes. - */ - public long size(); - - /** - * The last modified timestamp of the content. - */ - public long lastModified(); - - /** - * The content itself. - */ - public InputStream getInputStream() throws IOException; - -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Represents content that can be read via the ResourceManager. + *+ * Typically either content from a File or a URL. + *
+ */ +public interface ResourceContent { + + /** + * The name of the content. + */ + public String getName(); + + /** + * The size of the content in bytes. + */ + public long size(); + + /** + * The last modified timestamp of the content. + */ + public long lastModified(); + + /** + * The content itself. + */ + public InputStream getInputStream() throws IOException; + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java index c0cca33f4..346df0d6f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java @@ -1,37 +1,37 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.IOException; - -/** - * A Source for ResourceManager. - *- * Typically a File System Directory based source or a ServletContext URL - * resource based source (for Servlet WAR files). - *
- */ -public interface ResourceSource { - - /** - * Return the File System path of the root of the ResourceSource. - *- * This will return null IF the ResourceSource is an unpacked WAR file. - *
- */ - public String getRealPath(); - - /** - * Find the content with a given entry name. This will return null if no - * matching content was found. - */ - public ResourceContent getContent(String entry); - - /** - * Return the content as a String. - */ - public String readString(ResourceContent content, int bufSize) throws IOException; - - /** - * Return the content as a byte[]. - */ - public byte[] readBytes(ResourceContent content, int bufSize) throws IOException; -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; + +/** + * A Source for ResourceManager. + *+ * Typically a File System Directory based source or a ServletContext URL + * resource based source (for Servlet WAR files). + *
+ */ +public interface ResourceSource { + + /** + * Return the File System path of the root of the ResourceSource. + *+ * This will return null IF the ResourceSource is an unpacked WAR file. + *
+ */ + public String getRealPath(); + + /** + * Find the content with a given entry name. This will return null if no + * matching content was found. + */ + public ResourceContent getContent(String entry); + + /** + * Return the content as a String. + */ + public String readString(ResourceContent content, int bufSize) throws IOException; + + /** + * Return the content as a byte[]. + */ + public byte[] readBytes(ResourceContent content, int bufSize) throws IOException; +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java index 0dac4c904..b23554aa5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceContent.java @@ -1,77 +1,77 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.Date; - -/** - * Content from a URL Resource. - */ -public class UrlResourceContent implements ResourceContent { - - /** - * The underlying resource. - */ - //URL url; - - String entryName; - - URLConnection con; - - /** - * Create with a File and the entryName. - */ - public UrlResourceContent(URL url, String entryName) { - //this.url = url; - this.entryName = entryName; - try { - con = url.openConnection(); - } catch (IOException ex){ - throw new RuntimeException(ex); - } - } - - public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("[").append(getName()); - sb.append("] size[").append(size()); - sb.append("] lastModified[").append(new Date(lastModified())); - sb.append("]"); - return sb.toString(); - } - - /** - * Returns the entry name which contains the path from the base directory. - *- * This does not return the full path of the file, but the path relative to - * the FileIoSource directory. - *
- */ - public String getName() { - return entryName; - } - - /** - * Return the time the file was last modified. - */ - public long lastModified() { - return con.getLastModified(); - } - - /** - * Return the size of the file. - */ - public long size() { - return con.getContentLength(); - } - - /** - * Return the input stream for this file. - */ - public InputStream getInputStream() throws IOException { - - return con.getInputStream(); - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Date; + +/** + * Content from a URL Resource. + */ +public class UrlResourceContent implements ResourceContent { + + /** + * The underlying resource. + */ + //URL url; + + String entryName; + + URLConnection con; + + /** + * Create with a File and the entryName. + */ + public UrlResourceContent(URL url, String entryName) { + //this.url = url; + this.entryName = entryName; + try { + con = url.openConnection(); + } catch (IOException ex){ + throw new RuntimeException(ex); + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("[").append(getName()); + sb.append("] size[").append(size()); + sb.append("] lastModified[").append(new Date(lastModified())); + sb.append("]"); + return sb.toString(); + } + + /** + * Returns the entry name which contains the path from the base directory. + *+ * This does not return the full path of the file, but the path relative to + * the FileIoSource directory. + *
+ */ + public String getName() { + return entryName; + } + + /** + * Return the time the file was last modified. + */ + public long lastModified() { + return con.getLastModified(); + } + + /** + * Return the size of the file. + */ + public long size() { + return con.getContentLength(); + } + + /** + * Return the input stream for this file. + */ + public InputStream getInputStream() throws IOException { + + return con.getInputStream(); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java index 5be64a80b..6a60938c6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/UrlResourceSource.java @@ -1,64 +1,64 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.net.MalformedURLException; -import java.net.URL; - -import javax.servlet.ServletContext; - -import com.avaje.ebeaninternal.server.lib.util.GeneralException; - -/** - * A file system directory represented as a FileSource. - */ -public class UrlResourceSource extends AbstractResourceSource implements ResourceSource { - - - ServletContext sc; - - String basePath; - - String realPath; - - /** - * Create the source based on a directory name. - */ - public UrlResourceSource(ServletContext sc, String basePath){ - this.sc = sc; - if (basePath == null){ - this.basePath = "/"; - } else { - this.basePath = "/"+basePath+"/"; - } - this.realPath = sc.getRealPath(basePath); - } - - /** - * Returns the "real path" from the ServletContext root. - *- * This can be null for unpacked WAR deployment. - *
- */ - public String getRealPath() { - return realPath; - } - - /** - * Search for the given URL resource and return as ResourceContent. - *- * Returns null if the resource is not found. - *
- */ - public ResourceContent getContent(String entry) { - - try { - URL url = sc.getResource(basePath+entry); - if (url != null){ - return new UrlResourceContent(url, entry); - } - return null; - - } catch (MalformedURLException ex){ - throw new GeneralException(ex); - } - } -} +package com.avaje.ebeaninternal.server.lib.resource; + +import java.net.MalformedURLException; +import java.net.URL; + +import javax.servlet.ServletContext; + +import com.avaje.ebeaninternal.server.lib.util.GeneralException; + +/** + * A file system directory represented as a FileSource. + */ +public class UrlResourceSource extends AbstractResourceSource implements ResourceSource { + + + ServletContext sc; + + String basePath; + + String realPath; + + /** + * Create the source based on a directory name. + */ + public UrlResourceSource(ServletContext sc, String basePath){ + this.sc = sc; + if (basePath == null){ + this.basePath = "/"; + } else { + this.basePath = "/"+basePath+"/"; + } + this.realPath = sc.getRealPath(basePath); + } + + /** + * Returns the "real path" from the ServletContext root. + *+ * This can be null for unpacked WAR deployment. + *
+ */ + public String getRealPath() { + return realPath; + } + + /** + * Search for the given URL resource and return as ResourceContent. + *+ * Returns null if the resource is not found. + *
+ */ + public ResourceContent getContent(String entry) { + + try { + URL url = sc.getResource(basePath+entry); + if (url != null){ + return new UrlResourceContent(url, entry); + } + return null; + + } catch (MalformedURLException ex){ + throw new GeneralException(ex); + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java index 98b50346b..38be65f7d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/BusyConnectionBuffer.java @@ -1,202 +1,202 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; - -/** - * A buffer especially designed for Busy PooledConnections. - *- * All thread safety controlled externally (by PooledConnectionQueue). - *
- *- * It has a set of 'slots' and PooledConnections know which slot they went into - * and this allows for fast addition and removal (by slotId without looping). - * The capacity will increase on demand by the 'growBy' amount. - *
- * - * @author rbygrave - * - */ -class BusyConnectionBuffer { - - private static final Logger logger = LoggerFactory.getLogger(BusyConnectionBuffer.class); - - private PooledConnection[] slots; - - private int growBy; - - private int size; - - private int pos = -1; - - /** - * Create the buffer with an initial capacity and fixed growBy. - * We generally do not want the buffer to grow very often. - * - * @param capacity - * the initial capacity - * @param growBy - * the fixed amount to grow the buffer by. - */ - protected BusyConnectionBuffer(int capacity, int growBy) { - this.slots = new PooledConnection[capacity]; - this.growBy = growBy; - } - - /** - * We can only grow (not shrink) the capacity. - */ - protected void setCapacity(int newCapacity) { - if (newCapacity > slots.length){ - PooledConnection[] current = this.slots; - this.slots = new PooledConnection[newCapacity]; - System.arraycopy(current, 0, this.slots, 0, current.length); - } - } - - public String toString() { - return Arrays.toString(slots); - } - - protected int getCapacity() { - return slots.length; - } - - protected int size(){ - return size; - } - - protected boolean isEmpty() { - return size == 0; - } - - protected int add(PooledConnection pc){ - if (size == slots.length){ - // grow the capacity - setCapacity(slots.length + growBy); - } - int slot = nextEmptySlot(); - pc.setSlotId(slot); - slots[slot] = pc; - return ++size; - } - - protected boolean remove(PooledConnection pc) { - - int slotId = pc.getSlotId(); - if (slots[slotId] != pc){ - PooledConnection heldBy = slots[slotId]; - logger.warn("Failed to remove from slot[{}] PooledConnection[{}] - HeldBy[{}]", pc.getSlotId(), pc, heldBy); - return false; - } - slots[slotId] = null; - --size; - return true; - } - - /** - * Collect the load statistics from all the busy connections. - * @param reset - */ - protected void collectStatistics(LoadValues values, boolean reset) { - - for (int i = 0; i < slots.length; i++) { - if (slots[i] != null){ - values.plus(slots[i].getStatistics().getValues(reset)); - } - } - } - - /** - * Close connections that should be considered leaked. - */ - protected void closeBusyConnections(long leakTimeMinutes) { - - long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes*60000); - - logger.debug("Closing busy connections using leakTimeMinutes {}", leakTimeMinutes); - - for (int i = 0; i < slots.length; i++) { - if (slots[i] != null){ - //tmp.add(slots[i]); - PooledConnection pc = slots[i]; - if (pc.isLongRunning() || pc.getLastUsedTime() > olderThanTime) { - // PooledConnection has been used recently or - // expected to be longRunning so not closing... - } else { - slots[i] = null; - --size; - closeBusyConnection(pc); - } - } - } - } - - private void closeBusyConnection(PooledConnection pc) { - try { - - logger.warn("DataSourcePool closing busy connection? "+pc.getFullDescription()); - System.out.println("CLOSING busy connection: "+pc.getFullDescription()); - - pc.closeConnectionFully(false); - - } catch (Exception ex) { - // this should never actually happen - logger.error("Error when closing potentially leaked connection "+pc.getDescription(), ex); - } - } - - /** - * Returns information describing connections that are currently being used. - */ - protected String getBusyConnectionInformation(boolean toLogger) { - - if (toLogger) { - logger.info("Dumping [{}] busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)", size()); - } - - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < slots.length; i++) { - if (slots[i] != null){ - PooledConnection pc = slots[i]; - if (toLogger) { - logger.info("Busy Connection - {}", pc.getFullDescription()); - } else { - sb.append(pc.getFullDescription()).append("\r\n"); - } - } - } - - return sb.toString(); - } - - - /** - * Return the position of the next empty slot. - */ - private int nextEmptySlot() { - - // search forward - while(++pos < slots.length) { - if (slots[pos] == null){ - return pos; - } - } - // search from beginning - pos = -1; - while(++pos < slots.length) { - if (slots[pos] == null){ - return pos; - } - } - - // not expecting this - throw new RuntimeException("No Empty Slot Found?"); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.util.Arrays; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; + +/** + * A buffer especially designed for Busy PooledConnections. + *+ * All thread safety controlled externally (by PooledConnectionQueue). + *
+ *+ * It has a set of 'slots' and PooledConnections know which slot they went into + * and this allows for fast addition and removal (by slotId without looping). + * The capacity will increase on demand by the 'growBy' amount. + *
+ * + * @author rbygrave + * + */ +class BusyConnectionBuffer { + + private static final Logger logger = LoggerFactory.getLogger(BusyConnectionBuffer.class); + + private PooledConnection[] slots; + + private int growBy; + + private int size; + + private int pos = -1; + + /** + * Create the buffer with an initial capacity and fixed growBy. + * We generally do not want the buffer to grow very often. + * + * @param capacity + * the initial capacity + * @param growBy + * the fixed amount to grow the buffer by. + */ + protected BusyConnectionBuffer(int capacity, int growBy) { + this.slots = new PooledConnection[capacity]; + this.growBy = growBy; + } + + /** + * We can only grow (not shrink) the capacity. + */ + protected void setCapacity(int newCapacity) { + if (newCapacity > slots.length){ + PooledConnection[] current = this.slots; + this.slots = new PooledConnection[newCapacity]; + System.arraycopy(current, 0, this.slots, 0, current.length); + } + } + + public String toString() { + return Arrays.toString(slots); + } + + protected int getCapacity() { + return slots.length; + } + + protected int size(){ + return size; + } + + protected boolean isEmpty() { + return size == 0; + } + + protected int add(PooledConnection pc){ + if (size == slots.length){ + // grow the capacity + setCapacity(slots.length + growBy); + } + int slot = nextEmptySlot(); + pc.setSlotId(slot); + slots[slot] = pc; + return ++size; + } + + protected boolean remove(PooledConnection pc) { + + int slotId = pc.getSlotId(); + if (slots[slotId] != pc){ + PooledConnection heldBy = slots[slotId]; + logger.warn("Failed to remove from slot[{}] PooledConnection[{}] - HeldBy[{}]", pc.getSlotId(), pc, heldBy); + return false; + } + slots[slotId] = null; + --size; + return true; + } + + /** + * Collect the load statistics from all the busy connections. + * @param reset + */ + protected void collectStatistics(LoadValues values, boolean reset) { + + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null){ + values.plus(slots[i].getStatistics().getValues(reset)); + } + } + } + + /** + * Close connections that should be considered leaked. + */ + protected void closeBusyConnections(long leakTimeMinutes) { + + long olderThanTime = System.currentTimeMillis() - (leakTimeMinutes*60000); + + logger.debug("Closing busy connections using leakTimeMinutes {}", leakTimeMinutes); + + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null){ + //tmp.add(slots[i]); + PooledConnection pc = slots[i]; + if (pc.isLongRunning() || pc.getLastUsedTime() > olderThanTime) { + // PooledConnection has been used recently or + // expected to be longRunning so not closing... + } else { + slots[i] = null; + --size; + closeBusyConnection(pc); + } + } + } + } + + private void closeBusyConnection(PooledConnection pc) { + try { + + logger.warn("DataSourcePool closing busy connection? "+pc.getFullDescription()); + System.out.println("CLOSING busy connection: "+pc.getFullDescription()); + + pc.closeConnectionFully(false); + + } catch (Exception ex) { + // this should never actually happen + logger.error("Error when closing potentially leaked connection "+pc.getDescription(), ex); + } + } + + /** + * Returns information describing connections that are currently being used. + */ + protected String getBusyConnectionInformation(boolean toLogger) { + + if (toLogger) { + logger.info("Dumping [{}] busy connections: (Use datasource.xxx.capturestacktrace=true ... to get stackTraces)", size()); + } + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < slots.length; i++) { + if (slots[i] != null){ + PooledConnection pc = slots[i]; + if (toLogger) { + logger.info("Busy Connection - {}", pc.getFullDescription()); + } else { + sb.append(pc.getFullDescription()).append("\r\n"); + } + } + } + + return sb.toString(); + } + + + /** + * Return the position of the next empty slot. + */ + private int nextEmptySlot() { + + // search forward + while(++pos < slots.length) { + if (slots[pos] == null){ + return pos; + } + } + // search from beginning + pos = -1; + while(++pos < slots.length) { + if (slots[pos] == null){ + return pos; + } + } + + // not expecting this + throw new RuntimeException("No Empty Slot Found?"); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java index 9dc198aaf..0a585033d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourceException.java @@ -1,24 +1,24 @@ -package com.avaje.ebeaninternal.server.lib.sql; - - - -/** - * A general DataSource exception. - */ -public class DataSourceException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539844L; - - public DataSourceException(Exception cause) { - super(cause); - } - - public DataSourceException(String s, Exception cause) { - super(s, cause); - } - - public DataSourceException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + + + +/** + * A general DataSource exception. + */ +public class DataSourceException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539844L; + + public DataSourceException(Exception cause) { + super(cause); + } + + public DataSourceException(String s, Exception cause) { + super(s, cause); + } + + public DataSourceException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java index 0c1c5c2e4..9601cab98 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/DataSourcePool.java @@ -1,1017 +1,1017 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; -import java.sql.Statement; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.Set; - -import javax.persistence.PersistenceException; -import javax.sql.DataSource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebean.config.DataSourceConfig; -import com.avaje.ebeaninternal.api.ClassUtil; - -/** - * A robust DataSource. - *- *
- * This is only used when {@link #isCaptureStackTrace()} is true. - *
- */ - public int getMaxStackTraceSize() { - return maxStackTraceSize; - } - - /** - * Returns false when the dataSource is down. - */ - public boolean isDataSourceUp() { - return dataSourceUp; - } - - /** - * Called when the pool hits the warning level. - */ - protected void notifyWarning(String msg) { - - if (!inWarningMode) { - // send an Error to the event log... - inWarningMode = true; - logger.warn(msg); - if (notify != null) { - String subject = "DataSourcePool [" + name + "] warning"; - notify.dataSourceWarning(subject, msg); - } - } - } - - private void notifyDataSourceIsDown(SQLException ex) { - - if (!dataSourceDownAlertSent) { - logger.error("FATAL: DataSourcePool [" + name + "] is down or has network error!!!", ex); - if (notify != null) { - notify.dataSourceDown(name); - } - dataSourceDownAlertSent = true; - } - if (dataSourceUp) { - reset(); - } - dataSourceUp = false; - } - - private void notifyDataSourceIsUp() { - if (dataSourceDownAlertSent) { - logger.error("RESOLVED FATAL: DataSourcePool [" + name + "] is back up!"); - if (notify != null) { - notify.dataSourceUp(name); - } - dataSourceDownAlertSent = false; - - } else if (!dataSourceUp) { - logger.info("DataSourcePool [" + name + "] is back up!"); - } - - if (!dataSourceUp) { - dataSourceUp = true; - reset(); - } - } - - - /** - * Return the heartbeat frequency in seconds. - *- * This is the frequency that the heartbeat runnable should be run. - *
- */ - public int getHeartbeatFreqSecs() { - return heartbeatFreqSecs; - } - - /** - * Returns the Runnable used to check the dataSource using a heartbeat query. - */ - public Runnable getHeartbeatRunnable() { - return heartbeatRunnable; - } - - /** - * Trim connections (in the free list) based on idle time and maximum age. - */ - private void trimIdleConnections() { - if (System.currentTimeMillis() > (lastTrimTime + trimPoolFreqMillis)) { - try { - queue.trim(maxInactiveMillis, maxAgeMillis); - lastTrimTime = System.currentTimeMillis(); - } catch (Exception e) { - logger.error("Error trying to trim idle connections", e); - } - } - } - - /** - * Check the dataSource is up. Trim connections. - *- * This is called by the HeartbeatRunnable which should be scheduled to - * run periodically (every heartbeatFreqSecs seconds actually). - *
- */ - public void checkDataSource() { - - // first trim idle connections - trimIdleConnections(); - - Connection conn = null; - try { - // Get a connection from the pool and test it - conn = getConnection(); - if (testConnection(conn)) { - notifyDataSourceIsUp(); - - } else { - notifyDataSourceIsDown(null); - } - - } catch (SQLException ex) { - notifyDataSourceIsDown(ex); - - } finally { - try { - if (conn != null) { - conn.close(); - } - } catch (SQLException ex) { - logger.warn("Can't close connection in checkDataSource!"); - } - } - } - - /** - * Create a Connection that will not be part of the connection pool. - * - *- * When this connection is closed it will not go back into the pool. - *
- * - *- * If withDefaults is true then the Connection will have the autoCommit and - * transaction isolation set to the defaults for the pool. - *
- */ - public Connection createUnpooledConnection() throws SQLException { - - try { - Connection conn = DriverManager.getConnection(databaseUrl, connectionProps); - conn.setAutoCommit(autoCommit); - conn.setTransactionIsolation(transactionIsolation); - return conn; - - } catch (SQLException ex) { - notifyDataSourceIsDown(null); - throw ex; - } - } - - /** - * Set a new maximum size. The pool should respect this new maximum - * immediately and not require a restart. You may want to increase the - * maxConnections if the pool gets large and hits the warning level. - */ - public void setMaxSize(int max) { - queue.setMaxSize(max); - this.maxConnections = max; - } - - /** - * Return the max size this pool can grow to. - */ - public int getMaxSize() { - return maxConnections; - } - - /** - * Set the min size this pool should maintain. - */ - public void setMinSize(int min) { - queue.setMinSize(min); - this.minConnections = min; - } - - /** - * Return the min size this pool should maintain. - */ - public int getMinSize() { - return minConnections; - } - - /** - * Set a new maximum size. The pool should respect this new maximum - * immediately and not require a restart. You may want to increase the - * maxConnections if the pool gets large and hits the warning and or alert - * levels. - */ - public void setWarningSize(int warningSize) { - queue.setWarningSize(warningSize); - this.warningSize = warningSize; - } - - /** - * Return the warning size. When the pool hits this size it can send a - * notify message to an administrator. - */ - public int getWarningSize() { - return warningSize; - } - - /** - * Return the time in millis that threads will wait when the pool has hit - * the max size. These threads wait for connections to be returned by the - * busy connections. - */ - public int getWaitTimeoutMillis() { - return waitTimeoutMillis; - } - - /** - * Return the time after which inactive connections are trimmed. - */ - public int getMaxInactiveMillis() { - return maxInactiveMillis; - } - - /** - * Return the maximum age a connection is allowed to be before it is trimmed - * out of the pool. This value can be 0 which means there is no maximum age. - */ - public long getMaxAgeMillis() { - return maxAgeMillis; - } - - private boolean testConnection(Connection conn) throws SQLException { - - if (heartbeatsql == null) { - return conn.isValid(heartbeatTimeoutSeconds); - } - Statement stmt = null; - ResultSet rset = null; - try { - // It should only error IF the DataSource is down or a network issue - stmt = conn.createStatement(); - if (heartbeatTimeoutSeconds > 0) { - stmt.setQueryTimeout(heartbeatTimeoutSeconds); - } - rset = stmt.executeQuery(heartbeatsql); - conn.commit(); - - return true; - - } finally { - try { - if (rset != null) { - rset.close(); - } - } catch (SQLException e) { - logger.error(null, e); - } - try { - if (stmt != null) { - stmt.close(); - } - } catch (SQLException e) { - logger.error(null, e); - } - } - } - - /** - * Make sure the connection is still ok to use. If not then remove it from - * the pool. - */ - protected boolean validateConnection(PooledConnection conn) { - try { - return testConnection(conn); - - } catch (Exception e) { - logger.warn("heartbeatsql test failed on connection[" + conn.getName() + "]"); - return false; - } - } - - /** - * Called by the PooledConnection themselves, returning themselves to the - * pool when they have been finished with. - *- * Note that connections may not be added back to the pool if returnToPool - * is false or if they where created before the recycleTime. In both of - * these cases the connection is fully closed and not pooled. - *
- * - * @param pooledConnection - * the returning connection - * - */ - protected void returnConnection(PooledConnection pooledConnection) { - - // return a normal 'good' connection - returnTheConnection(pooledConnection, false); - } - - /** - * This is a bad connection and must be removed from the pool's busy list and fully closed. - */ - protected void returnConnectionForceClose(PooledConnection pooledConnection) { - - returnTheConnection(pooledConnection, true); - } - - /** - * Return connection. If forceClose is true then this is a bad connection that - * must be removed and closed fully. - */ - private void returnTheConnection(PooledConnection pooledConnection, boolean forceClose) { - - if (poolListener != null && !forceClose) { - poolListener.onBeforeReturnConnection(pooledConnection); - } - queue.returnPooledConnection(pooledConnection, forceClose); - - if (forceClose) { - // Got a bad connection so check the pool - checkDataSource(); - } - } - - /** - * Collect statistics of a connection that is fully closing - */ - protected void reportClosingConnection(PooledConnection pooledConnection) { - - queue.reportClosingConnection(pooledConnection); - } - - /** - * Returns information describing connections that are currently being used. - */ - public String getBusyConnectionInformation() { - - return queue.getBusyConnectionInformation(); - } - - /** - * Dumps the busy connection information to the logs. - *- * This includes the stackTrace elements if they are being captured. This is - * useful when needing to look a potential connection pool leaks. - *
- */ - public void dumpBusyConnectionInformation() { - - queue.dumpBusyConnectionInformation(); - } - - /** - * Close any busy connections that have not been used for some time. - *- * These connections are considered to have leaked from the connection pool. - *
- *- * Connection leaks occur when code doesn't ensure that connections are - * closed() after they have been finished with. There should be an - * appropriate try catch finally block to ensure connections are always - * closed and put back into the pool. - *
- */ - public void closeBusyConnections(long leakTimeMinutes) { - - queue.closeBusyConnections(leakTimeMinutes); - } - - /** - * Grow the pool by creating a new connection. The connection can either be - * added to the available list, or returned. - *- * This method is protected by synchronization in calling methods. - *
- */ - protected PooledConnection createConnectionForQueue(int connId) throws SQLException { - - try { - Connection c = createUnpooledConnection(); - - PooledConnection pc = new PooledConnection(this, connId, c); - pc.resetForUse(); - - if (!dataSourceUp) { - notifyDataSourceIsUp(); - } - return pc; - - } catch (SQLException ex) { - notifyDataSourceIsDown(ex); - throw ex; - } - } - - /** - * Close all the connections in the pool. - *- *
- * This will grow the pool if all the current connections are busy. This - * will go into a wait if the pool has hit its maximum size. - *
- */ - public PooledConnection getPooledConnection() throws SQLException { - - PooledConnection c = queue.getPooledConnection(); - - if (captureStackTrace) { - c.setStackTrace(Thread.currentThread().getStackTrace()); - } - - if (poolListener != null) { - poolListener.onAfterBorrowConnection(c); - } - return c; - } - - /** - * Send a message to the DataSourceAlertListener to test it. This is so that - * you can make sure the alerter is configured correctly etc. - */ - public void testAlert() { - - String subject = "Test DataSourcePool [" + name + "]"; - String msg = "Just testing if alert message is sent successfully."; - - if (notify != null) { - notify.dataSourceWarning(subject, msg); - } - } - - /** - * This will close all the free connections, and then go into a wait loop, - * waiting for the busy connections to be freed. - * - *- * The DataSources's should be shutdown AFTER thread pools. Leaked - * Connections are not waited on, as that would hang the server. - *
- */ - public void shutdown(boolean deregisterDriver) { - queue.shutdown(); - if (deregisterDriver){ - deregisterDriver(); - } - } - - /** - * Return the default autoCommit setting Connections in this pool will use. - * - * @return true if the pool defaults autoCommit to true - */ - public boolean getAutoCommit() { - return autoCommit; - } - - /** - * Return the default transaction isolation level connections in this pool - * should have. - * - * @return the default transaction isolation level - */ - public int getTransactionIsolation() { - return transactionIsolation; - } - - /** - * Return true if the connection pool is currently capturing the StackTrace - * when connections are 'got' from the pool. - *- * This is set to true to help diagnose connection pool leaks. - *
- */ - public boolean isCaptureStackTrace() { - return captureStackTrace; - } - - /** - * Set this to true means that the StackElements are captured every time a - * connection is retrieved from the pool. This can be used to identify - * connection pool leaks. - */ - public void setCaptureStackTrace(boolean captureStackTrace) { - this.captureStackTrace = captureStackTrace; - } - - /** - * Not implemented and shouldn't be used. - */ - public Connection getConnection(String username, String password) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Not implemented and shouldn't be used. - */ - public int getLoginTimeout() throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Not implemented and shouldn't be used. - */ - public void setLoginTimeout(int seconds) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * Returns null. - */ - public PrintWriter getLogWriter() { - return null; - } - - /** - * Not implemented. - */ - public void setLogWriter(PrintWriter writer) throws SQLException { - throw new SQLException("Method not supported"); - } - - /** - * For detecting and closing leaked connections. Connections that have been - * busy for more than leakTimeMinutes are considered leaks and will be - * closed on a reset(). - *- * If you want to use a connection for that longer then you should consider - * creating an unpooled connection or setting longRunning to true on that - * connection. - *
- */ - public void setLeakTimeMinutes(long leakTimeMinutes) { - this.leakTimeMinutes = leakTimeMinutes; - } - - /** - * Return the number of minutes after which a busy connection could be - * considered leaked from the connection pool. - */ - public long getLeakTimeMinutes() { - return leakTimeMinutes; - } - - /** - * Return the preparedStatement cache size. - */ - public int getPstmtCacheSize() { - return pstmtCacheSize; - } - - /** - * Set the preparedStatement cache size. - */ - public void setPstmtCacheSize(int pstmtCacheSize) { - this.pstmtCacheSize = pstmtCacheSize; - } - - /** - * Return the current status of the connection pool. - *- * If you pass reset = true then the counters such as - * hitCount, waitCount and highWaterMark are reset. - *
- */ - public Status getStatus(boolean reset) { - return queue.getStatus(reset); - } - - /** - * Return the aggregated load statistics collected on all the connections in the pool. - */ - public DataSourcePoolStatistics getStatistics(boolean reset) { - - return queue.getStatistics(reset); - } - - /** - * Deregister the JDBC driver. - */ - public void deregisterDriver() { - try { - logger.debug("Deregistered the JDBC driver "+this.databaseDriver); - DriverManager.deregisterDriver(DriverManager.getDriver(this.databaseUrl)); - } catch (SQLException e) { - logger.warn("Error trying to deregister the JDBC driver "+this.databaseDriver, e); - } - } - - public static class Status { - - private final String name; - private final int minSize; - private final int maxSize; - private final int free; - private final int busy; - private final int waiting; - private final int highWaterMark; - private final int waitCount; - private final int hitCount; - - protected Status(String name, int minSize, int maxSize, int free, int busy, int waiting, int highWaterMark, - int waitCount, int hitCount) { - this.name = name; - this.minSize = minSize; - this.maxSize = maxSize; - this.free = free; - this.busy = busy; - this.waiting = waiting; - this.highWaterMark = highWaterMark; - this.waitCount = waitCount; - this.hitCount = hitCount; - } - - public String toString() { - return "min[" + minSize + "] max[" + maxSize + "] free[" + free + "] busy[" + busy + "] waiting[" + waiting - + "] highWaterMark[" + highWaterMark + "] waitCount[" + waitCount + "] hitCount[" + hitCount+"]"; - } - - /** - * Return the DataSource name. - */ - public String getName() { - return name; - } - - /** - * Return the min pool size. - */ - public int getMinSize() { - return minSize; - } - - /** - * Return the max pool size. - */ - public int getMaxSize() { - return maxSize; - } - - /** - * Return the current number of free connections in the pool. - */ - public int getFree() { - return free; - } - - /** - * Return the current number of busy connections in the pool. - */ - public int getBusy() { - return busy; - } - - /** - * Return the current number of threads waiting for a connection. - */ - public int getWaiting() { - return waiting; - } - - /** - * Return the high water mark of busy connections. - */ - public int getHighWaterMark() { - return highWaterMark; - } - - /** - * Return the total number of times a thread had to wait. - */ - public int getWaitCount() { - return waitCount; - } - - /** - * Return the total number of times there was an attempt to get a - * connection. - *- * If the attempt to get a connection failed with a timeout or other - * exception those attempts are still included in this hit count. - *
- */ - public int getHitCount() { - return hitCount; - } - - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.sql.Statement; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.Set; + +import javax.persistence.PersistenceException; +import javax.sql.DataSource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebean.config.DataSourceConfig; +import com.avaje.ebeaninternal.api.ClassUtil; + +/** + * A robust DataSource. + *+ *
+ * This is only used when {@link #isCaptureStackTrace()} is true. + *
+ */ + public int getMaxStackTraceSize() { + return maxStackTraceSize; + } + + /** + * Returns false when the dataSource is down. + */ + public boolean isDataSourceUp() { + return dataSourceUp; + } + + /** + * Called when the pool hits the warning level. + */ + protected void notifyWarning(String msg) { + + if (!inWarningMode) { + // send an Error to the event log... + inWarningMode = true; + logger.warn(msg); + if (notify != null) { + String subject = "DataSourcePool [" + name + "] warning"; + notify.dataSourceWarning(subject, msg); + } + } + } + + private void notifyDataSourceIsDown(SQLException ex) { + + if (!dataSourceDownAlertSent) { + logger.error("FATAL: DataSourcePool [" + name + "] is down or has network error!!!", ex); + if (notify != null) { + notify.dataSourceDown(name); + } + dataSourceDownAlertSent = true; + } + if (dataSourceUp) { + reset(); + } + dataSourceUp = false; + } + + private void notifyDataSourceIsUp() { + if (dataSourceDownAlertSent) { + logger.error("RESOLVED FATAL: DataSourcePool [" + name + "] is back up!"); + if (notify != null) { + notify.dataSourceUp(name); + } + dataSourceDownAlertSent = false; + + } else if (!dataSourceUp) { + logger.info("DataSourcePool [" + name + "] is back up!"); + } + + if (!dataSourceUp) { + dataSourceUp = true; + reset(); + } + } + + + /** + * Return the heartbeat frequency in seconds. + *+ * This is the frequency that the heartbeat runnable should be run. + *
+ */ + public int getHeartbeatFreqSecs() { + return heartbeatFreqSecs; + } + + /** + * Returns the Runnable used to check the dataSource using a heartbeat query. + */ + public Runnable getHeartbeatRunnable() { + return heartbeatRunnable; + } + + /** + * Trim connections (in the free list) based on idle time and maximum age. + */ + private void trimIdleConnections() { + if (System.currentTimeMillis() > (lastTrimTime + trimPoolFreqMillis)) { + try { + queue.trim(maxInactiveMillis, maxAgeMillis); + lastTrimTime = System.currentTimeMillis(); + } catch (Exception e) { + logger.error("Error trying to trim idle connections", e); + } + } + } + + /** + * Check the dataSource is up. Trim connections. + *+ * This is called by the HeartbeatRunnable which should be scheduled to + * run periodically (every heartbeatFreqSecs seconds actually). + *
+ */ + public void checkDataSource() { + + // first trim idle connections + trimIdleConnections(); + + Connection conn = null; + try { + // Get a connection from the pool and test it + conn = getConnection(); + if (testConnection(conn)) { + notifyDataSourceIsUp(); + + } else { + notifyDataSourceIsDown(null); + } + + } catch (SQLException ex) { + notifyDataSourceIsDown(ex); + + } finally { + try { + if (conn != null) { + conn.close(); + } + } catch (SQLException ex) { + logger.warn("Can't close connection in checkDataSource!"); + } + } + } + + /** + * Create a Connection that will not be part of the connection pool. + * + *+ * When this connection is closed it will not go back into the pool. + *
+ * + *+ * If withDefaults is true then the Connection will have the autoCommit and + * transaction isolation set to the defaults for the pool. + *
+ */ + public Connection createUnpooledConnection() throws SQLException { + + try { + Connection conn = DriverManager.getConnection(databaseUrl, connectionProps); + conn.setAutoCommit(autoCommit); + conn.setTransactionIsolation(transactionIsolation); + return conn; + + } catch (SQLException ex) { + notifyDataSourceIsDown(null); + throw ex; + } + } + + /** + * Set a new maximum size. The pool should respect this new maximum + * immediately and not require a restart. You may want to increase the + * maxConnections if the pool gets large and hits the warning level. + */ + public void setMaxSize(int max) { + queue.setMaxSize(max); + this.maxConnections = max; + } + + /** + * Return the max size this pool can grow to. + */ + public int getMaxSize() { + return maxConnections; + } + + /** + * Set the min size this pool should maintain. + */ + public void setMinSize(int min) { + queue.setMinSize(min); + this.minConnections = min; + } + + /** + * Return the min size this pool should maintain. + */ + public int getMinSize() { + return minConnections; + } + + /** + * Set a new maximum size. The pool should respect this new maximum + * immediately and not require a restart. You may want to increase the + * maxConnections if the pool gets large and hits the warning and or alert + * levels. + */ + public void setWarningSize(int warningSize) { + queue.setWarningSize(warningSize); + this.warningSize = warningSize; + } + + /** + * Return the warning size. When the pool hits this size it can send a + * notify message to an administrator. + */ + public int getWarningSize() { + return warningSize; + } + + /** + * Return the time in millis that threads will wait when the pool has hit + * the max size. These threads wait for connections to be returned by the + * busy connections. + */ + public int getWaitTimeoutMillis() { + return waitTimeoutMillis; + } + + /** + * Return the time after which inactive connections are trimmed. + */ + public int getMaxInactiveMillis() { + return maxInactiveMillis; + } + + /** + * Return the maximum age a connection is allowed to be before it is trimmed + * out of the pool. This value can be 0 which means there is no maximum age. + */ + public long getMaxAgeMillis() { + return maxAgeMillis; + } + + private boolean testConnection(Connection conn) throws SQLException { + + if (heartbeatsql == null) { + return conn.isValid(heartbeatTimeoutSeconds); + } + Statement stmt = null; + ResultSet rset = null; + try { + // It should only error IF the DataSource is down or a network issue + stmt = conn.createStatement(); + if (heartbeatTimeoutSeconds > 0) { + stmt.setQueryTimeout(heartbeatTimeoutSeconds); + } + rset = stmt.executeQuery(heartbeatsql); + conn.commit(); + + return true; + + } finally { + try { + if (rset != null) { + rset.close(); + } + } catch (SQLException e) { + logger.error(null, e); + } + try { + if (stmt != null) { + stmt.close(); + } + } catch (SQLException e) { + logger.error(null, e); + } + } + } + + /** + * Make sure the connection is still ok to use. If not then remove it from + * the pool. + */ + protected boolean validateConnection(PooledConnection conn) { + try { + return testConnection(conn); + + } catch (Exception e) { + logger.warn("heartbeatsql test failed on connection[" + conn.getName() + "]"); + return false; + } + } + + /** + * Called by the PooledConnection themselves, returning themselves to the + * pool when they have been finished with. + *+ * Note that connections may not be added back to the pool if returnToPool + * is false or if they where created before the recycleTime. In both of + * these cases the connection is fully closed and not pooled. + *
+ * + * @param pooledConnection + * the returning connection + * + */ + protected void returnConnection(PooledConnection pooledConnection) { + + // return a normal 'good' connection + returnTheConnection(pooledConnection, false); + } + + /** + * This is a bad connection and must be removed from the pool's busy list and fully closed. + */ + protected void returnConnectionForceClose(PooledConnection pooledConnection) { + + returnTheConnection(pooledConnection, true); + } + + /** + * Return connection. If forceClose is true then this is a bad connection that + * must be removed and closed fully. + */ + private void returnTheConnection(PooledConnection pooledConnection, boolean forceClose) { + + if (poolListener != null && !forceClose) { + poolListener.onBeforeReturnConnection(pooledConnection); + } + queue.returnPooledConnection(pooledConnection, forceClose); + + if (forceClose) { + // Got a bad connection so check the pool + checkDataSource(); + } + } + + /** + * Collect statistics of a connection that is fully closing + */ + protected void reportClosingConnection(PooledConnection pooledConnection) { + + queue.reportClosingConnection(pooledConnection); + } + + /** + * Returns information describing connections that are currently being used. + */ + public String getBusyConnectionInformation() { + + return queue.getBusyConnectionInformation(); + } + + /** + * Dumps the busy connection information to the logs. + *+ * This includes the stackTrace elements if they are being captured. This is + * useful when needing to look a potential connection pool leaks. + *
+ */ + public void dumpBusyConnectionInformation() { + + queue.dumpBusyConnectionInformation(); + } + + /** + * Close any busy connections that have not been used for some time. + *+ * These connections are considered to have leaked from the connection pool. + *
+ *+ * Connection leaks occur when code doesn't ensure that connections are + * closed() after they have been finished with. There should be an + * appropriate try catch finally block to ensure connections are always + * closed and put back into the pool. + *
+ */ + public void closeBusyConnections(long leakTimeMinutes) { + + queue.closeBusyConnections(leakTimeMinutes); + } + + /** + * Grow the pool by creating a new connection. The connection can either be + * added to the available list, or returned. + *+ * This method is protected by synchronization in calling methods. + *
+ */ + protected PooledConnection createConnectionForQueue(int connId) throws SQLException { + + try { + Connection c = createUnpooledConnection(); + + PooledConnection pc = new PooledConnection(this, connId, c); + pc.resetForUse(); + + if (!dataSourceUp) { + notifyDataSourceIsUp(); + } + return pc; + + } catch (SQLException ex) { + notifyDataSourceIsDown(ex); + throw ex; + } + } + + /** + * Close all the connections in the pool. + *+ *
+ * This will grow the pool if all the current connections are busy. This + * will go into a wait if the pool has hit its maximum size. + *
+ */ + public PooledConnection getPooledConnection() throws SQLException { + + PooledConnection c = queue.getPooledConnection(); + + if (captureStackTrace) { + c.setStackTrace(Thread.currentThread().getStackTrace()); + } + + if (poolListener != null) { + poolListener.onAfterBorrowConnection(c); + } + return c; + } + + /** + * Send a message to the DataSourceAlertListener to test it. This is so that + * you can make sure the alerter is configured correctly etc. + */ + public void testAlert() { + + String subject = "Test DataSourcePool [" + name + "]"; + String msg = "Just testing if alert message is sent successfully."; + + if (notify != null) { + notify.dataSourceWarning(subject, msg); + } + } + + /** + * This will close all the free connections, and then go into a wait loop, + * waiting for the busy connections to be freed. + * + *+ * The DataSources's should be shutdown AFTER thread pools. Leaked + * Connections are not waited on, as that would hang the server. + *
+ */ + public void shutdown(boolean deregisterDriver) { + queue.shutdown(); + if (deregisterDriver){ + deregisterDriver(); + } + } + + /** + * Return the default autoCommit setting Connections in this pool will use. + * + * @return true if the pool defaults autoCommit to true + */ + public boolean getAutoCommit() { + return autoCommit; + } + + /** + * Return the default transaction isolation level connections in this pool + * should have. + * + * @return the default transaction isolation level + */ + public int getTransactionIsolation() { + return transactionIsolation; + } + + /** + * Return true if the connection pool is currently capturing the StackTrace + * when connections are 'got' from the pool. + *+ * This is set to true to help diagnose connection pool leaks. + *
+ */ + public boolean isCaptureStackTrace() { + return captureStackTrace; + } + + /** + * Set this to true means that the StackElements are captured every time a + * connection is retrieved from the pool. This can be used to identify + * connection pool leaks. + */ + public void setCaptureStackTrace(boolean captureStackTrace) { + this.captureStackTrace = captureStackTrace; + } + + /** + * Not implemented and shouldn't be used. + */ + public Connection getConnection(String username, String password) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Not implemented and shouldn't be used. + */ + public int getLoginTimeout() throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Not implemented and shouldn't be used. + */ + public void setLoginTimeout(int seconds) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * Returns null. + */ + public PrintWriter getLogWriter() { + return null; + } + + /** + * Not implemented. + */ + public void setLogWriter(PrintWriter writer) throws SQLException { + throw new SQLException("Method not supported"); + } + + /** + * For detecting and closing leaked connections. Connections that have been + * busy for more than leakTimeMinutes are considered leaks and will be + * closed on a reset(). + *+ * If you want to use a connection for that longer then you should consider + * creating an unpooled connection or setting longRunning to true on that + * connection. + *
+ */ + public void setLeakTimeMinutes(long leakTimeMinutes) { + this.leakTimeMinutes = leakTimeMinutes; + } + + /** + * Return the number of minutes after which a busy connection could be + * considered leaked from the connection pool. + */ + public long getLeakTimeMinutes() { + return leakTimeMinutes; + } + + /** + * Return the preparedStatement cache size. + */ + public int getPstmtCacheSize() { + return pstmtCacheSize; + } + + /** + * Set the preparedStatement cache size. + */ + public void setPstmtCacheSize(int pstmtCacheSize) { + this.pstmtCacheSize = pstmtCacheSize; + } + + /** + * Return the current status of the connection pool. + *+ * If you pass reset = true then the counters such as + * hitCount, waitCount and highWaterMark are reset. + *
+ */ + public Status getStatus(boolean reset) { + return queue.getStatus(reset); + } + + /** + * Return the aggregated load statistics collected on all the connections in the pool. + */ + public DataSourcePoolStatistics getStatistics(boolean reset) { + + return queue.getStatistics(reset); + } + + /** + * Deregister the JDBC driver. + */ + public void deregisterDriver() { + try { + logger.debug("Deregistered the JDBC driver "+this.databaseDriver); + DriverManager.deregisterDriver(DriverManager.getDriver(this.databaseUrl)); + } catch (SQLException e) { + logger.warn("Error trying to deregister the JDBC driver "+this.databaseDriver, e); + } + } + + public static class Status { + + private final String name; + private final int minSize; + private final int maxSize; + private final int free; + private final int busy; + private final int waiting; + private final int highWaterMark; + private final int waitCount; + private final int hitCount; + + protected Status(String name, int minSize, int maxSize, int free, int busy, int waiting, int highWaterMark, + int waitCount, int hitCount) { + this.name = name; + this.minSize = minSize; + this.maxSize = maxSize; + this.free = free; + this.busy = busy; + this.waiting = waiting; + this.highWaterMark = highWaterMark; + this.waitCount = waitCount; + this.hitCount = hitCount; + } + + public String toString() { + return "min[" + minSize + "] max[" + maxSize + "] free[" + free + "] busy[" + busy + "] waiting[" + waiting + + "] highWaterMark[" + highWaterMark + "] waitCount[" + waitCount + "] hitCount[" + hitCount+"]"; + } + + /** + * Return the DataSource name. + */ + public String getName() { + return name; + } + + /** + * Return the min pool size. + */ + public int getMinSize() { + return minSize; + } + + /** + * Return the max pool size. + */ + public int getMaxSize() { + return maxSize; + } + + /** + * Return the current number of free connections in the pool. + */ + public int getFree() { + return free; + } + + /** + * Return the current number of busy connections in the pool. + */ + public int getBusy() { + return busy; + } + + /** + * Return the current number of threads waiting for a connection. + */ + public int getWaiting() { + return waiting; + } + + /** + * Return the high water mark of busy connections. + */ + public int getHighWaterMark() { + return highWaterMark; + } + + /** + * Return the total number of times a thread had to wait. + */ + public int getWaitCount() { + return waitCount; + } + + /** + * Return the total number of times there was an attempt to get a + * connection. + *+ * If the attempt to get a connection failed with a timeout or other + * exception those attempts are still included in this hit count. + *
+ */ + public int getHitCount() { + return hitCount; + } + + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java index 439b0731f..9e97996fc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedPreparedStatement.java @@ -1,389 +1,389 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.Array; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Date; -import java.sql.ParameterMetaData; -import java.sql.PreparedStatement; -import java.sql.Ref; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Calendar; - -/** - * Extended PreparedStatement that supports caching. - *- * Designed so that it can be cached by the PooledConnection. It additionally - * notes any Exceptions that occur and this is used to ensure bad connections - * are removed from the connection pool. - *
- */ -public class ExtendedPreparedStatement extends ExtendedStatement implements PreparedStatement { - - /** - * The SQL used to create the underlying PreparedStatement. - */ - private final String sql; - - /** - * The key used to cache this in the connection. - */ - private final String cacheKey; - - /** - * Create a wrapped PreparedStatement that can be cached. - */ - public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, String sql, String cacheKey) { - super(pooledConnection, pstmt); - this.sql = sql; - this.cacheKey = cacheKey; - } - - public PreparedStatement getDelegate() { - return pstmt; - } - - /** - * Return the key used to cache this on the Connection. - */ - public String getCacheKey() { - return cacheKey; - } - - /** - * Return the SQL used to create this PreparedStatement. - */ - public String getSql() { - return sql; - } - - /** - * Fully close the underlying PreparedStatement. After this we can no longer - * reuse the PreparedStatement. - */ - public void closeDestroy() throws SQLException { - pstmt.close(); - } - - /** - * Returns the PreparedStatement back into the cache. This doesn't fully - * close the underlying PreparedStatement. - */ - public void close() throws SQLException { - // return the connection back into the cache. - pooledConnection.returnPreparedStatement(this); - } - - /** - * Add the last binding for batch execution. - */ - public void addBatch() throws SQLException { - try { - pstmt.addBatch(); - } catch (SQLException e) { - // we got an error... need to check this - // connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Clear parameters. - */ - public void clearParameters() throws SQLException { - try { - pstmt.clearParameters(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * execute the statement. - */ - public boolean execute() throws SQLException { - try { - return pstmt.execute(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute teh query. - */ - public ResultSet executeQuery() throws SQLException { - try { - return pstmt.executeQuery(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the dml statement. - */ - public int executeUpdate() throws SQLException { - try { - return pstmt.executeUpdate(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Return the MetaData for the query. - */ - public ResultSetMetaData getMetaData() throws SQLException { - try { - return pstmt.getMetaData(); - } catch (SQLException e) { - // we got an error... need to check - // this connection before returning it - pooledConnection.addError(e); - throw e; - } - } - - /** - * Standard PreparedStatement method execution. - */ - public ParameterMetaData getParameterMetaData() throws SQLException { - return pstmt.getParameterMetaData(); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setArray(int i, Array x) throws SQLException { - pstmt.setArray(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setAsciiStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { - pstmt.setBigDecimal(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setBinaryStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBlob(int i, Blob x) throws SQLException { - pstmt.setBlob(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBoolean(int parameterIndex, boolean x) throws SQLException { - pstmt.setBoolean(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setByte(int parameterIndex, byte x) throws SQLException { - pstmt.setByte(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setBytes(int parameterIndex, byte[] x) throws SQLException { - pstmt.setBytes(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setCharacterStream(int parameterIndex, Reader reader, int length) - throws SQLException { - pstmt.setCharacterStream(parameterIndex, reader, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setClob(int i, Clob x) throws SQLException { - pstmt.setClob(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDate(int parameterIndex, Date x) throws SQLException { - pstmt.setDate(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { - pstmt.setDate(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setDouble(int parameterIndex, double x) throws SQLException { - pstmt.setDouble(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setFloat(int parameterIndex, float x) throws SQLException { - pstmt.setFloat(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setInt(int parameterIndex, int x) throws SQLException { - pstmt.setInt(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setLong(int parameterIndex, long x) throws SQLException { - pstmt.setLong(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setNull(int parameterIndex, int sqlType) throws SQLException { - pstmt.setNull(parameterIndex, sqlType); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { - pstmt.setNull(paramIndex, sqlType, typeName); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x) throws SQLException { - pstmt.setObject(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { - pstmt.setObject(parameterIndex, x, targetSqlType); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) - throws SQLException { - pstmt.setObject(parameterIndex, x, targetSqlType, scale); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setRef(int i, Ref x) throws SQLException { - pstmt.setRef(i, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setShort(int parameterIndex, short x) throws SQLException { - pstmt.setShort(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setString(int parameterIndex, String x) throws SQLException { - pstmt.setString(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTime(int parameterIndex, Time x) throws SQLException { - pstmt.setTime(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { - pstmt.setTime(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { - pstmt.setTimestamp(parameterIndex, x); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { - pstmt.setTimestamp(parameterIndex, x, cal); - } - - /** - * Standard PreparedStatement method execution. - * @deprecated - */ - public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { - pstmt.setUnicodeStream(parameterIndex, x, length); - } - - /** - * Standard PreparedStatement method execution. - */ - public void setURL(int parameterIndex, URL x) throws SQLException { - pstmt.setURL(parameterIndex, x); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Date; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.Ref; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Calendar; + +/** + * Extended PreparedStatement that supports caching. + *+ * Designed so that it can be cached by the PooledConnection. It additionally + * notes any Exceptions that occur and this is used to ensure bad connections + * are removed from the connection pool. + *
+ */ +public class ExtendedPreparedStatement extends ExtendedStatement implements PreparedStatement { + + /** + * The SQL used to create the underlying PreparedStatement. + */ + private final String sql; + + /** + * The key used to cache this in the connection. + */ + private final String cacheKey; + + /** + * Create a wrapped PreparedStatement that can be cached. + */ + public ExtendedPreparedStatement(PooledConnection pooledConnection, PreparedStatement pstmt, String sql, String cacheKey) { + super(pooledConnection, pstmt); + this.sql = sql; + this.cacheKey = cacheKey; + } + + public PreparedStatement getDelegate() { + return pstmt; + } + + /** + * Return the key used to cache this on the Connection. + */ + public String getCacheKey() { + return cacheKey; + } + + /** + * Return the SQL used to create this PreparedStatement. + */ + public String getSql() { + return sql; + } + + /** + * Fully close the underlying PreparedStatement. After this we can no longer + * reuse the PreparedStatement. + */ + public void closeDestroy() throws SQLException { + pstmt.close(); + } + + /** + * Returns the PreparedStatement back into the cache. This doesn't fully + * close the underlying PreparedStatement. + */ + public void close() throws SQLException { + // return the connection back into the cache. + pooledConnection.returnPreparedStatement(this); + } + + /** + * Add the last binding for batch execution. + */ + public void addBatch() throws SQLException { + try { + pstmt.addBatch(); + } catch (SQLException e) { + // we got an error... need to check this + // connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Clear parameters. + */ + public void clearParameters() throws SQLException { + try { + pstmt.clearParameters(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * execute the statement. + */ + public boolean execute() throws SQLException { + try { + return pstmt.execute(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute teh query. + */ + public ResultSet executeQuery() throws SQLException { + try { + return pstmt.executeQuery(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the dml statement. + */ + public int executeUpdate() throws SQLException { + try { + return pstmt.executeUpdate(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Return the MetaData for the query. + */ + public ResultSetMetaData getMetaData() throws SQLException { + try { + return pstmt.getMetaData(); + } catch (SQLException e) { + // we got an error... need to check + // this connection before returning it + pooledConnection.addError(e); + throw e; + } + } + + /** + * Standard PreparedStatement method execution. + */ + public ParameterMetaData getParameterMetaData() throws SQLException { + return pstmt.getParameterMetaData(); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setArray(int i, Array x) throws SQLException { + pstmt.setArray(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setAsciiStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + pstmt.setBigDecimal(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setBinaryStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBlob(int i, Blob x) throws SQLException { + pstmt.setBlob(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + pstmt.setBoolean(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setByte(int parameterIndex, byte x) throws SQLException { + pstmt.setByte(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + pstmt.setBytes(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setCharacterStream(int parameterIndex, Reader reader, int length) + throws SQLException { + pstmt.setCharacterStream(parameterIndex, reader, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setClob(int i, Clob x) throws SQLException { + pstmt.setClob(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDate(int parameterIndex, Date x) throws SQLException { + pstmt.setDate(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + pstmt.setDate(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setDouble(int parameterIndex, double x) throws SQLException { + pstmt.setDouble(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setFloat(int parameterIndex, float x) throws SQLException { + pstmt.setFloat(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setInt(int parameterIndex, int x) throws SQLException { + pstmt.setInt(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setLong(int parameterIndex, long x) throws SQLException { + pstmt.setLong(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setNull(int parameterIndex, int sqlType) throws SQLException { + pstmt.setNull(parameterIndex, sqlType); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { + pstmt.setNull(paramIndex, sqlType, typeName); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x) throws SQLException { + pstmt.setObject(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + pstmt.setObject(parameterIndex, x, targetSqlType); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) + throws SQLException { + pstmt.setObject(parameterIndex, x, targetSqlType, scale); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setRef(int i, Ref x) throws SQLException { + pstmt.setRef(i, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setShort(int parameterIndex, short x) throws SQLException { + pstmt.setShort(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setString(int parameterIndex, String x) throws SQLException { + pstmt.setString(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTime(int parameterIndex, Time x) throws SQLException { + pstmt.setTime(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + pstmt.setTime(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + pstmt.setTimestamp(parameterIndex, x); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + pstmt.setTimestamp(parameterIndex, x, cal); + } + + /** + * Standard PreparedStatement method execution. + * @deprecated + */ + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + pstmt.setUnicodeStream(parameterIndex, x, length); + } + + /** + * Standard PreparedStatement method execution. + */ + public void setURL(int parameterIndex, URL x) throws SQLException { + pstmt.setURL(parameterIndex, x); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java index 780cf7625..46fce0992 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/ExtendedStatement.java @@ -1,328 +1,328 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.SQLWarning; - -import com.avaje.ebeaninternal.jdbc.PreparedStatementDelegator; - -/** - * Implements the Statement methods for ExtendedPreparedStatement. - *- * PreparedStatements should always be used and the intention is that there - * should be no use of Statement at all. The implementation here is generally - * for the case where someone uses the Statement api on an ExtendedPreparedStatement. - *
- */ -public abstract class ExtendedStatement extends PreparedStatementDelegator -{ - - /** - * The pooled connection this Statement belongs to. - */ - protected final PooledConnection pooledConnection; - - /** - * The underlying Statement that this object wraps. - */ - protected final PreparedStatement pstmt; - - /** - * Create the ExtendedStatement for a given pooledConnection. - */ - public ExtendedStatement(PooledConnection pooledConnection, PreparedStatement pstmt) { - super(pstmt); - - this.pooledConnection = pooledConnection; - this.pstmt = pstmt; - } - - /** - * Put the statement back into the statement cache. - */ - public abstract void close() throws SQLException; - - /** - * Return the underlying connection. - */ - public Connection getConnection() throws SQLException { - try { - return pstmt.getConnection(); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Add the sql for batch execution. - */ - public void addBatch(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - pstmt.addBatch(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the sql. - */ - public boolean execute(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.execute(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the query. - */ - public ResultSet executeQuery(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.executeQuery(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Execute the dml sql. - */ - public int executeUpdate(String sql) throws SQLException { - try { - pooledConnection.setLastStatement(sql); - return pstmt.executeUpdate(sql); - } catch (SQLException e) { - pooledConnection.addError(e); - throw e; - } - } - - /** - * Standard Statement method call. - */ - public int[] executeBatch() throws SQLException { - return pstmt.executeBatch(); - } - - /** - * Standard Statement method call. - */ - public void cancel() throws SQLException { - pstmt.cancel(); - } - - /** - * Standard Statement method call. - */ - public void clearBatch() throws SQLException { - pstmt.clearBatch(); - } - - /** - * Standard Statement method call. - */ - public void clearWarnings() throws SQLException { - pstmt.clearWarnings(); - } - - /** - * Standard Statement method call. - */ - public int getFetchDirection() throws SQLException { - return pstmt.getFetchDirection(); - } - - /** - * Standard Statement method call. - */ - public int getFetchSize() throws SQLException { - return pstmt.getFetchSize(); - } - - /** - * Standard Statement method call. - */ - public int getMaxFieldSize() throws SQLException { - return pstmt.getMaxFieldSize(); - } - - /** - * Standard Statement method call. - */ - public int getMaxRows() throws SQLException { - return pstmt.getMaxRows(); - } - - /** - * Standard Statement method call. - */ - public boolean getMoreResults() throws SQLException { - return pstmt.getMoreResults(); - } - - /** - * Standard Statement method call. - */ - public int getQueryTimeout() throws SQLException { - return pstmt.getQueryTimeout(); - } - - /** - * Standard Statement method call. - */ - public ResultSet getResultSet() throws SQLException { - return pstmt.getResultSet(); - } - - /** - * Standard Statement method call. - */ - public int getResultSetConcurrency() throws SQLException { - return pstmt.getResultSetConcurrency(); - } - - /** - * Standard Statement method call. - */ - public int getResultSetType() throws SQLException { - return pstmt.getResultSetType(); - } - - /** - * Standard Statement method call. - */ - public int getUpdateCount() throws SQLException { - return pstmt.getUpdateCount(); - } - - /** - * Standard Statement method call. - */ - public SQLWarning getWarnings() throws SQLException { - return pstmt.getWarnings(); - } - - /** - * Standard Statement method call. - */ - public void setCursorName(String name) throws SQLException { - pstmt.setCursorName(name); - } - - /** - * Standard Statement method call. - */ - public void setEscapeProcessing(boolean enable) throws SQLException { - pstmt.setEscapeProcessing(enable); - } - - /** - * Standard Statement method call. - */ - public void setFetchDirection(int direction) throws SQLException { - pstmt.setFetchDirection(direction); - } - - /** - * Standard Statement method call. - */ - public void setFetchSize(int rows) throws SQLException { - pstmt.setFetchSize(rows); - } - - /** - * Standard Statement method call. - */ - public void setMaxFieldSize(int max) throws SQLException { - pstmt.setMaxFieldSize(max); - } - - /** - * Standard Statement method call. - */ - public void setMaxRows(int max) throws SQLException { - pstmt.setMaxRows(max); - } - - /** - * Standard Statement method call. - */ - public void setQueryTimeout(int seconds) throws SQLException { - pstmt.setQueryTimeout(seconds); - } - - /** - * Standard Statement method call. - */ - public boolean getMoreResults(int i) throws SQLException { - return pstmt.getMoreResults(i); - } - - /** - * Standard Statement method call. - */ - public ResultSet getGeneratedKeys() throws SQLException { - return pstmt.getGeneratedKeys(); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, int i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, int[] i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public int executeUpdate(String s, String[] i) throws SQLException { - return pstmt.executeUpdate(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, int i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, int[] i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public boolean execute(String s, String[] i) throws SQLException { - return pstmt.execute(s, i); - } - - /** - * Standard Statement method call. - */ - public int getResultSetHoldability() throws SQLException { - return pstmt.getResultSetHoldability(); - } - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; + +import com.avaje.ebeaninternal.jdbc.PreparedStatementDelegator; + +/** + * Implements the Statement methods for ExtendedPreparedStatement. + *+ * PreparedStatements should always be used and the intention is that there + * should be no use of Statement at all. The implementation here is generally + * for the case where someone uses the Statement api on an ExtendedPreparedStatement. + *
+ */ +public abstract class ExtendedStatement extends PreparedStatementDelegator +{ + + /** + * The pooled connection this Statement belongs to. + */ + protected final PooledConnection pooledConnection; + + /** + * The underlying Statement that this object wraps. + */ + protected final PreparedStatement pstmt; + + /** + * Create the ExtendedStatement for a given pooledConnection. + */ + public ExtendedStatement(PooledConnection pooledConnection, PreparedStatement pstmt) { + super(pstmt); + + this.pooledConnection = pooledConnection; + this.pstmt = pstmt; + } + + /** + * Put the statement back into the statement cache. + */ + public abstract void close() throws SQLException; + + /** + * Return the underlying connection. + */ + public Connection getConnection() throws SQLException { + try { + return pstmt.getConnection(); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Add the sql for batch execution. + */ + public void addBatch(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + pstmt.addBatch(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the sql. + */ + public boolean execute(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.execute(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the query. + */ + public ResultSet executeQuery(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.executeQuery(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Execute the dml sql. + */ + public int executeUpdate(String sql) throws SQLException { + try { + pooledConnection.setLastStatement(sql); + return pstmt.executeUpdate(sql); + } catch (SQLException e) { + pooledConnection.addError(e); + throw e; + } + } + + /** + * Standard Statement method call. + */ + public int[] executeBatch() throws SQLException { + return pstmt.executeBatch(); + } + + /** + * Standard Statement method call. + */ + public void cancel() throws SQLException { + pstmt.cancel(); + } + + /** + * Standard Statement method call. + */ + public void clearBatch() throws SQLException { + pstmt.clearBatch(); + } + + /** + * Standard Statement method call. + */ + public void clearWarnings() throws SQLException { + pstmt.clearWarnings(); + } + + /** + * Standard Statement method call. + */ + public int getFetchDirection() throws SQLException { + return pstmt.getFetchDirection(); + } + + /** + * Standard Statement method call. + */ + public int getFetchSize() throws SQLException { + return pstmt.getFetchSize(); + } + + /** + * Standard Statement method call. + */ + public int getMaxFieldSize() throws SQLException { + return pstmt.getMaxFieldSize(); + } + + /** + * Standard Statement method call. + */ + public int getMaxRows() throws SQLException { + return pstmt.getMaxRows(); + } + + /** + * Standard Statement method call. + */ + public boolean getMoreResults() throws SQLException { + return pstmt.getMoreResults(); + } + + /** + * Standard Statement method call. + */ + public int getQueryTimeout() throws SQLException { + return pstmt.getQueryTimeout(); + } + + /** + * Standard Statement method call. + */ + public ResultSet getResultSet() throws SQLException { + return pstmt.getResultSet(); + } + + /** + * Standard Statement method call. + */ + public int getResultSetConcurrency() throws SQLException { + return pstmt.getResultSetConcurrency(); + } + + /** + * Standard Statement method call. + */ + public int getResultSetType() throws SQLException { + return pstmt.getResultSetType(); + } + + /** + * Standard Statement method call. + */ + public int getUpdateCount() throws SQLException { + return pstmt.getUpdateCount(); + } + + /** + * Standard Statement method call. + */ + public SQLWarning getWarnings() throws SQLException { + return pstmt.getWarnings(); + } + + /** + * Standard Statement method call. + */ + public void setCursorName(String name) throws SQLException { + pstmt.setCursorName(name); + } + + /** + * Standard Statement method call. + */ + public void setEscapeProcessing(boolean enable) throws SQLException { + pstmt.setEscapeProcessing(enable); + } + + /** + * Standard Statement method call. + */ + public void setFetchDirection(int direction) throws SQLException { + pstmt.setFetchDirection(direction); + } + + /** + * Standard Statement method call. + */ + public void setFetchSize(int rows) throws SQLException { + pstmt.setFetchSize(rows); + } + + /** + * Standard Statement method call. + */ + public void setMaxFieldSize(int max) throws SQLException { + pstmt.setMaxFieldSize(max); + } + + /** + * Standard Statement method call. + */ + public void setMaxRows(int max) throws SQLException { + pstmt.setMaxRows(max); + } + + /** + * Standard Statement method call. + */ + public void setQueryTimeout(int seconds) throws SQLException { + pstmt.setQueryTimeout(seconds); + } + + /** + * Standard Statement method call. + */ + public boolean getMoreResults(int i) throws SQLException { + return pstmt.getMoreResults(i); + } + + /** + * Standard Statement method call. + */ + public ResultSet getGeneratedKeys() throws SQLException { + return pstmt.getGeneratedKeys(); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, int i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, int[] i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public int executeUpdate(String s, String[] i) throws SQLException { + return pstmt.executeUpdate(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, int i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, int[] i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public boolean execute(String s, String[] i) throws SQLException { + return pstmt.execute(s, i); + } + + /** + * Standard Statement method call. + */ + public int getResultSetHoldability() throws SQLException { + return pstmt.getResultSetHoldability(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java index 8fede2fc2..778410909 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/FreeConnectionBuffer.java @@ -1,106 +1,106 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; - -/** - * A buffer designed especially to hold free pooled connections. - *- * All thread safety controlled externally (by PooledConnectionQueue). - *
- */ -class FreeConnectionBuffer { - - private static final Logger logger = LoggerFactory.getLogger(FreeConnectionBuffer.class); - - /** - * Buffer oriented for add and remove. - */ - private final LinkedList+ * All thread safety controlled externally (by PooledConnectionQueue). + *
+ */ +class FreeConnectionBuffer { + + private static final Logger logger = LoggerFactory.getLogger(FreeConnectionBuffer.class); + + /** + * Buffer oriented for add and remove. + */ + private final LinkedList- * It is designed to be part of DataSourcePool. Closing the connection puts it - * back into the pool. - *
- * - *- * It defaults autoCommit and Transaction Isolation to the defaults of the - * DataSourcePool. - *
- * - *- * It has caching of Statements and PreparedStatements. Remembers the last - * statement that was executed. Keeps statistics on how long it is in use. - *
- */ -public class PooledConnection extends ConnectionDelegator { - - private static final Logger logger = LoggerFactory.getLogger(PooledConnection.class); - - private static final String IDLE_CONNECTION_ACCESSED_ERROR = "Pooled Connection has been accessed whilst idle in the pool, via method: "; - - /** - * Marker for when connection is closed due to exceeding the max allowed age. - */ - private static final String REASON_MAXAGE = "maxAge"; - - /** - * Marker for when connection is closed due to exceeding the max inactive time. - */ - private static final String REASON_IDLE = "idleTime"; - - /** - * Marker for when the connection is closed due to a reset. - */ - private static final String REASON_RESET = "reset"; - - /** - * Set when connection is idle in the pool. In general when in the pool the - * connection should not be modified. - */ - private static final int STATUS_IDLE = 88; - - /** - * Set when connection given to client. - */ - private static final int STATUS_ACTIVE = 89; - - /** - * Set when commit() or rollback() called. - */ - private static final int STATUS_ENDED = 87; - - /** - * Name used to identify the PooledConnection for logging. - */ - private final String name; - - /** - * The pool this connection belongs to. - */ - private final DataSourcePool pool; - - /** - * The underlying connection. - */ - private final Connection connection; - - /** - * The time this connection was created. - */ - private final long creationTime; - - /** - * Cache of the PreparedStatements - */ - private final PstmtCache pstmtCache; - - private final Object pstmtMonitor = new Object(); - - /** - * Helper for statistics collection. - */ - private final PooledConnectionStatistics stats = new PooledConnectionStatistics(); - - /** - * The status of the connection. IDLE, ACTIVE or ENDED. - */ - private int status = STATUS_IDLE; - - /** - * The reason for a connection closing. - */ - private String closeReason; - - /** - * Set this to true if the connection will be busy for a long time. - *- * This means it should skip the suspected connection pool leak checking. - *
- */ - private boolean longRunning; - - /** - * Flag to indicate that this connection had errors and should be checked to - * make sure it is okay. - */ - private boolean hadErrors; - - /** - * The last start time. When the connection was given to a thread. - */ - private long startUseTime; - - /** - * The last end time of this connection. This is to calculate the usage - * time. - */ - private long lastUseTime; - - private long exeStartNanos; - - /** - * The last statement executed by this connection. - */ - private String lastStatement; - - /** - * The non avaje method that created the connection. - */ - private String createdByMethod; - - /** - * Used to find connection pool leaks. - */ - private StackTraceElement[] stackTrace; - - private int maxStackTrace; - - /** - * Slot position in the BusyConnectionBuffer. - */ - private int slotId; - - private boolean resetIsolationReadOnlyRequired; - - - /** - * Construct the connection that can refer back to the pool it belongs to. - *- * close() will return the connection back to the pool , while - * closeDestroy() will close() the underlining connection properly. - *
- */ - public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) throws SQLException { - super(connection); - - this.pool = pool; - this.connection = connection; - this.name = pool.getName() + "." + uniqueId; - this.pstmtCache = new PstmtCache(name, pool.getPstmtCacheSize()); - this.maxStackTrace = pool.getMaxStackTraceSize(); - this.creationTime = System.currentTimeMillis(); - this.lastUseTime = creationTime; - } - - /** - * For testing the pool without real connections. - */ - protected PooledConnection(String name) { - super(null); - this.name = name; - this.pool = null; - this.connection = null; - this.pstmtCache = null; - this.maxStackTrace = 0; - this.creationTime = System.currentTimeMillis(); - this.lastUseTime = creationTime; - } - - /** - * Return the slot position in the busy buffer. - */ - public int getSlotId() { - return slotId; - } - - /** - * Set the slot position in the busy buffer. - */ - public void setSlotId(int slotId) { - this.slotId = slotId; - } - - /** - * Return the DataSourcePool that this connection belongs to. - */ - public DataSourcePool getDataSourcePool() { - return pool; - } - - /** - * Return the time the connection was created. - */ - public long getCreationTime() { - return creationTime; - } - - /** - * Return a string to identify the connection. - */ - public String getName() { - return name; - } - - public String getNameSlot() { - return name+":"+slotId; - } - - public String toString() { - return getDescription(); - } - - public long getBusySeconds() { - return (System.currentTimeMillis() - startUseTime)/1000; - } - - public String getDescription() { - return "name["+name+"] slot["+slotId+"] startTime["+getStartUseTime()+"] busySeconds["+getBusySeconds()+"] createdBy["+getCreatedByMethod()+"] stmt["+getLastStatement()+"]"; - } - - public String getFullDescription() { - return "name["+name+"] slot["+slotId+"] startTime["+getStartUseTime()+"] busySeconds["+getBusySeconds()+"] stackTrace["+getStackTraceAsString()+"] stmt["+getLastStatement()+"]"; - } - - public String getPstmtStatistics() { - return "name["+name+"] startTime["+getStartUseTime()+"] "+pstmtCache.getDescription(); - } - - public PooledConnectionStatistics getStatistics() { - return stats; - } - - /** - * Return true if the connection should be treated as long running (skip connection pool leak check). - */ - public boolean isLongRunning() { - return longRunning; - } - - /** - * Set this to true if the connection is a long running connection and should skip the - * 'suspected connection pool leak' checking. - */ - public void setLongRunning(boolean longRunning) { - this.longRunning = longRunning; - } - - /** - * Close the connection fully NOT putting in back into the pool. - *- * The logErrors parameter exists so that expected errors are not logged - * such as when the database is known to be down. - *
- * - * @param logErrors - * if false then don't log errors when closing - */ - public void closeConnectionFully(boolean logErrors) { - - if (pool != null) { - // allow collection of load statistics - pool.reportClosingConnection(this); - } - - if (logger.isDebugEnabled()) { - logger.debug("Closing Connection[{}] slot[{}] reason[{}] stats: {} , pstmtStats: {} ", name, slotId, closeReason, stats.getValues(false), pstmtCache.getDescription()); - } - - try { - if (connection.isClosed()) { - // Typically the JDBC Driver has its own JVM shutdown hook and already - // closed the connections in our DataSource pool so making this DEBUG level - logger.debug("Closing Connection[{}] that is already closed?", name); - return; - } - } catch (SQLException ex) { - if (logErrors) { - logger.error("Error checking if connection [" + getNameSlot() + "] is closed", ex); - } - } - - try { - for (ExtendedPreparedStatement ps : pstmtCache.values()) { - ps.closeDestroy(); - } - - } catch (SQLException ex) { - if (logErrors) { - logger.warn("Error when closing connection Statements", ex); - } - } - - try { - connection.close(); - } catch (SQLException ex) { - if (logErrors || logger.isDebugEnabled()) { - logger.error("Error when fully closing connection [" + getNameSlot() + "]", ex); - } - } - } - - /** - * A Least Recently used cache of PreparedStatements. - */ - public PstmtCache getPstmtCache() { - return pstmtCache; - } - - /** - * Creates a wrapper ExtendedStatement so that I can get the executed sql. I - * want to do this so that I can get the slowest query statments etc, and - * log that information. - */ - public Statement createStatement() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); - } - try { - return connection.createStatement(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public Statement createStatement(int resultSetType, int resultSetConcurreny) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); - } - try { - return connection.createStatement(resultSetType, resultSetConcurreny); - - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - /** - * Return a PreparedStatement back into the cache. - */ - protected void returnPreparedStatement(ExtendedPreparedStatement pstmt) { - - synchronized (pstmtMonitor) { - if (!pstmtCache.returnStatement(pstmt)) { - try { - // Already an entry in the cache with the exact same SQL... - pstmt.closeDestroy(); - - } catch (SQLException e) { - logger.error("Error closing Pstmt", e); - } - } - } - } - - /** - * This will try to use a cache of PreparedStatements. - */ - public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException { - String cacheKey = sql + returnKeysFlag; - return prepareStatement(sql, true, returnKeysFlag, cacheKey); - } - - /** - * This will try to use a cache of PreparedStatements. - */ - public PreparedStatement prepareStatement(String sql) throws SQLException { - return prepareStatement(sql, false, 0, sql); - } - - /** - * This will try to use a cache of PreparedStatements. - */ - private PreparedStatement prepareStatement(String sql, boolean useFlag, int flag, String cacheKey) throws SQLException { - - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); - } - try { - synchronized (pstmtMonitor) { - lastStatement = sql; - - // try to get a matching cached PStmt from the cache. - ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey); - - if (pstmt != null) { - return pstmt; - } - - // create a new PreparedStatement - PreparedStatement actualPstmt; - if (useFlag) { - actualPstmt = connection.prepareStatement(sql, flag); - } else { - actualPstmt = connection.prepareStatement(sql); - } - return new ExtendedPreparedStatement(this, actualPstmt, sql, cacheKey); - } - - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurreny) throws SQLException { - - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); - } - try { - // no caching when creating PreparedStatements this way - lastStatement = sql; - return connection.prepareStatement(sql, resultSetType, resultSetConcurreny); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - /** - * Reset the connection for returning to the client. Resets the status, - * startUseTime and hadErrors. - */ - protected void resetForUse() { - this.status = STATUS_ACTIVE; - this.startUseTime = System.currentTimeMillis(); - this.exeStartNanos = System.nanoTime(); - this.createdByMethod = null; - this.lastStatement = null; - this.hadErrors = false; - this.longRunning = false; - } - - /** - * When an error occurs during use add it the connection. - *- * Any PooledConnection that has an error is checked to make sure it works - * before it is placed back into the connection pool. - *
- */ - public void addError(Throwable e) { - hadErrors = true; - } - - /** - * Returns true if the connect threw any errors during use. - *- * Connections with errors are testing to make sure they are still good - * before putting them back into the pool. - *
- */ - public boolean hadErrors() { - return hadErrors; - } - - /** - * close the connection putting it back into the connection pool. - *- * Note that to ensure that the next transaction starts at the correct time - * a commit() or rollback() should be called. If neither has occured at this - * time then a rollback() is used (to end the transaction). - *
- *- * To close the connection fully use closeConnectionFully(). - *
- */ - public void close() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()"); - } - - long durationNanos = System.nanoTime() - exeStartNanos; - stats.add(durationNanos, hadErrors); - - if (hadErrors) { - if (!pool.validateConnection(this)) { - // the connection is BAD, remove it, close it and test the pool - pool.returnConnectionForceClose(this); - return; - } - } - - try { - // reset the autoCommit back if client code changed it - if (connection.getAutoCommit() != pool.getAutoCommit()) { - connection.setAutoCommit(pool.getAutoCommit()); - } - // Generally resetting Isolation level seems expensive. - // Hence using resetIsolationReadOnlyRequired flag - // performance reasons. - if (resetIsolationReadOnlyRequired) { - resetIsolationReadOnly(); - resetIsolationReadOnlyRequired = false; - } - - // the connection is assumed GOOD so put it back in the pool - lastUseTime = System.currentTimeMillis(); - // connection.clearWarnings(); - status = STATUS_IDLE; - pool.returnConnection(this); - - } catch (Exception ex) { - // the connection is BAD, remove it, close it and test the pool - logger.warn("Error when trying to return connection to pool, closing fully.", ex); - pool.returnConnectionForceClose(this); - } - } - - private void resetIsolationReadOnly() throws SQLException { - // reset the transaction isolation if the client code changed it - if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) { - connection.setTransactionIsolation(pool.getTransactionIsolation()); - } - // reset readonly to false - if (connection.isReadOnly()) { - connection.setReadOnly(false); - } - } - - protected void finalize() throws Throwable { - try { - if (connection != null && !connection.isClosed()) { - // connect leak? - logger.warn("Closing Connection on finalize() - {}", getFullDescription()); - closeConnectionFully(false); - } - } catch (Exception e) { - logger.error("Error when finalize is closing a connection? (unexpected)", e); - } - super.finalize(); - } - - /** - * Return true if the connection is too old. - */ - public boolean exceedsMaxAge(long maxAgeMillis) { - if (maxAgeMillis > 0 && (creationTime < (System.currentTimeMillis() - maxAgeMillis))){ - this.closeReason = REASON_MAXAGE; - return true; - } - return false; - } - - public boolean shouldTrimOnReturn(long lastResetTime, long maxAgeMillis) { - if (creationTime <= lastResetTime) { - this.closeReason = REASON_RESET; - return true; - } - if (exceedsMaxAge(maxAgeMillis)) { - return true; - } - return false; - } - - /** - * Return true if the connection has been idle for too long or is too old. - */ - public boolean shouldTrim(long usedSince, long createdSince) { - if (lastUseTime < usedSince) { - // been idle for too long so trim it - this.closeReason = REASON_IDLE; - return true; - } - if (createdSince > 0 && createdSince > creationTime) { - // exceeds max age so trim it - this.closeReason = REASON_MAXAGE; - return true; - } - return false; - } - - /** - * Return the time the connection was passed to the client code. - *- * Used to detect busy connections that could be leaks. - *
- */ - public long getStartUseTime() { - return startUseTime; - } - - /** - * Returns the time the connection was last used. - *- * Used to close connections that have been idle for some time. Typically 5 - * minutes. - *
- */ - public long getLastUsedTime() { - return lastUseTime; - } - - /** - * Returns the last sql statement executed. - */ - public String getLastStatement() { - return lastStatement; - } - - /** - * Called by ExtendedStatement to trace the sql being executed. - *- * Note with addBatch() this will not really work. - *
- */ - protected void setLastStatement(String lastStatement) { - this.lastStatement = lastStatement; - if (logger.isTraceEnabled()) { - logger.trace(".setLastStatement[" + lastStatement + "]"); - } - } - - - /** - * Also note the read only status needs to be reset when put back into the - * pool. - */ - public void setReadOnly(boolean readOnly) throws SQLException { - // A bit loose not checking for STATUS_IDLE - // if (status == STATUS_IDLE) { - // throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + - // "setReadOnly()"); - // } - resetIsolationReadOnlyRequired = true; - connection.setReadOnly(readOnly); - } - - /** - * Also note the Isolation level needs to be reset when put back into the - * pool. - */ - public void setTransactionIsolation(int level) throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTransactionIsolation()"); - } - try { - resetIsolationReadOnlyRequired = true; - connection.setTransactionIsolation(level); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - // - // - // Simple wrapper methods which pass a method call onto the acutal - // connection object. These methods are safe-guarded to prevent use of - // the methods whilst the connection is in the connection pool. - // - // - public void clearWarnings() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); - } - connection.clearWarnings(); - } - - public void commit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); - } - try { - status = STATUS_ENDED; - connection.commit(); - } catch (SQLException ex) { - addError(ex); - throw ex; - } - } - - public boolean getAutoCommit() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); - } - return connection.getAutoCommit(); - } - - public String getCatalog() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); - } - return connection.getCatalog(); - } - - public DatabaseMetaData getMetaData() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); - } - return connection.getMetaData(); - } - - public int getTransactionIsolation() throws SQLException { - if (status == STATUS_IDLE) { - throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()"); - } - return connection.getTransactionIsolation(); - } - - public Map- * Used to help finding connection pool leaks. - *
- */ - public String getCreatedByMethod() { - if (createdByMethod != null) { - return createdByMethod; - } - if (stackTrace == null) { - return null; - } - - for (int j = 0; j < stackTrace.length; j++) { - String methodLine = stackTrace[j].toString(); - if (skipElement(methodLine)) { - // ignore these methods... - } else { - createdByMethod = methodLine; - return createdByMethod; - } - } - - return null; - } - - private boolean skipElement(String methodLine) { - if (methodLine.startsWith("java.lang.")) { - return true; - } else if (methodLine.startsWith("java.util.")) { - return true; - } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.+ * It is designed to be part of DataSourcePool. Closing the connection puts it + * back into the pool. + *
+ * + *+ * It defaults autoCommit and Transaction Isolation to the defaults of the + * DataSourcePool. + *
+ * + *+ * It has caching of Statements and PreparedStatements. Remembers the last + * statement that was executed. Keeps statistics on how long it is in use. + *
+ */ +public class PooledConnection extends ConnectionDelegator { + + private static final Logger logger = LoggerFactory.getLogger(PooledConnection.class); + + private static final String IDLE_CONNECTION_ACCESSED_ERROR = "Pooled Connection has been accessed whilst idle in the pool, via method: "; + + /** + * Marker for when connection is closed due to exceeding the max allowed age. + */ + private static final String REASON_MAXAGE = "maxAge"; + + /** + * Marker for when connection is closed due to exceeding the max inactive time. + */ + private static final String REASON_IDLE = "idleTime"; + + /** + * Marker for when the connection is closed due to a reset. + */ + private static final String REASON_RESET = "reset"; + + /** + * Set when connection is idle in the pool. In general when in the pool the + * connection should not be modified. + */ + private static final int STATUS_IDLE = 88; + + /** + * Set when connection given to client. + */ + private static final int STATUS_ACTIVE = 89; + + /** + * Set when commit() or rollback() called. + */ + private static final int STATUS_ENDED = 87; + + /** + * Name used to identify the PooledConnection for logging. + */ + private final String name; + + /** + * The pool this connection belongs to. + */ + private final DataSourcePool pool; + + /** + * The underlying connection. + */ + private final Connection connection; + + /** + * The time this connection was created. + */ + private final long creationTime; + + /** + * Cache of the PreparedStatements + */ + private final PstmtCache pstmtCache; + + private final Object pstmtMonitor = new Object(); + + /** + * Helper for statistics collection. + */ + private final PooledConnectionStatistics stats = new PooledConnectionStatistics(); + + /** + * The status of the connection. IDLE, ACTIVE or ENDED. + */ + private int status = STATUS_IDLE; + + /** + * The reason for a connection closing. + */ + private String closeReason; + + /** + * Set this to true if the connection will be busy for a long time. + *+ * This means it should skip the suspected connection pool leak checking. + *
+ */ + private boolean longRunning; + + /** + * Flag to indicate that this connection had errors and should be checked to + * make sure it is okay. + */ + private boolean hadErrors; + + /** + * The last start time. When the connection was given to a thread. + */ + private long startUseTime; + + /** + * The last end time of this connection. This is to calculate the usage + * time. + */ + private long lastUseTime; + + private long exeStartNanos; + + /** + * The last statement executed by this connection. + */ + private String lastStatement; + + /** + * The non avaje method that created the connection. + */ + private String createdByMethod; + + /** + * Used to find connection pool leaks. + */ + private StackTraceElement[] stackTrace; + + private int maxStackTrace; + + /** + * Slot position in the BusyConnectionBuffer. + */ + private int slotId; + + private boolean resetIsolationReadOnlyRequired; + + + /** + * Construct the connection that can refer back to the pool it belongs to. + *+ * close() will return the connection back to the pool , while + * closeDestroy() will close() the underlining connection properly. + *
+ */ + public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) throws SQLException { + super(connection); + + this.pool = pool; + this.connection = connection; + this.name = pool.getName() + "." + uniqueId; + this.pstmtCache = new PstmtCache(name, pool.getPstmtCacheSize()); + this.maxStackTrace = pool.getMaxStackTraceSize(); + this.creationTime = System.currentTimeMillis(); + this.lastUseTime = creationTime; + } + + /** + * For testing the pool without real connections. + */ + protected PooledConnection(String name) { + super(null); + this.name = name; + this.pool = null; + this.connection = null; + this.pstmtCache = null; + this.maxStackTrace = 0; + this.creationTime = System.currentTimeMillis(); + this.lastUseTime = creationTime; + } + + /** + * Return the slot position in the busy buffer. + */ + public int getSlotId() { + return slotId; + } + + /** + * Set the slot position in the busy buffer. + */ + public void setSlotId(int slotId) { + this.slotId = slotId; + } + + /** + * Return the DataSourcePool that this connection belongs to. + */ + public DataSourcePool getDataSourcePool() { + return pool; + } + + /** + * Return the time the connection was created. + */ + public long getCreationTime() { + return creationTime; + } + + /** + * Return a string to identify the connection. + */ + public String getName() { + return name; + } + + public String getNameSlot() { + return name+":"+slotId; + } + + public String toString() { + return getDescription(); + } + + public long getBusySeconds() { + return (System.currentTimeMillis() - startUseTime)/1000; + } + + public String getDescription() { + return "name["+name+"] slot["+slotId+"] startTime["+getStartUseTime()+"] busySeconds["+getBusySeconds()+"] createdBy["+getCreatedByMethod()+"] stmt["+getLastStatement()+"]"; + } + + public String getFullDescription() { + return "name["+name+"] slot["+slotId+"] startTime["+getStartUseTime()+"] busySeconds["+getBusySeconds()+"] stackTrace["+getStackTraceAsString()+"] stmt["+getLastStatement()+"]"; + } + + public String getPstmtStatistics() { + return "name["+name+"] startTime["+getStartUseTime()+"] "+pstmtCache.getDescription(); + } + + public PooledConnectionStatistics getStatistics() { + return stats; + } + + /** + * Return true if the connection should be treated as long running (skip connection pool leak check). + */ + public boolean isLongRunning() { + return longRunning; + } + + /** + * Set this to true if the connection is a long running connection and should skip the + * 'suspected connection pool leak' checking. + */ + public void setLongRunning(boolean longRunning) { + this.longRunning = longRunning; + } + + /** + * Close the connection fully NOT putting in back into the pool. + *+ * The logErrors parameter exists so that expected errors are not logged + * such as when the database is known to be down. + *
+ * + * @param logErrors + * if false then don't log errors when closing + */ + public void closeConnectionFully(boolean logErrors) { + + if (pool != null) { + // allow collection of load statistics + pool.reportClosingConnection(this); + } + + if (logger.isDebugEnabled()) { + logger.debug("Closing Connection[{}] slot[{}] reason[{}] stats: {} , pstmtStats: {} ", name, slotId, closeReason, stats.getValues(false), pstmtCache.getDescription()); + } + + try { + if (connection.isClosed()) { + // Typically the JDBC Driver has its own JVM shutdown hook and already + // closed the connections in our DataSource pool so making this DEBUG level + logger.debug("Closing Connection[{}] that is already closed?", name); + return; + } + } catch (SQLException ex) { + if (logErrors) { + logger.error("Error checking if connection [" + getNameSlot() + "] is closed", ex); + } + } + + try { + for (ExtendedPreparedStatement ps : pstmtCache.values()) { + ps.closeDestroy(); + } + + } catch (SQLException ex) { + if (logErrors) { + logger.warn("Error when closing connection Statements", ex); + } + } + + try { + connection.close(); + } catch (SQLException ex) { + if (logErrors || logger.isDebugEnabled()) { + logger.error("Error when fully closing connection [" + getNameSlot() + "]", ex); + } + } + } + + /** + * A Least Recently used cache of PreparedStatements. + */ + public PstmtCache getPstmtCache() { + return pstmtCache; + } + + /** + * Creates a wrapper ExtendedStatement so that I can get the executed sql. I + * want to do this so that I can get the slowest query statments etc, and + * log that information. + */ + public Statement createStatement() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); + } + try { + return connection.createStatement(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public Statement createStatement(int resultSetType, int resultSetConcurreny) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "createStatement()"); + } + try { + return connection.createStatement(resultSetType, resultSetConcurreny); + + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + /** + * Return a PreparedStatement back into the cache. + */ + protected void returnPreparedStatement(ExtendedPreparedStatement pstmt) { + + synchronized (pstmtMonitor) { + if (!pstmtCache.returnStatement(pstmt)) { + try { + // Already an entry in the cache with the exact same SQL... + pstmt.closeDestroy(); + + } catch (SQLException e) { + logger.error("Error closing Pstmt", e); + } + } + } + } + + /** + * This will try to use a cache of PreparedStatements. + */ + public PreparedStatement prepareStatement(String sql, int returnKeysFlag) throws SQLException { + String cacheKey = sql + returnKeysFlag; + return prepareStatement(sql, true, returnKeysFlag, cacheKey); + } + + /** + * This will try to use a cache of PreparedStatements. + */ + public PreparedStatement prepareStatement(String sql) throws SQLException { + return prepareStatement(sql, false, 0, sql); + } + + /** + * This will try to use a cache of PreparedStatements. + */ + private PreparedStatement prepareStatement(String sql, boolean useFlag, int flag, String cacheKey) throws SQLException { + + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); + } + try { + synchronized (pstmtMonitor) { + lastStatement = sql; + + // try to get a matching cached PStmt from the cache. + ExtendedPreparedStatement pstmt = pstmtCache.remove(cacheKey); + + if (pstmt != null) { + return pstmt; + } + + // create a new PreparedStatement + PreparedStatement actualPstmt; + if (useFlag) { + actualPstmt = connection.prepareStatement(sql, flag); + } else { + actualPstmt = connection.prepareStatement(sql); + } + return new ExtendedPreparedStatement(this, actualPstmt, sql, cacheKey); + } + + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurreny) throws SQLException { + + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareStatement()"); + } + try { + // no caching when creating PreparedStatements this way + lastStatement = sql; + return connection.prepareStatement(sql, resultSetType, resultSetConcurreny); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + /** + * Reset the connection for returning to the client. Resets the status, + * startUseTime and hadErrors. + */ + protected void resetForUse() { + this.status = STATUS_ACTIVE; + this.startUseTime = System.currentTimeMillis(); + this.exeStartNanos = System.nanoTime(); + this.createdByMethod = null; + this.lastStatement = null; + this.hadErrors = false; + this.longRunning = false; + } + + /** + * When an error occurs during use add it the connection. + *+ * Any PooledConnection that has an error is checked to make sure it works + * before it is placed back into the connection pool. + *
+ */ + public void addError(Throwable e) { + hadErrors = true; + } + + /** + * Returns true if the connect threw any errors during use. + *+ * Connections with errors are testing to make sure they are still good + * before putting them back into the pool. + *
+ */ + public boolean hadErrors() { + return hadErrors; + } + + /** + * close the connection putting it back into the connection pool. + *+ * Note that to ensure that the next transaction starts at the correct time + * a commit() or rollback() should be called. If neither has occured at this + * time then a rollback() is used (to end the transaction). + *
+ *+ * To close the connection fully use closeConnectionFully(). + *
+ */ + public void close() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "close()"); + } + + long durationNanos = System.nanoTime() - exeStartNanos; + stats.add(durationNanos, hadErrors); + + if (hadErrors) { + if (!pool.validateConnection(this)) { + // the connection is BAD, remove it, close it and test the pool + pool.returnConnectionForceClose(this); + return; + } + } + + try { + // reset the autoCommit back if client code changed it + if (connection.getAutoCommit() != pool.getAutoCommit()) { + connection.setAutoCommit(pool.getAutoCommit()); + } + // Generally resetting Isolation level seems expensive. + // Hence using resetIsolationReadOnlyRequired flag + // performance reasons. + if (resetIsolationReadOnlyRequired) { + resetIsolationReadOnly(); + resetIsolationReadOnlyRequired = false; + } + + // the connection is assumed GOOD so put it back in the pool + lastUseTime = System.currentTimeMillis(); + // connection.clearWarnings(); + status = STATUS_IDLE; + pool.returnConnection(this); + + } catch (Exception ex) { + // the connection is BAD, remove it, close it and test the pool + logger.warn("Error when trying to return connection to pool, closing fully.", ex); + pool.returnConnectionForceClose(this); + } + } + + private void resetIsolationReadOnly() throws SQLException { + // reset the transaction isolation if the client code changed it + if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) { + connection.setTransactionIsolation(pool.getTransactionIsolation()); + } + // reset readonly to false + if (connection.isReadOnly()) { + connection.setReadOnly(false); + } + } + + protected void finalize() throws Throwable { + try { + if (connection != null && !connection.isClosed()) { + // connect leak? + logger.warn("Closing Connection on finalize() - {}", getFullDescription()); + closeConnectionFully(false); + } + } catch (Exception e) { + logger.error("Error when finalize is closing a connection? (unexpected)", e); + } + super.finalize(); + } + + /** + * Return true if the connection is too old. + */ + public boolean exceedsMaxAge(long maxAgeMillis) { + if (maxAgeMillis > 0 && (creationTime < (System.currentTimeMillis() - maxAgeMillis))){ + this.closeReason = REASON_MAXAGE; + return true; + } + return false; + } + + public boolean shouldTrimOnReturn(long lastResetTime, long maxAgeMillis) { + if (creationTime <= lastResetTime) { + this.closeReason = REASON_RESET; + return true; + } + if (exceedsMaxAge(maxAgeMillis)) { + return true; + } + return false; + } + + /** + * Return true if the connection has been idle for too long or is too old. + */ + public boolean shouldTrim(long usedSince, long createdSince) { + if (lastUseTime < usedSince) { + // been idle for too long so trim it + this.closeReason = REASON_IDLE; + return true; + } + if (createdSince > 0 && createdSince > creationTime) { + // exceeds max age so trim it + this.closeReason = REASON_MAXAGE; + return true; + } + return false; + } + + /** + * Return the time the connection was passed to the client code. + *+ * Used to detect busy connections that could be leaks. + *
+ */ + public long getStartUseTime() { + return startUseTime; + } + + /** + * Returns the time the connection was last used. + *+ * Used to close connections that have been idle for some time. Typically 5 + * minutes. + *
+ */ + public long getLastUsedTime() { + return lastUseTime; + } + + /** + * Returns the last sql statement executed. + */ + public String getLastStatement() { + return lastStatement; + } + + /** + * Called by ExtendedStatement to trace the sql being executed. + *+ * Note with addBatch() this will not really work. + *
+ */ + protected void setLastStatement(String lastStatement) { + this.lastStatement = lastStatement; + if (logger.isTraceEnabled()) { + logger.trace(".setLastStatement[" + lastStatement + "]"); + } + } + + + /** + * Also note the read only status needs to be reset when put back into the + * pool. + */ + public void setReadOnly(boolean readOnly) throws SQLException { + // A bit loose not checking for STATUS_IDLE + // if (status == STATUS_IDLE) { + // throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + + // "setReadOnly()"); + // } + resetIsolationReadOnlyRequired = true; + connection.setReadOnly(readOnly); + } + + /** + * Also note the Isolation level needs to be reset when put back into the + * pool. + */ + public void setTransactionIsolation(int level) throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTransactionIsolation()"); + } + try { + resetIsolationReadOnlyRequired = true; + connection.setTransactionIsolation(level); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + // + // + // Simple wrapper methods which pass a method call onto the acutal + // connection object. These methods are safe-guarded to prevent use of + // the methods whilst the connection is in the connection pool. + // + // + public void clearWarnings() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "clearWarnings()"); + } + connection.clearWarnings(); + } + + public void commit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "commit()"); + } + try { + status = STATUS_ENDED; + connection.commit(); + } catch (SQLException ex) { + addError(ex); + throw ex; + } + } + + public boolean getAutoCommit() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getAutoCommit()"); + } + return connection.getAutoCommit(); + } + + public String getCatalog() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getCatalog()"); + } + return connection.getCatalog(); + } + + public DatabaseMetaData getMetaData() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getMetaData()"); + } + return connection.getMetaData(); + } + + public int getTransactionIsolation() throws SQLException { + if (status == STATUS_IDLE) { + throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTransactionIsolation()"); + } + return connection.getTransactionIsolation(); + } + + public Map+ * Used to help finding connection pool leaks. + *
+ */ + public String getCreatedByMethod() { + if (createdByMethod != null) { + return createdByMethod; + } + if (stackTrace == null) { + return null; + } + + for (int j = 0; j < stackTrace.length; j++) { + String methodLine = stackTrace[j].toString(); + if (skipElement(methodLine)) { + // ignore these methods... + } else { + createdByMethod = methodLine; + return createdByMethod; + } + } + + return null; + } + + private boolean skipElement(String methodLine) { + if (methodLine.startsWith("java.lang.")) { + return true; + } else if (methodLine.startsWith("java.util.")) { + return true; + } else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.CallableQuery.- * This is typically done when a database down event occurs. - *
- */ - public void reset(long leakTimeMinutes) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - Status status = createStatus(); - logger.info("Reseting DataSourcePool [{}] {}", name, status); - lastResetTime = System.currentTimeMillis(); - - closeFreeConnections(false); - closeBusyConnections(leakTimeMinutes); - - logger.info("Busy Connections:\n" + getBusyConnectionInformation()); - - } finally { - lock.unlock(); - } - } - - public void trim(long maxInactiveMillis, long maxAgeMillis) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis) > 0) { - try { - ensureMinimumConnections(); - } catch (SQLException e) { - logger.error("Error trying to ensure minimum connections", e); - } - } - } finally { - lock.unlock(); - } - } - - /** - * Trim connections that have been not used for some time. - */ - private int trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) { - - long usedSince = System.currentTimeMillis() - maxInactiveMillis; - long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis; - - int trimedCount = freeList.trim(usedSince, createdSince); - if (trimedCount > 0) { - logger.debug("DataSourcePool [{}] trimmed [{}] inactive connections. New size[{}]", name, trimedCount, totalConnections()); - } - return trimedCount; - } - - /** - * Close all the connections that are in the free list. - */ - public void closeFreeConnections(boolean logErrors) { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - freeList.closeAll(logErrors); - } finally { - lock.unlock(); - } - } - - /** - * Close any busy connections that have not been used for some time. - *- * These connections are considered to have leaked from the connection pool. - *
- *- * Connection leaks occur when code doesn't ensure that connections are - * closed() after they have been finished with. There should be an - * appropriate try catch finally block to ensure connections are always - * closed and put back into the pool. - *
- */ - public void closeBusyConnections(long leakTimeMinutes) { - - final ReentrantLock lock = this.lock; - lock.lock(); - try { - busyList.closeBusyConnections(leakTimeMinutes); - } finally { - lock.unlock(); - } - } - - /** - * As the pool grows it gets closer to the maxConnections limit. We can send - * an Alert (or warning) as we get close to this limit and hence an - * Administrator could increase the pool size if desired. - *- * This is called whenever the pool grows in size (towards the max limit). - *
- */ - private void checkForWarningSize() { - - // the the total number of connections that we can add - // to the pool before it hits the maximum - int availableGrowth = (maxSize - totalConnections()); - - if (availableGrowth < warningSize) { - - closeBusyConnections(leakTimeMinutes); - - String msg = "DataSourcePool [" + name + "] is [" + availableGrowth+ "] connections from its maximum size."; - pool.notifyWarning(msg); - } - } - - public String getBusyConnectionInformation() { - return getBusyConnectionInformation(false); - } - - public void dumpBusyConnectionInformation() { - getBusyConnectionInformation(true); - } - - /** - * Returns information describing connections that are currently being used. - */ - private String getBusyConnectionInformation(boolean toLogger) { - - final ReentrantLock lock = this.lock; - lock.lock(); - try { - - return busyList.getBusyConnectionInformation(toLogger); - - } finally { - lock.unlock(); - } - } - -} - +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.SQLException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool.Status; +import com.avaje.ebeaninternal.server.lib.sql.PooledConnectionStatistics.LoadValues; + +public class PooledConnectionQueue { + + private static final Logger logger = LoggerFactory.getLogger(PooledConnectionQueue.class); + + private static final TimeUnit MILLIS_TIME_UNIT = TimeUnit.MILLISECONDS; + + private final String name; + + private final DataSourcePool pool; + + /** + * A 'circular' buffer designed specifically for free connections. + */ + private final FreeConnectionBuffer freeList; + + /** + * A 'slots' buffer designed specifically for busy connections. + * Fast add remove based on slot id. + */ + private final BusyConnectionBuffer busyList; + + /** + * Load statistics collected off connections that have closed fully (left the pool). + */ + private final PooledConnectionStatistics collectedStats = new PooledConnectionStatistics(); + + /** + * Currently accumulated load statistics. + */ + private LoadValues accumulatedValues = new LoadValues(); + + /** + * Main lock guarding all access + */ + private final ReentrantLock lock; + + /** + * Condition for threads waiting to take a connection + */ + private final Condition notEmpty; + + private int connectionId; + + private final long waitTimeoutMillis; + + private final long leakTimeMinutes; + + private final long maxAgeMillis; + + private int warningSize; + + private int maxSize; + + private int minSize; + + /** + * Number of threads in the wait queue. + */ + private int waitingThreads; + + /** + * Number of times a thread had to wait. + */ + private int waitCount; + + /** + * Number of times a connection was got from this queue. + */ + private int hitCount; + + /** + * The high water mark for the queue size. + */ + private int highWaterMark; + + /** + * Last time the pool was reset. Used to close busy connections as they are + * returned to the pool that where created prior to the lastResetTime. + */ + private long lastResetTime; + + private boolean doingShutdown; + + public PooledConnectionQueue(DataSourcePool pool) { + + this.pool = pool; + this.name = pool.getName(); + this.minSize = pool.getMinSize(); + this.maxSize = pool.getMaxSize(); + + this.warningSize = pool.getWarningSize(); + this.waitTimeoutMillis = pool.getWaitTimeoutMillis(); + this.leakTimeMinutes = pool.getLeakTimeMinutes(); + this.maxAgeMillis = pool.getMaxAgeMillis(); + + this.busyList = new BusyConnectionBuffer(maxSize, 20); + this.freeList = new FreeConnectionBuffer(); + + this.lock = new ReentrantLock(false); + this.notEmpty = lock.newCondition(); + } + + private Status createStatus() { + return new Status(name, minSize, maxSize, freeList.size(), busyList.size(), waitingThreads, highWaterMark, waitCount, hitCount); + } + + public String toString() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return createStatus().toString(); + } finally { + lock.unlock(); + } + } + + /** + * Collect statistics of a connection that is fully closing + */ + protected void reportClosingConnection(PooledConnection pooledConnection) { + + collectedStats.add(pooledConnection.getStatistics()); + } + + public DataSourcePoolStatistics getStatistics(boolean reset) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + + LoadValues aggregate = collectedStats.getValues(reset); + + freeList.collectStatistics(aggregate, reset); + busyList.collectStatistics(aggregate, reset); + + aggregate.plus(accumulatedValues); + + this.accumulatedValues = (reset) ? new LoadValues() : aggregate; + + return new DataSourcePoolStatistics(aggregate.getCollectionStart(), aggregate.getCount(), aggregate.getErrorCount(), aggregate.getHwmMicros(), aggregate.getTotalMicros()); + + } finally { + lock.unlock(); + } + } + + public Status getStatus(boolean reset) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Status s = createStatus(); + if (reset){ + highWaterMark = busyList.size(); + hitCount = 0; + waitCount = 0; + } + return s; + } finally { + lock.unlock(); + } + } + + public void setMinSize(int minSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (minSize > this.maxSize){ + throw new IllegalArgumentException("minSize "+minSize+" > maxSize "+this.maxSize); + } + this.minSize = minSize; + } finally { + lock.unlock(); + } + } + + public void setMaxSize(int maxSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (maxSize < this.minSize){ + throw new IllegalArgumentException("maxSize "+maxSize+" < minSize "+this.minSize); + } + this.busyList.setCapacity(maxSize); + this.maxSize = maxSize; + } finally { + lock.unlock(); + } + } + + public void setWarningSize(int warningSize) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (warningSize > this.maxSize){ + throw new IllegalArgumentException("warningSize "+warningSize+" > maxSize "+this.maxSize); + } + this.warningSize = warningSize; + } finally { + lock.unlock(); + } + } + + private int totalConnections() { + return freeList.size() + busyList.size(); + } + + public void ensureMinimumConnections() throws SQLException { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int add = minSize - totalConnections(); + if (add > 0){ + for (int i = 0; i < add; i++) { + PooledConnection c = pool.createConnectionForQueue(connectionId++); + freeList.add(c); + } + notEmpty.signal(); + } + + } finally { + lock.unlock(); + } + } + + /** + * Return a PooledConnection. + */ + protected void returnPooledConnection(PooledConnection c, boolean forceClose) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (!busyList.remove(c)) { + logger.error("Connection [{}] not found in BusyList? ", c); + } + if (forceClose || c.shouldTrimOnReturn(lastResetTime, maxAgeMillis)) { + c.closeConnectionFully(false); + + } else { + freeList.add(c); + notEmpty.signal(); + } + } finally { + lock.unlock(); + } + } + + private PooledConnection extractFromFreeList() { + PooledConnection c = freeList.remove(); + registerBusyConnection(c); + return c; + } + + public PooledConnection getPooledConnection() throws SQLException { + + try { + PooledConnection pc = _getPooledConnection(); + pc.resetForUse(); + return pc; + + } catch (InterruptedException e) { + String msg = "Interrupted getting connection from pool "+e; + throw new SQLException(msg); + } + } + + /** + * Register the PooledConnection with the busyList. + */ + private int registerBusyConnection(PooledConnection c) { + int busySize = busyList.add(c); + if (busySize > highWaterMark){ + highWaterMark = busySize; + } + return busySize; + } + + private PooledConnection _getPooledConnection() throws InterruptedException, SQLException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + if (doingShutdown) { + throw new SQLException("Trying to access the Connection Pool when it is shutting down"); + } + + // this includes attempts that fail with InterruptedException + // or SQLException but that is ok as its only an indicator + hitCount++; + + // are other threads already waiting? (they get priority) + if (waitingThreads == 0){ + + if (!freeList.isEmpty()){ + // we have a free connection to return + return extractFromFreeList(); + } + + if (busyList.size() < maxSize){ + // grow the connection pool + PooledConnection c = pool.createConnectionForQueue(connectionId++); + int busySize = registerBusyConnection(c); + + if (logger.isDebugEnabled()) { + logger.debug("DataSourcePool [{}] grow; id[{}] busy[{}] max[{}]", name, c.getName(), busySize, maxSize); + } + checkForWarningSize(); + return c; + } + } + + try { + // The pool is at maximum size. We are going to go into + // a wait loop until connections are returned into the pool. + waitCount++; + waitingThreads++; + return _getPooledConnectionWaitLoop(); + } finally { + waitingThreads--; + } + + } finally { + lock.unlock(); + } + } + + /** + * Got into a loop waiting for connections to be returned to the pool. + */ + private PooledConnection _getPooledConnectionWaitLoop() throws SQLException, InterruptedException { + + long nanos = MILLIS_TIME_UNIT.toNanos(waitTimeoutMillis); + for (;;) { + + if (nanos <= 0) { + String msg = "Unsuccessfully waited ["+waitTimeoutMillis+"] millis for a connection to be returned." + + " No connections are free. You need to Increase the max connections of ["+maxSize+"]" + + " or look for a connection pool leak using datasource.xxx.capturestacktrace=true"; + if (pool.isCaptureStackTrace()) { + dumpBusyConnectionInformation(); + } + + throw new SQLException(msg); + } + + try { + nanos = notEmpty.awaitNanos(nanos); + if (!freeList.isEmpty()) { + // successfully waited + return extractFromFreeList(); + } + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + } + } + + public void shutdown() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + doingShutdown = true; + Status status = createStatus(); + DataSourcePoolStatistics statistics = pool.getStatistics(false); + logger.debug("DataSourcePool [{}] shutdown {} - Statistics {}", name, status, statistics); + + closeFreeConnections(true); + + if (!busyList.isEmpty()) { + logger.warn("Closing busy connections on shutdown size: "+ busyList.size()); + dumpBusyConnectionInformation(); + closeBusyConnections(0); + } + } finally { + lock.unlock(); + } + } + + /** + * Close all the connections in the pool and any current busy connections + * when they are returned. New connections will be then created on demand. + *+ * This is typically done when a database down event occurs. + *
+ */ + public void reset(long leakTimeMinutes) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Status status = createStatus(); + logger.info("Reseting DataSourcePool [{}] {}", name, status); + lastResetTime = System.currentTimeMillis(); + + closeFreeConnections(false); + closeBusyConnections(leakTimeMinutes); + + logger.info("Busy Connections:\n" + getBusyConnectionInformation()); + + } finally { + lock.unlock(); + } + } + + public void trim(long maxInactiveMillis, long maxAgeMillis) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (trimInactiveConnections(maxInactiveMillis, maxAgeMillis) > 0) { + try { + ensureMinimumConnections(); + } catch (SQLException e) { + logger.error("Error trying to ensure minimum connections", e); + } + } + } finally { + lock.unlock(); + } + } + + /** + * Trim connections that have been not used for some time. + */ + private int trimInactiveConnections(long maxInactiveMillis, long maxAgeMillis) { + + long usedSince = System.currentTimeMillis() - maxInactiveMillis; + long createdSince = (maxAgeMillis == 0) ? 0 : System.currentTimeMillis() - maxAgeMillis; + + int trimedCount = freeList.trim(usedSince, createdSince); + if (trimedCount > 0) { + logger.debug("DataSourcePool [{}] trimmed [{}] inactive connections. New size[{}]", name, trimedCount, totalConnections()); + } + return trimedCount; + } + + /** + * Close all the connections that are in the free list. + */ + public void closeFreeConnections(boolean logErrors) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + freeList.closeAll(logErrors); + } finally { + lock.unlock(); + } + } + + /** + * Close any busy connections that have not been used for some time. + *+ * These connections are considered to have leaked from the connection pool. + *
+ *+ * Connection leaks occur when code doesn't ensure that connections are + * closed() after they have been finished with. There should be an + * appropriate try catch finally block to ensure connections are always + * closed and put back into the pool. + *
+ */ + public void closeBusyConnections(long leakTimeMinutes) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + busyList.closeBusyConnections(leakTimeMinutes); + } finally { + lock.unlock(); + } + } + + /** + * As the pool grows it gets closer to the maxConnections limit. We can send + * an Alert (or warning) as we get close to this limit and hence an + * Administrator could increase the pool size if desired. + *+ * This is called whenever the pool grows in size (towards the max limit). + *
+ */ + private void checkForWarningSize() { + + // the the total number of connections that we can add + // to the pool before it hits the maximum + int availableGrowth = (maxSize - totalConnections()); + + if (availableGrowth < warningSize) { + + closeBusyConnections(leakTimeMinutes); + + String msg = "DataSourcePool [" + name + "] is [" + availableGrowth+ "] connections from its maximum size."; + pool.notifyWarning(msg); + } + } + + public String getBusyConnectionInformation() { + return getBusyConnectionInformation(false); + } + + public void dumpBusyConnectionInformation() { + getBusyConnectionInformation(true); + } + + /** + * Returns information describing connections that are currently being used. + */ + private String getBusyConnectionInformation(boolean toLogger) { + + final ReentrantLock lock = this.lock; + lock.lock(); + try { + + return busyList.getBusyConnectionInformation(toLogger); + + } finally { + lock.unlock(); + } + } + +} + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java index 51676ae2e..4e58e8f1b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/Prefix.java @@ -1,100 +1,100 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Random; - -/** - * Security mechanisim. - */ -public class Prefix { - - private static final Logger logger = LoggerFactory.getLogger(Prefix.class); - - private static final int[] oa = { 50, 12, 4, 6, 8, 10, 7, 23, 45, 23, 6, 9, 12, 2, 8, 34 }; - - public static String getProp(String prop) { - String v = dec(prop); - int p = v.indexOf(":"); - String r = v.substring(1, p); - return r; - } - - public static void main(String[] args) { - String m = e(args[0]); - logger.info("[" + m + "]"); - String o = getProp(m); - logger.info("[" + o + "]"); - } - - public static String e(String msg) { - msg = elen(msg, 40); - return enc(msg); - } - - public static byte az(byte c, int offset) { - - int z = c + offset; - if (z > 122) { - // dp("z> "+z); - z = z - 122 + 48 - 1; - } - // dp("z="+z+" c:"+(int)c); - return (byte) z; - } - - public static byte bz(byte c, int offset) { - int z = c - offset; - if (z < (48)) { - // dp("z< "+z); - z = z + 122 - 48 + 1; - } - return (byte) z; - } - - public static String enc(String msg) { - byte[] msgbytes = msg.getBytes(); - byte[] encbytes = new byte[msgbytes.length + 1]; - Random r = new Random(); - int key = r.nextInt(70); - - char k = (char) (key + 48); - - encbytes[0] = az((byte) k, oa[0]); - // dp("key:"+key+" encbytes[0]:"+(byte)encbytes[0]); - int ios = key; - for (int i = 1; i < (msgbytes.length + 1); i++) { - encbytes[i] = az(msgbytes[i - 1], (oa[(i + ios) % oa.length])); - } - return new String(encbytes); - } - - public static String dec(String msg) { - byte[] msgbytes = msg.getBytes(); - byte[] encbytes = new byte[msgbytes.length]; - - encbytes[0] = bz(msgbytes[0], oa[0]); - byte key = encbytes[0]; - int ios = (key - 48); - for (int i = 1; i < msgbytes.length; i++) { - encbytes[i] = bz(msgbytes[i], oa[(i + ios) % oa.length]); - } - return new String(encbytes); - } - - public static String elen(String msg, int len) { - Random r = new Random(); - if (msg.length() < len) { - int max = len - msg.length(); - StringBuilder sb = new StringBuilder(); - sb.append(msg).append(":"); - for (int i = 1; i < max; i++) { - int bc = r.nextInt(122 - 48); - sb.append(Character.toString((char) (bc + 48))); - } - return sb.toString(); - } - return msg; - } -} +package com.avaje.ebeaninternal.server.lib.sql; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Random; + +/** + * Security mechanisim. + */ +public class Prefix { + + private static final Logger logger = LoggerFactory.getLogger(Prefix.class); + + private static final int[] oa = { 50, 12, 4, 6, 8, 10, 7, 23, 45, 23, 6, 9, 12, 2, 8, 34 }; + + public static String getProp(String prop) { + String v = dec(prop); + int p = v.indexOf(":"); + String r = v.substring(1, p); + return r; + } + + public static void main(String[] args) { + String m = e(args[0]); + logger.info("[" + m + "]"); + String o = getProp(m); + logger.info("[" + o + "]"); + } + + public static String e(String msg) { + msg = elen(msg, 40); + return enc(msg); + } + + public static byte az(byte c, int offset) { + + int z = c + offset; + if (z > 122) { + // dp("z> "+z); + z = z - 122 + 48 - 1; + } + // dp("z="+z+" c:"+(int)c); + return (byte) z; + } + + public static byte bz(byte c, int offset) { + int z = c - offset; + if (z < (48)) { + // dp("z< "+z); + z = z + 122 - 48 + 1; + } + return (byte) z; + } + + public static String enc(String msg) { + byte[] msgbytes = msg.getBytes(); + byte[] encbytes = new byte[msgbytes.length + 1]; + Random r = new Random(); + int key = r.nextInt(70); + + char k = (char) (key + 48); + + encbytes[0] = az((byte) k, oa[0]); + // dp("key:"+key+" encbytes[0]:"+(byte)encbytes[0]); + int ios = key; + for (int i = 1; i < (msgbytes.length + 1); i++) { + encbytes[i] = az(msgbytes[i - 1], (oa[(i + ios) % oa.length])); + } + return new String(encbytes); + } + + public static String dec(String msg) { + byte[] msgbytes = msg.getBytes(); + byte[] encbytes = new byte[msgbytes.length]; + + encbytes[0] = bz(msgbytes[0], oa[0]); + byte key = encbytes[0]; + int ios = (key - 48); + for (int i = 1; i < msgbytes.length; i++) { + encbytes[i] = bz(msgbytes[i], oa[(i + ios) % oa.length]); + } + return new String(encbytes); + } + + public static String elen(String msg, int len) { + Random r = new Random(); + if (msg.length() < len) { + int max = len - msg.length(); + StringBuilder sb = new StringBuilder(); + sb.append(msg).append(":"); + for (int i = 1; i < max; i++) { + int bc = r.nextInt(122 - 48); + sb.append(Character.toString((char) (bc + 48))); + } + return sb.toString(); + } + return msg; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java index f28bfe46d..2d6adc436 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/sql/PstmtCache.java @@ -1,183 +1,183 @@ -package com.avaje.ebeaninternal.server.lib.sql; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.SQLException; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * A LRU based cache for PreparedStatements. - */ -public class PstmtCache extends LinkedHashMapReturned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED, - * REPEATABLE_READ or SERIALIZABLE.
- * - * @param level the transaction isolation level as per java.sql.Connection - * @return the level description as a string. - */ - public static String getLevelDescription(int level) { - switch (level) { - case Connection.TRANSACTION_NONE : - return "NONE"; - case Connection.TRANSACTION_READ_COMMITTED : - return "READ_COMMITTED"; - case Connection.TRANSACTION_READ_UNCOMMITTED : - return "READ_UNCOMMITTED"; - case Connection.TRANSACTION_REPEATABLE_READ : - return "REPEATABLE_READ"; - case Connection.TRANSACTION_SERIALIZABLE : - return "SERIALIZABLE"; - case -1 : - return "NotSet"; - default : - throw new RuntimeException("Transaction Isolaction level [" + level + "] is not defined."); - } - } - - - -} +package com.avaje.ebeaninternal.server.lib.sql; + +import java.sql.Connection; + +/** + * Helper object that can convert between transaction isolation descriptions and values. + * + */ +public class TransactionIsolation { + + + /** + * return the isolation level for a given string description. + */ + public static int getLevel(String level) { + level = level.toUpperCase(); + if (level.startsWith("TRANSACTION")){ + level = level.substring("TRANSACTION".length()); + } + level = level.replace("_", ""); + if ("NONE".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_NONE; + } + if ("READCOMMITTED".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_READ_COMMITTED; + } + if ("READUNCOMMITTED".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_READ_UNCOMMITTED; + } + if ("REPEATABLEREAD".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_REPEATABLE_READ; + } + if ("SERIALIZABLE".equalsIgnoreCase(level)){ + return Connection.TRANSACTION_SERIALIZABLE; + } + + throw new RuntimeException("Transaction Isolaction level [" + level + "] is not known."); + } + + /** + * Return the string description of the transaction isolation level specified. + *Returned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED, + * REPEATABLE_READ or SERIALIZABLE.
+ * + * @param level the transaction isolation level as per java.sql.Connection + * @return the level description as a string. + */ + public static String getLevelDescription(int level) { + switch (level) { + case Connection.TRANSACTION_NONE : + return "NONE"; + case Connection.TRANSACTION_READ_COMMITTED : + return "READ_COMMITTED"; + case Connection.TRANSACTION_READ_UNCOMMITTED : + return "READ_UNCOMMITTED"; + case Connection.TRANSACTION_REPEATABLE_READ : + return "REPEATABLE_READ"; + case Connection.TRANSACTION_SERIALIZABLE : + return "SERIALIZABLE"; + case -1 : + return "NotSet"; + default : + throw new RuntimeException("Transaction Isolaction level [" + level + "] is not defined."); + } + } + + + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java index 62aeebeda..80b3ff955 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/CreateObjectException.java @@ -1,23 +1,23 @@ -package com.avaje.ebeaninternal.server.lib.util; - - -/** - * A general exception when creating an Object. - */ -public class CreateObjectException extends RuntimeException -{ - static final long serialVersionUID = 7061559938704539736L; - - public CreateObjectException(Exception cause) { - super(cause); - } - - public CreateObjectException(String s, Exception cause) { - super(s, cause); - } - - public CreateObjectException(String s) { - super(s); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general exception when creating an Object. + */ +public class CreateObjectException extends RuntimeException +{ + static final long serialVersionUID = 7061559938704539736L; + + public CreateObjectException(Exception cause) { + super(cause); + } + + public CreateObjectException(String s, Exception cause) { + super(s, cause); + } + + public CreateObjectException(String s) { + super(s); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java index 2562aa45a..8b7a0da73 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/Dnode.java @@ -1,324 +1,324 @@ -package com.avaje.ebeaninternal.server.lib.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; - -/** - * A lightweight tree structure for simple XML handling. - *- * It removes support for nodes being mixed with content. That is, a node can - * only contain content or a list of one or more child nodes. It does not - * support mixing bits of content between the child nodes. - *
- *- * Although designed to simplify XML in supported cases it can be used as a - * general tree structure with attributes of java Objects. - *
- */ -public class Dnode { - - int level; - - String nodeName; - - String nodeContent; - - ArrayList- * Will throw a ClassCastException if the attribute is not a String. - *
- */ - public String getStringAttr(String name, String defaultValue) { - Object o = attrList.get(name); - if (o == null){ - return defaultValue; - } else { - return o.toString(); - } - } - - /** - * Set an attribute. - */ - public void setAttribute(String name, String value) { - attrList.put(name, value); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[").append(getNodeName()).append(" ").append(attrList).append("]"); - return sb.toString(); - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * A lightweight tree structure for simple XML handling. + *+ * It removes support for nodes being mixed with content. That is, a node can + * only contain content or a list of one or more child nodes. It does not + * support mixing bits of content between the child nodes. + *
+ *+ * Although designed to simplify XML in supported cases it can be used as a + * general tree structure with attributes of java Objects. + *
+ */ +public class Dnode { + + int level; + + String nodeName; + + String nodeContent; + + ArrayList+ * Will throw a ClassCastException if the attribute is not a String. + *
+ */ + public String getStringAttr(String name, String defaultValue) { + Object o = attrList.get(name); + if (o == null){ + return defaultValue; + } else { + return o.toString(); + } + } + + /** + * Set an attribute. + */ + public void setAttribute(String name, String value) { + attrList.put(name, value); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(getNodeName()).append(" ").append(attrList).append("]"); + return sb.toString(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java index eb40ab7ba..736e515bb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/DnodeParser.java @@ -1,200 +1,200 @@ -package com.avaje.ebeaninternal.server.lib.util; - -import java.util.Stack; - -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -/** - * Parse an xml document into a Dnode tree. - */ -public class DnodeParser extends DefaultHandler { - - /** - * The root of the DContent tree. - */ - Dnode root; - - /** - * The current node being parsed. - */ - Dnode currentNode; - - /** - * The nodeContent buffer. - */ - StringBuilder buffer; - - /** - * Used to stack the nodes. - */ - Stack
- * e.g. "alpha,beta,,theta"
- * With keepEmpties true, this results in a String[] of size 4 with the
- * third one having a String of 0 length. With keepEmpties false, this
- * results in a String[] of size 3.
- *
- *
- *
- * e.g. ",alpha,beta,,theta,"
- * With keepEmpties true, this results in a String[] of size 6 with the
- * 1st,4th and 6th one having a String of 0 length. With keepEmpties false,
- * this results in a String[] of size 3.
- *
- * If leftBound can't be found this returns null. - *
- *- * This rightBound can't be found then this throws a - * StringIndexOutOfBoundsException. - *
- * - * @param str - * the base string that we will search for the bounded string. - * @param leftBound - * the left bound of the string. - * @param rightBound - * the right bound of the string. - */ - public static String getBoundedString(String str, String leftBound, String rightBound) - throws RuntimeException { - - if (str == null) { - throw new RuntimeException("string to parse is null?"); - } - int startPos = str.indexOf(leftBound); - if (startPos > -1) { - startPos = startPos + leftBound.length(); - int endPos = str.indexOf(rightBound, startPos); - // dp(str+" start:"+startPos+" end:"+endPos); - if (endPos == -1) { - throw new RuntimeException("Can't find rightBound: " + rightBound); - } - return str.substring(startPos, endPos); - } else { - // if no leftBound can be found.. return null... could be in a - // search n parse type loop? - // this keeps "no tag"==null different from "tag not formed - // properly"==StringParsingException - return null; - } - } - - /** - * Takes the String bounded by leftBound & rightBound, and replaces it with - * replaceString. Actually removes the left and right bound strings aswell. - */ - public static String setBoundedString(String str, String leftBound, String rightBound, - String replaceString) { - - int startPos = str.indexOf(leftBound); - if (startPos > -1) { - // startPos = startPos; - int endPos = str.indexOf(rightBound, startPos + leftBound.length()); - if (endPos > -1) { - String toReplace = str.substring(startPos, endPos + 1); - return replaceString(str, toReplace, replaceString); - } else { - return str; - } - } else { - return str; - } - } - - // public static String replaceString(String str, String oldSub, String - // newSub) { - // - // if (str == null) { - // return null; - // } - // StringBuilder newSB = new StringBuilder(str.length()+20); - // int iPos = 0; - // int iPrevPos = 0; - // - // while (true) { - // iPos = str.indexOf(oldSub, iPrevPos); - // if (iPos > -1) { - // // found - // newSB.append(str.substring(iPrevPos, iPos)); - // newSB.append(newSub); - // iPrevPos = iPos + oldSub.length(); - // } else { - // // not found - // newSB.append(str.substring(iPrevPos)); - // break; - // } - // } - // - // return newSB.toString(); - // } - - /** - * This method takes a String and will replace all occurrences of the match - * String with that of the replace String. - * - * @param source - * the source string - * @param match - * the string used to find a match - * @param replace - * the string used to replace match with - * @return the source string after the search and replace - */ - public static String replaceString(String source, String match, String replace) { - if (source == null){ - return null; - } - if (replace == null){ - return source; - } - if (match == null){ - throw new NullPointerException("match is null?"); - } - if (match.equals(replace)){ - return source; - } - return replaceString(source, match, replace, 30, 0, source.length()); - } - - /** - * Additionally specify the additionalSize to add to the buffer. This will - * make the buffer bigger so that it doesn't have to grow when replacement - * occurs. - */ - public static String replaceString(String source, String match, String replace, - int additionalSize, int startPos, int endPos) { - - if (source == null){ - return source; - } - - char match0 = match.charAt(0); - - int matchLength = match.length(); - - if (matchLength == 1 && replace.length() == 1) { - char replace0 = replace.charAt(0); - return source.replace(match0, replace0); - } - if (matchLength >= replace.length()) { - additionalSize = 0; - } - - - int sourceLength = source.length(); - int lastMatch = endPos - matchLength; - - StringBuilder sb = new StringBuilder(sourceLength + additionalSize); - - if (startPos > 0) { - sb.append(source.substring(0, startPos)); - } - - char sourceChar; - boolean isMatch; - int sourceMatchPos; - - for (int i = startPos; i < sourceLength; i++) { - sourceChar = source.charAt(i); - if (i > lastMatch || sourceChar != match0) { - sb.append(sourceChar); - - } else { - // check to see if this is a match - isMatch = true; - sourceMatchPos = i; - - // check each following character... - for (int j = 1; j < matchLength; j++) { - sourceMatchPos++; - if (source.charAt(sourceMatchPos) != match.charAt(j)) { - isMatch = false; - break; - } - } - if (isMatch) { - i = i + matchLength - 1; - sb.append(replace); - } else { - // was not a match - sb.append(sourceChar); - } - } - } - - return sb.toString(); - } - - /** - * A search and replace with multiple matching strings. - *- * Useful when converting CRNL CR and NL all to a BR tag for example. - *
- * - *
- * String[] multi = { "\r\n", "\r", "\n" };
- * content = StringHelper.replaceStringMulti(content, multi, "<br/>");
- *
- */
- public static String replaceStringMulti(String source, String[] match, String replace) {
- return replaceStringMulti(source, match, replace, 30, 0, source.length());
- }
-
- /**
- * Additionally specify an additional size estimate for the buffer plus
- * start and end positions.
- * - * The start and end positions can limit the search and replace. Otherwise - * these default to startPos = 0 and endPos = source.length(). - *
- */ - public static String replaceStringMulti(String source, String[] match, String replace, - int additionalSize, int startPos, int endPos) { - - int shortestMatch = match[0].length(); - - char[] match0 = new char[match.length]; - for (int i = 0; i < match0.length; i++) { - match0[i] = match[i].charAt(0); - if (match[i].length() < shortestMatch) { - shortestMatch = match[i].length(); - } - } - - StringBuilder sb = new StringBuilder(source.length() + additionalSize); - - char sourceChar; - - int len = source.length(); - int lastMatch = endPos - shortestMatch; - - if (startPos > 0) { - sb.append(source.substring(0, startPos)); - } - - int matchCount = 0; - - for (int i = startPos; i < len; i++) { - sourceChar = source.charAt(i); - if (i > lastMatch) { - sb.append(sourceChar); - } else { - matchCount = 0; - for (int k = 0; k < match0.length; k++) { - if (matchCount == 0 && sourceChar == match0[k]) { - if (match[k].length() + i <= len) { - - ++matchCount; - int j = 1; - for (; j < match[k].length(); j++) { - if (source.charAt(i + j) != match[k].charAt(j)) { - --matchCount; - break; - } - } - if (matchCount > 0) { - i = i + j - 1; - sb.append(replace); - break; - } - } - } - } - if (matchCount == 0) { - sb.append(sourceChar); - } - } - } - - return sb.toString(); - } - - /** - * This method takes a String as an argument and removes all occurrences of - * the supplied Char. It returns the resulting String. - */ - public static String removeChar(String s, char chr) { - - StringBuilder sb = new StringBuilder(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (c != chr){ - sb.append(c); - } - } - - return sb.toString(); - } - - /** - * This method takes a String as an argument and removes all occurrences of - * the supplied Chars. It returns the resulting String. - */ - public static String removeChars(String s, char[] chr) { - - StringBuilder sb = new StringBuilder(s.length()); - - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (!charMatch(c, chr)){ - sb.append(c); - } - } - - return sb.toString(); - } - - private static boolean charMatch(int iChr, char[] chr) { - for (int i = 0; i < chr.length; i++) { - if (iChr == chr[i]) { - return true; - } - } - return false; - } - -} +package com.avaje.ebeaninternal.server.lib.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility String class that supports String manipulation functions. + */ +public class StringHelper { + + private static final char SINGLE_QUOTE = '\''; + + private static final char DOUBLE_QUOTE = '"'; + + /** + * parses a String of the form name1='value1' name2='value2'. Note that you + * can use either single or double quotes for any particular name value pair + * and the end quote must match the begin quote. + */ + public static HashMap
+ * e.g. "alpha,beta,,theta"
+ * With keepEmpties true, this results in a String[] of size 4 with the
+ * third one having a String of 0 length. With keepEmpties false, this
+ * results in a String[] of size 3.
+ *
+ *
+ *
+ * e.g. ",alpha,beta,,theta,"
+ * With keepEmpties true, this results in a String[] of size 6 with the
+ * 1st,4th and 6th one having a String of 0 length. With keepEmpties false,
+ * this results in a String[] of size 3.
+ *
+ * If leftBound can't be found this returns null. + *
+ *+ * This rightBound can't be found then this throws a + * StringIndexOutOfBoundsException. + *
+ * + * @param str + * the base string that we will search for the bounded string. + * @param leftBound + * the left bound of the string. + * @param rightBound + * the right bound of the string. + */ + public static String getBoundedString(String str, String leftBound, String rightBound) + throws RuntimeException { + + if (str == null) { + throw new RuntimeException("string to parse is null?"); + } + int startPos = str.indexOf(leftBound); + if (startPos > -1) { + startPos = startPos + leftBound.length(); + int endPos = str.indexOf(rightBound, startPos); + // dp(str+" start:"+startPos+" end:"+endPos); + if (endPos == -1) { + throw new RuntimeException("Can't find rightBound: " + rightBound); + } + return str.substring(startPos, endPos); + } else { + // if no leftBound can be found.. return null... could be in a + // search n parse type loop? + // this keeps "no tag"==null different from "tag not formed + // properly"==StringParsingException + return null; + } + } + + /** + * Takes the String bounded by leftBound & rightBound, and replaces it with + * replaceString. Actually removes the left and right bound strings aswell. + */ + public static String setBoundedString(String str, String leftBound, String rightBound, + String replaceString) { + + int startPos = str.indexOf(leftBound); + if (startPos > -1) { + // startPos = startPos; + int endPos = str.indexOf(rightBound, startPos + leftBound.length()); + if (endPos > -1) { + String toReplace = str.substring(startPos, endPos + 1); + return replaceString(str, toReplace, replaceString); + } else { + return str; + } + } else { + return str; + } + } + + // public static String replaceString(String str, String oldSub, String + // newSub) { + // + // if (str == null) { + // return null; + // } + // StringBuilder newSB = new StringBuilder(str.length()+20); + // int iPos = 0; + // int iPrevPos = 0; + // + // while (true) { + // iPos = str.indexOf(oldSub, iPrevPos); + // if (iPos > -1) { + // // found + // newSB.append(str.substring(iPrevPos, iPos)); + // newSB.append(newSub); + // iPrevPos = iPos + oldSub.length(); + // } else { + // // not found + // newSB.append(str.substring(iPrevPos)); + // break; + // } + // } + // + // return newSB.toString(); + // } + + /** + * This method takes a String and will replace all occurrences of the match + * String with that of the replace String. + * + * @param source + * the source string + * @param match + * the string used to find a match + * @param replace + * the string used to replace match with + * @return the source string after the search and replace + */ + public static String replaceString(String source, String match, String replace) { + if (source == null){ + return null; + } + if (replace == null){ + return source; + } + if (match == null){ + throw new NullPointerException("match is null?"); + } + if (match.equals(replace)){ + return source; + } + return replaceString(source, match, replace, 30, 0, source.length()); + } + + /** + * Additionally specify the additionalSize to add to the buffer. This will + * make the buffer bigger so that it doesn't have to grow when replacement + * occurs. + */ + public static String replaceString(String source, String match, String replace, + int additionalSize, int startPos, int endPos) { + + if (source == null){ + return source; + } + + char match0 = match.charAt(0); + + int matchLength = match.length(); + + if (matchLength == 1 && replace.length() == 1) { + char replace0 = replace.charAt(0); + return source.replace(match0, replace0); + } + if (matchLength >= replace.length()) { + additionalSize = 0; + } + + + int sourceLength = source.length(); + int lastMatch = endPos - matchLength; + + StringBuilder sb = new StringBuilder(sourceLength + additionalSize); + + if (startPos > 0) { + sb.append(source.substring(0, startPos)); + } + + char sourceChar; + boolean isMatch; + int sourceMatchPos; + + for (int i = startPos; i < sourceLength; i++) { + sourceChar = source.charAt(i); + if (i > lastMatch || sourceChar != match0) { + sb.append(sourceChar); + + } else { + // check to see if this is a match + isMatch = true; + sourceMatchPos = i; + + // check each following character... + for (int j = 1; j < matchLength; j++) { + sourceMatchPos++; + if (source.charAt(sourceMatchPos) != match.charAt(j)) { + isMatch = false; + break; + } + } + if (isMatch) { + i = i + matchLength - 1; + sb.append(replace); + } else { + // was not a match + sb.append(sourceChar); + } + } + } + + return sb.toString(); + } + + /** + * A search and replace with multiple matching strings. + *+ * Useful when converting CRNL CR and NL all to a BR tag for example. + *
+ * + *
+ * String[] multi = { "\r\n", "\r", "\n" };
+ * content = StringHelper.replaceStringMulti(content, multi, "<br/>");
+ *
+ */
+ public static String replaceStringMulti(String source, String[] match, String replace) {
+ return replaceStringMulti(source, match, replace, 30, 0, source.length());
+ }
+
+ /**
+ * Additionally specify an additional size estimate for the buffer plus
+ * start and end positions.
+ * + * The start and end positions can limit the search and replace. Otherwise + * these default to startPos = 0 and endPos = source.length(). + *
+ */ + public static String replaceStringMulti(String source, String[] match, String replace, + int additionalSize, int startPos, int endPos) { + + int shortestMatch = match[0].length(); + + char[] match0 = new char[match.length]; + for (int i = 0; i < match0.length; i++) { + match0[i] = match[i].charAt(0); + if (match[i].length() < shortestMatch) { + shortestMatch = match[i].length(); + } + } + + StringBuilder sb = new StringBuilder(source.length() + additionalSize); + + char sourceChar; + + int len = source.length(); + int lastMatch = endPos - shortestMatch; + + if (startPos > 0) { + sb.append(source.substring(0, startPos)); + } + + int matchCount = 0; + + for (int i = startPos; i < len; i++) { + sourceChar = source.charAt(i); + if (i > lastMatch) { + sb.append(sourceChar); + } else { + matchCount = 0; + for (int k = 0; k < match0.length; k++) { + if (matchCount == 0 && sourceChar == match0[k]) { + if (match[k].length() + i <= len) { + + ++matchCount; + int j = 1; + for (; j < match[k].length(); j++) { + if (source.charAt(i + j) != match[k].charAt(j)) { + --matchCount; + break; + } + } + if (matchCount > 0) { + i = i + j - 1; + sb.append(replace); + break; + } + } + } + } + if (matchCount == 0) { + sb.append(sourceChar); + } + } + } + + return sb.toString(); + } + + /** + * This method takes a String as an argument and removes all occurrences of + * the supplied Char. It returns the resulting String. + */ + public static String removeChar(String s, char chr) { + + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c != chr){ + sb.append(c); + } + } + + return sb.toString(); + } + + /** + * This method takes a String as an argument and removes all occurrences of + * the supplied Chars. It returns the resulting String. + */ + public static String removeChars(String s, char[] chr) { + + StringBuilder sb = new StringBuilder(s.length()); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!charMatch(c, chr)){ + sb.append(c); + } + } + + return sb.toString(); + } + + private static boolean charMatch(int iChr, char[] chr) { + for (int i = 0; i < chr.length; i++) { + if (iChr == chr[i]) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java index cdd091a54..e55872587 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/StringParsingException.java @@ -1,15 +1,15 @@ -package com.avaje.ebeaninternal.server.lib.util; - - -/** - * A general string parsing exception. - */ -public class StringParsingException extends RuntimeException { - - static final long serialVersionUID = -3070423471260426402L; - - public StringParsingException(String message) { - super(message); - } -}; - +package com.avaje.ebeaninternal.server.lib.util; + + +/** + * A general string parsing exception. + */ +public class StringParsingException extends RuntimeException { + + static final long serialVersionUID = -3070423471260426402L; + + public StringParsingException(String message) { + super(message); + } +}; + diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java b/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java index a21a4cb0e..4c995f98e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/lib/util/ThrowablePrinter.java @@ -1,91 +1,91 @@ -package com.avaje.ebeaninternal.server.lib.util; - -/** - * Builds a string from a stack trace. - *- * Generally used to flatten a stack trace into a single string - * removing \r\n and limiting the size of any given stack. - *
- */ -public class ThrowablePrinter { - - private static final String atString = " at "; - - private String newLineChar = "\\r\\n"; - - private int maxStackTraceLines = 3; - - /** - * Set the maximum number of lines in any one part of - * the stack trace. This is not the total maximum. - */ - public void setMaxStackTraceLines(int maxStackTraceLines) { - this.maxStackTraceLines = maxStackTraceLines; - } - - /** - * Set the new line character used to replace \r\n with. - * This is useful so that the stack is placed on a single line - * in a log file. - */ - public void setNewLineChar(String newLineChar) { - this.newLineChar = newLineChar; - } - - /** - * Convert the error into a string representation. - *- * Replaces the \r\n and limits the stack lines. - *
- */ - public String print(Throwable e) { - StringBuffer sb = new StringBuffer(); - printThrowable(sb, e, false); - - String line = sb.toString(); - line = StringHelper.replaceString(line, "\r", "\\r"); - line = StringHelper.replaceString(line, "\n", "\\n"); - - return line; - } - - /** - * Recursively output the Throwable stack trace to the log. - * - * @param sb the buffer to write the stack trace to - * @param e the source throwable - * @param isCause flag to indicate if this is the top level throwable or a - * cause - */ - protected void printThrowable(StringBuffer sb, Throwable e, boolean isCause) { - if (e != null) { - if (isCause) { - sb.append("Caused by: "); - } - sb.append(e.getClass().getName()); - sb.append(":"); - sb.append(e.getMessage()).append(newLineChar); - - StackTraceElement[] ste = e.getStackTrace(); - int outputStackLines = ste.length; - int notShownCount = 0; - if (ste.length > maxStackTraceLines) { - outputStackLines = maxStackTraceLines; - notShownCount = ste.length - outputStackLines; - } - for (int i = 0; i < outputStackLines; i++) { - sb.append(atString); - sb.append(ste[i].toString()).append(newLineChar); - } - if (notShownCount > 0) { - sb.append(" ... "); - sb.append(notShownCount); - sb.append(" more").append(newLineChar); - } - Throwable cause = e.getCause(); - if (cause != null) { - printThrowable(sb, cause, true); - } - } - } -} +package com.avaje.ebeaninternal.server.lib.util; + +/** + * Builds a string from a stack trace. + *+ * Generally used to flatten a stack trace into a single string + * removing \r\n and limiting the size of any given stack. + *
+ */ +public class ThrowablePrinter { + + private static final String atString = " at "; + + private String newLineChar = "\\r\\n"; + + private int maxStackTraceLines = 3; + + /** + * Set the maximum number of lines in any one part of + * the stack trace. This is not the total maximum. + */ + public void setMaxStackTraceLines(int maxStackTraceLines) { + this.maxStackTraceLines = maxStackTraceLines; + } + + /** + * Set the new line character used to replace \r\n with. + * This is useful so that the stack is placed on a single line + * in a log file. + */ + public void setNewLineChar(String newLineChar) { + this.newLineChar = newLineChar; + } + + /** + * Convert the error into a string representation. + *+ * Replaces the \r\n and limits the stack lines. + *
+ */ + public String print(Throwable e) { + StringBuffer sb = new StringBuffer(); + printThrowable(sb, e, false); + + String line = sb.toString(); + line = StringHelper.replaceString(line, "\r", "\\r"); + line = StringHelper.replaceString(line, "\n", "\\n"); + + return line; + } + + /** + * Recursively output the Throwable stack trace to the log. + * + * @param sb the buffer to write the stack trace to + * @param e the source throwable + * @param isCause flag to indicate if this is the top level throwable or a + * cause + */ + protected void printThrowable(StringBuffer sb, Throwable e, boolean isCause) { + if (e != null) { + if (isCause) { + sb.append("Caused by: "); + } + sb.append(e.getClass().getName()); + sb.append(":"); + sb.append(e.getMessage()).append(newLineChar); + + StackTraceElement[] ste = e.getStackTrace(); + int outputStackLines = ste.length; + int notShownCount = 0; + if (ste.length > maxStackTraceLines) { + outputStackLines = maxStackTraceLines; + notShownCount = ste.length - outputStackLines; + } + for (int i = 0; i < outputStackLines; i++) { + sb.append(atString); + sb.append(ste[i].toString()).append(newLineChar); + } + if (notShownCount > 0) { + sb.append(" ... "); + sb.append(notShownCount); + sb.append(" more").append(newLineChar); + } + Throwable cause = e.getCause(); + if (cause != null) { + printThrowable(sb, cause, true); + } + } + } +}