Compare commits

..
Author SHA1 Message Date
Robin Bygrave 737022bc57 [maven-release-plugin] prepare release avaje-ebeanorm-7.4.1 2016-03-29 16:39:32 +13:00
Robin Bygrave de6ba31996 #627 - Rename Query.includeSoftDeletes() to Query.setIncludeSoftDeletes() 2016-03-29 16:38:09 +13:00
Robin Bygrave df95f4759b Bump pom to 7.4.1-SNAPSHOT 2016-03-29 16:11:37 +13:00
Robin Bygrave 32f4a56ab4 #626 - ENH: Add support for where().idIn(...) using varargs 2016-03-29 16:10:00 +13:00
Robin Bygrave 04965892f0 #625 - Rename @DocStoreEmbedded to @DocEmbedded (with deprecation) 2016-03-29 15:46:10 +13:00
Robin Bygrave fdb2965d6e #622 - Set interrupted status for findFutureList() ... (specifically the FutureList.getUnchecked() methods) 2016-03-25 16:49:03 +13:00
Robin Bygrave ab2789dd08 #621 - EbeanServer.shutdown(true, false) followed by EbeanServerFactory create() when using the same ServerConfig give SQLException: Trying to access the Connection Pool when it is shutting down 2016-03-25 16:22:11 +13:00
Robin Bygrave 8f80280105 No effective change - tidy up in DefaultContainer 2016-03-25 16:18:29 +13:00
Robin Bygrave 1179f84611 Javadoc only update #619 2016-03-25 10:51:41 +13:00
Robin Bygrave 8593d3e496 [maven-release-plugin] prepare for next development iteration 2016-03-24 14:34:54 +13:00
Robin Bygrave c658b8c80c [maven-release-plugin] prepare release avaje-ebeanorm-7.3.1 2016-03-24 14:34:31 +13:00
Robin Bygrave 44e3c6b9a8 Bump pom to 7.3.1-SNAPSHOT 2016-03-24 14:30:07 +13:00
Robin Bygrave c296127bab #618 - Refactor - remove DataSourcePool implementation as separate dependency 2016-03-24 14:29:21 +13:00
Robin Bygrave 83d0d6320c No effective change - remove from test properties on H2 DB_CLOSE_DELAY=-1 2016-03-24 14:21:45 +13:00
Robin Bygrave 6ce4ca4a5b No effective change - fix test method visibility 2016-03-24 11:32:17 +13:00
Robin Bygrave 21c97e786d No effective change - fix test to put back connections 2016-03-24 11:30:55 +13:00
Robin Bygrave 2a1c394093 #617 - Refactor - tidy up DataSource internals, reduce method visibility 2016-03-23 21:21:50 +13:00
Robin Bygrave f55b348a55 #617 - Refactor - tidy up DataSource internals, reduce method visibility - markWithError() 2016-03-23 20:54:55 +13:00
Robin Bygrave 84b2ffa971 #617 - Refactor - tidy up DataSource internals, reduce method visibility 2016-03-23 20:51:32 +13:00
Robin Bygrave 406b7304ad [maven-release-plugin] prepare for next development iteration 2016-03-23 18:20:56 +13:00
71 changed files with 331 additions and 6574 deletions
+20 -3
View File
@@ -9,7 +9,7 @@
<groupId>org.avaje.ebeanorm</groupId>
<artifactId>avaje-ebeanorm</artifactId>
<version>7.2.3</version>
<version>7.4.1</version>
<packaging>jar</packaging>
<name>avaje-ebeanorm</name>
@@ -55,6 +55,24 @@
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
<artifactId>avaje-datasource</artifactId>
<version>1.1.1</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.avaje</groupId>-->
<!--<artifactId>avaje-datasource-alert</artifactId>-->
<!--<version>1.1.1</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -273,7 +291,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<doctitle>Ebean 6</doctitle>
<doctitle>Ebean 7</doctitle>
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
<source>1.8</source>
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
@@ -292,7 +310,6 @@
</additionalparam>
<linksource>true</linksource>
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
</configuration>
<executions>
@@ -251,6 +251,11 @@ public interface ExpressionFactory {
*/
Expression idEq(Object value);
/**
* Id IN a list of Id values.
*/
Expression idIn(Object... idValues);
/**
* Id IN a list of Id values.
*/
@@ -116,10 +116,16 @@ public interface ExpressionList<T> {
*/
Query<T> asDraft();
/**
* Deprecated in favour of setIncludeSoftDeletes().
*/
@Deprecated
Query<T> includeSoftDeletes();
/**
* Execute the query including soft deleted rows.
*/
Query<T> includeSoftDeletes();
Query<T> setIncludeSoftDeletes();
/**
* Execute as a delete query deleting the 'root level' beans that match the predicates
@@ -758,6 +764,11 @@ public interface ExpressionList<T> {
*/
ExpressionList<T> notExists(Query<?> subQuery);
/**
* Id IN a list of id values.
*/
ExpressionList<T> idIn(Object... idValues);
/**
* Id IN a list of id values.
*/
+7 -1
View File
@@ -302,8 +302,9 @@ public interface Query<T> {
Query<T> asDraft();
/**
* Execute the query including soft deleted rows.
* Deprecated in favour of setIncludeSoftDeletes().
*/
@Deprecated
Query<T> includeSoftDeletes();
/**
@@ -375,6 +376,11 @@ public interface Query<T> {
*/
Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
/**
* Execute the query including soft deleted rows.
*/
Query<T> setIncludeSoftDeletes();
/**
* Disable read auditing for this query.
* <p>
@@ -0,0 +1,37 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify the property is included in the parent document store index.
*
* <pre>{@code
*
*
* @DocStore
* @Entity @Table(name = "o_order")
* public class Order {
*
* ...
* // include some customer details including
* // nested billingAddress
* @DocEmbedded(doc = "id,status,name,billingAddress(*,country(*)")
* @ManyToOne
* Customer customer;
*
*
* }</pre>
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DocEmbedded {
/**
* The properties on the embedded bean to include in the index.
*/
String doc() default "";
}
@@ -6,27 +6,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify the property is included in the parent document store index.
*
* <pre>{@code
*
*
* @DocStore
* @Entity @Table(name = "o_order")
* public class Order {
*
* ...
* // include some customer details including
* // nested billingAddress
* @DocStoreEmbedded(doc = "id,status,name,billingAddress(*,country(*)")
* @ManyToOne
* Customer customer;
*
*
* }</pre>
* Deprecated in favor of @DocEmbedded (i.e. renamed to @DocEmbedded)
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
public @interface DocStoreEmbedded {
/**
@@ -1,556 +0,0 @@
package com.avaje.ebean.config;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.util.StringHelper;
/**
* Used to config a DataSource when using the internal Ebean DataSource
* implementation.
* <p>
* If a DataSource instance is already defined via
* {@link ServerConfig#setDataSource(javax.sql.DataSource)} or defined as JNDI
* dataSource via {@link ServerConfig#setDataSourceJndiName(String)} then those
* will used and not this DataSourceConfig.
* </p>
*/
public class DataSourceConfig {
private String url;
private String username;
private String password;
private String driver;
private int minConnections = 2;
private int maxConnections = 20;
private int isolationLevel = Transaction.READ_COMMITTED;
private boolean autoCommit;
private String heartbeatSql;
private int heartbeatFreqSecs = 30;
private int heartbeatTimeoutSeconds = 3;
private boolean captureStackTrace;
private int maxStackTraceSize = 5;
private int leakTimeMinutes = 30;
private int maxInactiveTimeSecs = 720;
private int maxAgeMinutes = 0;
private int trimPoolFreqSecs = 59;
private int pstmtCacheSize = 20;
private int cstmtCacheSize = 20;
private int waitTimeoutMillis = 1000;
private String poolListener;
private boolean offline;
protected Map<String, String> customProperties;
/**
* Return the connection URL.
*/
public String getUrl() {
return url;
}
/**
* Set the connection URL.
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Return the database username.
*/
public String getUsername() {
return username;
}
/**
* Set the database username.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* Return the database password.
*/
public String getPassword() {
return password;
}
/**
* Set the database password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* Return the database driver.
*/
public String getDriver() {
return driver;
}
/**
* Set the database driver.
*/
public void setDriver(String driver) {
this.driver = driver;
}
/**
* Return the transaction isolation level.
*/
public int getIsolationLevel() {
return isolationLevel;
}
/**
* Set the transaction isolation level.
*/
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
/**
* Return autoCommit setting.
*/
public boolean isAutoCommit() {
return autoCommit;
}
/**
* Set to true to turn on autoCommit.
*/
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
/**
* Return the minimum number of connections the pool should maintain.
*/
public int getMinConnections() {
return minConnections;
}
/**
* Set the minimum number of connections the pool should maintain.
*/
public void setMinConnections(int minConnections) {
this.minConnections = minConnections;
}
/**
* Return the maximum number of connections the pool can reach.
*/
public int getMaxConnections() {
return maxConnections;
}
/**
* Set the maximum number of connections the pool can reach.
*/
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
/**
* Return a SQL statement used to test the database is accessible.
* <p>
* Note that if this is not set then it can get defaulted from the
* DatabasePlatform.
* </p>
*/
public String getHeartbeatSql() {
return heartbeatSql;
}
/**
* Set a SQL statement used to test the database is accessible.
* <p>
* Note that if this is not set then it can get defaulted from the
* DatabasePlatform.
* </p>
*/
public void setHeartbeatSql(String heartbeatSql) {
this.heartbeatSql = heartbeatSql;
}
/**
* Return the heartbeat frequency in seconds.
* <p>
* This is the expected frequency in which the DataSource should be checked to
* make sure it is healthy and trim idle connections.
* </p>
*/
public int getHeartbeatFreqSecs() {
return heartbeatFreqSecs;
}
/**
* Set the expected heartbeat frequency in seconds.
*/
public void setHeartbeatFreqSecs(int heartbeatFreqSecs) {
this.heartbeatFreqSecs = heartbeatFreqSecs;
}
/**
* Return the heart beat timeout in seconds.
*/
public int getHeartbeatTimeoutSeconds() {
return heartbeatTimeoutSeconds;
}
/**
* Set the heart beat timeout in seconds.
*/
public void setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) {
this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds;
}
/**
* Return true if a stack trace should be captured when obtaining a connection
* from the pool.
* <p>
* This can be used to diagnose a suspected connection pool leak.
* </p>
* <p>
* Obviously this has a performance overhead.
* </p>
*/
public boolean isCaptureStackTrace() {
return captureStackTrace;
}
/**
* Set to true if a stack trace should be captured when obtaining a connection
* from the pool.
* <p>
* This can be used to diagnose a suspected connection pool leak.
* </p>
* <p>
* Obviously this has a performance overhead.
* </p>
*/
public void setCaptureStackTrace(boolean captureStackTrace) {
this.captureStackTrace = captureStackTrace;
}
/**
* Return the max size for reporting stack traces on busy connections.
*/
public int getMaxStackTraceSize() {
return maxStackTraceSize;
}
/**
* Set the max size for reporting stack traces on busy connections.
*/
public void setMaxStackTraceSize(int maxStackTraceSize) {
this.maxStackTraceSize = maxStackTraceSize;
}
/**
* Return the time in minutes after which a connection could be considered to
* have leaked.
*/
public int getLeakTimeMinutes() {
return leakTimeMinutes;
}
/**
* Set the time in minutes after which a connection could be considered to
* have leaked.
*/
public void setLeakTimeMinutes(int leakTimeMinutes) {
this.leakTimeMinutes = leakTimeMinutes;
}
/**
* Return the size of the PreparedStatement cache (per connection).
*/
public int getPstmtCacheSize() {
return pstmtCacheSize;
}
/**
* Set the size of the PreparedStatement cache (per connection).
*/
public void setPstmtCacheSize(int pstmtCacheSize) {
this.pstmtCacheSize = pstmtCacheSize;
}
/**
* Return the size of the CallableStatement cache (per connection).
*/
public int getCstmtCacheSize() {
return cstmtCacheSize;
}
/**
* Set the size of the CallableStatement cache (per connection).
*/
public void setCstmtCacheSize(int cstmtCacheSize) {
this.cstmtCacheSize = cstmtCacheSize;
}
/**
* Return the time in millis to wait for a connection before timing out once
* the pool has reached its maximum size.
*/
public int getWaitTimeoutMillis() {
return waitTimeoutMillis;
}
/**
* Set the time in millis to wait for a connection before timing out once the
* pool has reached its maximum size.
*/
public void setWaitTimeoutMillis(int waitTimeoutMillis) {
this.waitTimeoutMillis = waitTimeoutMillis;
}
/**
* Return the time in seconds a connection can be idle after which it can be
* trimmed from the pool.
* <p>
* This is so that the pool after a busy period can trend over time back
* towards the minimum connections.
* </p>
*/
public int getMaxInactiveTimeSecs() {
return maxInactiveTimeSecs;
}
/**
* Return the maximum age a connection is allowed to be before it is closed.
* <p>
* This can be used to close really old connections.
* </p>
*/
public int getMaxAgeMinutes() {
return maxAgeMinutes;
}
/**
* Set the maximum age a connection can be in minutes.
*/
public void setMaxAgeMinutes(int maxAgeMinutes) {
this.maxAgeMinutes = maxAgeMinutes;
}
/**
* Set the time in seconds a connection can be idle after which it can be
* trimmed from the pool.
* <p>
* This is so that the pool after a busy period can trend over time back
* towards the minimum connections.
* </p>
*/
public void setMaxInactiveTimeSecs(int maxInactiveTimeSecs) {
this.maxInactiveTimeSecs = maxInactiveTimeSecs;
}
/**
* Return the minimum time gap between pool trim checks.
* <p>
* This defaults to 59 seconds meaning that the pool trim check will run every
* minute assuming the heart beat check runs every 30 seconds.
* </p>
*/
public int getTrimPoolFreqSecs() {
return trimPoolFreqSecs;
}
/**
* Set the minimum trim gap between pool trim checks.
*/
public void setTrimPoolFreqSecs(int trimPoolFreqSecs) {
this.trimPoolFreqSecs = trimPoolFreqSecs;
}
/**
* Return the pool listener.
*/
public String getPoolListener() {
return poolListener;
}
/**
* Set a pool listener.
*/
public void setPoolListener(String poolListener) {
this.poolListener = poolListener;
}
/**
* Return true if the DataSource should be left offline.
* <p>
* This is to support DDL generation etc without having a real database.
* </p>
*/
public boolean isOffline() {
return offline;
}
/**
* Set to true if the DataSource should be left offline.
* <p>
* This is to support DDL generation etc without having a real database.
* </p>
* <p>
* Note that you MUST specify the database platform name (oracle, postgres,
* h2, mysql etc) using {@link ServerConfig#setDatabasePlatformName(String)}
* when you do this.
* </p>
*/
public void setOffline(boolean offline) {
this.offline = offline;
}
/**
* Return a map of custom properties for the jdbc driver connection.
*/
public Map<String, String> getCustomProperties() {
return customProperties;
}
/**
* Set custom properties for the jdbc driver connection.
*
* @param customProperties
*/
public void setCustomProperties(Map<String, String> customProperties) {
this.customProperties = customProperties;
}
/**
* Load the settings by reading the ebean.properties file.
*
* @param serverName name of the server
*/
public void loadSettings(String serverName) {
loadSettings(new PropertiesWrapper("datasource", serverName, PropertyMap.defaultProperties()));
}
/**
* Load the settings from the properties supplied.
* <p>
* You can use this when you have your own properties to use for configuration.
* </p>
*
* @param properties the properties to configure the datasource
* @param serverName the name of the specific datasource (optional)
*/
public void loadSettings(Properties properties, String serverName) {
PropertiesWrapper dbProps = new PropertiesWrapper("datasource", serverName, properties);
loadSettings(dbProps);
}
/**
* Load the settings from the PropertiesWrapper.
*/
public void loadSettings(PropertiesWrapper properties) {
username = properties.get("username", username);
password = properties.get("password", password);
driver = properties.get("driver", properties.get("databaseDriver", driver));
url = properties.get("url", properties.get("databaseUrl", url));
autoCommit = properties.getBoolean("autoCommit", autoCommit);
captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace);
maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize);
leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes);
maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs);
trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs);
maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes);
minConnections = properties.getInt("minConnections", minConnections);
maxConnections = properties.getInt("maxConnections", maxConnections);
pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize);
cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize);
waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis);
heartbeatSql = properties.get("heartbeatSql", heartbeatSql);
heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds);
poolListener = properties.get("poolListener", poolListener);
offline = properties.getBoolean("offline", offline);
String isoLevel = properties.get("isolationlevel", getTransactionIsolationLevel(isolationLevel));
this.isolationLevel = getTransactionIsolationLevel(isoLevel);
String customProperties = properties.get("customProperties", null);
if (customProperties != null && customProperties.length() > 0) {
this.customProperties = StringHelper.delimitedToMap(customProperties, ";", "=");
}
}
/**
* Return the isolation level description from the associated Connection int value.
*/
public String getTransactionIsolationLevel(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";
default: throw new RuntimeException("Transaction Isolation level [" + level + "] is not known.");
}
}
/**
* Return the isolation level for a given string description.
*/
public int getTransactionIsolationLevel(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 Isolation level [" + level + "] is not known.");
}
}
@@ -22,6 +22,7 @@ import com.avaje.ebean.event.readaudit.ReadAuditLogger;
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
import com.avaje.ebean.meta.MetaInfoManager;
import com.fasterxml.jackson.core.JsonFactory;
import org.avaje.datasource.DataSourceConfig;
import javax.sql.DataSource;
import java.util.ArrayList;
@@ -1391,8 +1392,6 @@ public class ServerConfig {
* <p>
* Values are oracle, h2, postgres, mysql, mssqlserver2005.
* </p>
*
* @see DataSourceConfig#setOffline(boolean)
*/
public void setDatabasePlatformName(String databasePlatformName) {
this.databasePlatformName = databasePlatformName;
@@ -2246,7 +2245,7 @@ public class ServerConfig {
* @param p - The defined property source passed to load settings
*/
protected void loadDataSourceSettings(PropertiesWrapper p) {
dataSourceConfig.loadSettings(p.withPrefix("datasource"));
dataSourceConfig.loadSettings(p.properties, name);
}
/**
+57 -58
View File
@@ -3,85 +3,84 @@
<TITLE>Ebean core API</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
Core API (see <a href="Ebean.html">Ebean</a> and <a href="EbeanServer.html">EbeanServer</a>).
Core API (see <a href="EbeanServer.html">EbeanServer</a> and <a href="Ebean.html">Ebean</a>).
<h3>Ebean</h3>
<p>
Provides the main API for fetching and persisting beans with eBean.
</p>
<pre class="code">
// EXAMPLE 1: Simple fetch
//========================
<pre>{@code
// EXAMPLE 1: Simple fetch
//========================
// fetch order 10
Order order = Ebean.find(Order.class, 10);
// fetch order 10
Order order = Ebean.find(Order.class, 10);
// EXAMPLE 2: Fetch an Object with associations
//=============================================
// fetch Customer 7 including their billing and shipping addresses
Customer customer =
Ebean.find(Customer.class)
.setId(7)
.fetch("billingAddress")
.fetch("shippingAddress")
.findUnique();
Address billAddr = customer.getBillingAddress();
Address shipAddr = customer.getShippingAddress();
// EXAMPLE 2: Fetch an Object with associations
//=============================================
// fetch Customer 7 including their billing and shipping addresses
Customer customer =
Ebean.find(Customer.class)
.setId(7)
.fetch("billingAddress")
.fetch("shippingAddress")
.findUnique();
Address billAddr = customer.getBillingAddress();
Address shipAddr = customer.getShippingAddress();
// EXAMPLE 3: Create and save an Order
//=====================================
// get a Customer reference so we don't hit the database
Customer custRef = Ebean.getReference(Customer.class, 7);
// EXAMPLE 3: Create and save an Order
//=====================================
// create a new Order object
Order newOrder = new Order();
newOrder.setStatus(Order.Status.NEW);
newOrder.setCustomer(custRef);
ArrayList orderLines = new ArrayList();
newOrder.setLines(orderLines);
...
// get a Customer reference so we don't hit the database
Customer custRef = Ebean.getReference(Customer.class, 7);
// add a line to the order
Product prodRef = Ebean.getReference(Product.class, 41);
OrderLine line = new OrderLine();
line.setProduct(prodRef);
line.setQuantity(10);
orderLines.add(line);
...
// create a new Order object
Order newOrder = new Order();
newOrder.setStatus(Order.Status.NEW);
newOrder.setCustomer(custRef);
// save the order and its lines in a single transaction
// NB: assumes CascadeType.PERSIST is set on the order lines association
Ebean.save(newOrder);
ArrayList orderLines = new ArrayList();
newOrder.setLines(orderLines);
...
// add a line to the order
Product prodRef = Ebean.getReference(Product.class, 41);
OrderLine line = new OrderLine();
line.setProduct(prodRef);
line.setQuantity(10);
orderLines.add(line);
...
// save the order and its lines in a single transaction
// NB: assumes CascadeType.PERSIST is set on the order lines association
Ebean.save(newOrder);
// EXAMPLE 4: Use another database
//=================================
// EXAMPLE 4: Use another database
//=================================
// Get access to the Human Resources EbeanServer/Database
EbeanServer hrServer = Ebean.getServer(&quot;HR&quot;);
// fetch contact 3 from the HR database
Contact contact = hrServer.find(Contact.class, 3);
contact.setStatus(Contact.Status.INACTIVE);
...
// save the contact back to the HR database
hrServer.save(contact);
</pre>
// Get access to the Human Resources EbeanServer/Database
EbeanServer hrServer = Ebean.getServer(&quot;HR&quot;);
// fetch contact 3 from the HR database
Contact contact = hrServer.find(Contact.class, 3);
contact.setStatus(Contact.Status.INACTIVE);
...
// save the contact back to the HR database
hrServer.save(contact);
}</pre>
</Body>
</HTML>
@@ -0,0 +1,4 @@
/**
* Provides text search expressions like Match, TextQueryString etc.
*/
package com.avaje.ebean.search;
@@ -1,254 +0,0 @@
package com.avaje.ebeaninternal.jdbc;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class ConnectionDelegator implements Connection {
private final Connection delegate;
public ConnectionDelegator(Connection delegate) {
this.delegate = delegate;
}
@Override
public void setSchema(String schema) throws SQLException {
delegate.setSchema(schema);
}
@Override
public String getSchema() throws SQLException {
return delegate.getSchema();
}
@Override
public void abort(Executor executor) throws SQLException {
delegate.abort(executor);
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
delegate.setNetworkTimeout(executor, milliseconds);
}
@Override
public int getNetworkTimeout() throws SQLException {
return delegate.getNetworkTimeout();
}
public Statement createStatement() throws SQLException {
return delegate.createStatement();
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
return delegate.prepareStatement(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
return delegate.prepareCall(sql);
}
public String nativeSQL(String sql) throws SQLException {
return delegate.nativeSQL(sql);
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
delegate.setAutoCommit(autoCommit);
}
public boolean getAutoCommit() throws SQLException {
return delegate.getAutoCommit();
}
public void commit() throws SQLException {
delegate.commit();
}
public void rollback() throws SQLException {
delegate.rollback();
}
public void close() throws SQLException {
delegate.close();
}
public boolean isClosed() throws SQLException {
return delegate.isClosed();
}
public DatabaseMetaData getMetaData() throws SQLException {
return delegate.getMetaData();
}
public void setReadOnly(boolean readOnly) throws SQLException {
delegate.setReadOnly(readOnly);
}
public boolean isReadOnly() throws SQLException {
return delegate.isReadOnly();
}
public void setCatalog(String catalog) throws SQLException {
delegate.setCatalog(catalog);
}
public String getCatalog() throws SQLException {
return delegate.getCatalog();
}
public void setTransactionIsolation(int level) throws SQLException {
delegate.setTransactionIsolation(level);
}
public int getTransactionIsolation() throws SQLException {
return delegate.getTransactionIsolation();
}
public SQLWarning getWarnings() throws SQLException {
return delegate.getWarnings();
}
public void clearWarnings() throws SQLException {
delegate.clearWarnings();
}
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return delegate.createStatement(resultSetType, resultSetConcurrency);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
}
public Map<String, Class<?>> getTypeMap() throws SQLException {
return delegate.getTypeMap();
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
delegate.setTypeMap(map);
}
public void setHoldability(int holdability) throws SQLException {
delegate.setHoldability(holdability);
}
public int getHoldability() throws SQLException {
return delegate.getHoldability();
}
public Savepoint setSavepoint() throws SQLException {
return delegate.setSavepoint();
}
public Savepoint setSavepoint(String name) throws SQLException {
return delegate.setSavepoint(name);
}
public void rollback(Savepoint savepoint) throws SQLException {
delegate.rollback(savepoint);
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
delegate.releaseSavepoint(savepoint);
}
public Statement createStatement(int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.prepareStatement(sql, autoGeneratedKeys);
}
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return delegate.prepareStatement(sql, columnIndexes);
}
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return delegate.prepareStatement(sql, columnNames);
}
public Clob createClob() throws SQLException {
return delegate.createClob();
}
public Blob createBlob() throws SQLException {
return delegate.createBlob();
}
public NClob createNClob() throws SQLException {
return delegate.createNClob();
}
public SQLXML createSQLXML() throws SQLException {
return delegate.createSQLXML();
}
public boolean isValid(int timeout) throws SQLException {
return delegate.isValid(timeout);
}
public void setClientInfo(String name, String value) throws SQLClientInfoException {
delegate.setClientInfo(name, value);
}
public void setClientInfo(Properties properties) throws SQLClientInfoException {
delegate.setClientInfo(properties);
}
public String getClientInfo(String name) throws SQLException {
return delegate.getClientInfo(name);
}
public Properties getClientInfo() throws SQLException {
return delegate.getClientInfo();
}
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return delegate.createArrayOf(typeName, elements);
}
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return delegate.createStruct(typeName, attributes);
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}
}
@@ -1,435 +0,0 @@
package com.avaje.ebeaninternal.jdbc;
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.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
public class PreparedStatementDelegator implements PreparedStatement {
private final PreparedStatement delegate;
public PreparedStatementDelegator(PreparedStatement delegate) {
this.delegate = delegate;
}
@Override
public void closeOnCompletion() throws SQLException {
delegate.closeOnCompletion();
}
@Override
public boolean isCloseOnCompletion() throws SQLException {
return delegate.isCloseOnCompletion();
}
public ResultSet executeQuery() throws SQLException {
return delegate.executeQuery();
}
public int executeUpdate() throws SQLException {
return delegate.executeUpdate();
}
public void setNull(int parameterIndex, int sqlType) throws SQLException {
delegate.setNull(parameterIndex, sqlType);
}
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
delegate.setBoolean(parameterIndex, x);
}
public void setByte(int parameterIndex, byte x) throws SQLException {
delegate.setByte(parameterIndex, x);
}
public void setShort(int parameterIndex, short x) throws SQLException {
delegate.setShort(parameterIndex, x);
}
public void setInt(int parameterIndex, int x) throws SQLException {
delegate.setInt(parameterIndex, x);
}
public void setLong(int parameterIndex, long x) throws SQLException {
delegate.setLong(parameterIndex, x);
}
public void setFloat(int parameterIndex, float x) throws SQLException {
delegate.setFloat(parameterIndex, x);
}
public void setDouble(int parameterIndex, double x) throws SQLException {
delegate.setDouble(parameterIndex, x);
}
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
delegate.setBigDecimal(parameterIndex, x);
}
public void setString(int parameterIndex, String x) throws SQLException {
delegate.setString(parameterIndex, x);
}
public void setBytes(int parameterIndex, byte[] x) throws SQLException {
delegate.setBytes(parameterIndex, x);
}
public void setDate(int parameterIndex, Date x) throws SQLException {
delegate.setDate(parameterIndex, x);
}
public void setTime(int parameterIndex, Time x) throws SQLException {
delegate.setTime(parameterIndex, x);
}
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
delegate.setTimestamp(parameterIndex, x);
}
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setAsciiStream(parameterIndex, x, length);
}
@SuppressWarnings("deprecation")
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setUnicodeStream(parameterIndex, x, length);
}
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
delegate.setBinaryStream(parameterIndex, x, length);
}
public void clearParameters() throws SQLException {
delegate.clearParameters();
}
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType);
}
public void setObject(int parameterIndex, Object x) throws SQLException {
delegate.setObject(parameterIndex, x);
}
public boolean execute() throws SQLException {
return delegate.execute();
}
public void addBatch() throws SQLException {
delegate.addBatch();
}
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
delegate.setCharacterStream(parameterIndex, reader, length);
}
public void setRef(int parameterIndex, Ref x) throws SQLException {
delegate.setRef(parameterIndex, x);
}
public void setBlob(int parameterIndex, Blob x) throws SQLException {
delegate.setBlob(parameterIndex, x);
}
public void setClob(int parameterIndex, Clob x) throws SQLException {
delegate.setClob(parameterIndex, x);
}
public void setArray(int parameterIndex, Array x) throws SQLException {
delegate.setArray(parameterIndex, x);
}
public ResultSetMetaData getMetaData() throws SQLException {
return delegate.getMetaData();
}
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
delegate.setDate(parameterIndex, x, cal);
}
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
delegate.setTime(parameterIndex, x, cal);
}
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
delegate.setTimestamp(parameterIndex, x, cal);
}
public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
delegate.setNull(parameterIndex, sqlType, typeName);
}
public void setURL(int parameterIndex, URL x) throws SQLException {
delegate.setURL(parameterIndex, x);
}
public ParameterMetaData getParameterMetaData() throws SQLException {
return delegate.getParameterMetaData();
}
public void setRowId(int parameterIndex, RowId x) throws SQLException {
delegate.setRowId(parameterIndex, x);
}
public void setNString(int parameterIndex, String value) throws SQLException {
delegate.setNString(parameterIndex, value);
}
public void setNCharacterStream(int parameterIndex, Reader value, long length)
throws SQLException {
delegate.setNCharacterStream(parameterIndex, value, length);
}
public void setNClob(int parameterIndex, NClob value) throws SQLException {
delegate.setNClob(parameterIndex, value);
}
public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
delegate.setClob(parameterIndex, reader, length);
}
public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
delegate.setBlob(parameterIndex, inputStream, length);
}
public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
delegate.setNClob(parameterIndex, reader, length);
}
public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
delegate.setSQLXML(parameterIndex, xmlObject);
}
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
throws SQLException {
delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
}
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
delegate.setAsciiStream(parameterIndex, x, length);
}
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
delegate.setBinaryStream(parameterIndex, x, length);
}
public void setCharacterStream(int parameterIndex, Reader reader, long length)
throws SQLException {
delegate.setCharacterStream(parameterIndex, reader, length);
}
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
delegate.setAsciiStream(parameterIndex, x);
}
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
delegate.setBinaryStream(parameterIndex, x);
}
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
delegate.setCharacterStream(parameterIndex, reader);
}
public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
delegate.setNCharacterStream(parameterIndex, value);
}
public void setClob(int parameterIndex, Reader reader) throws SQLException {
delegate.setClob(parameterIndex, reader);
}
public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
delegate.setBlob(parameterIndex, inputStream);
}
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
delegate.setNClob(parameterIndex, reader);
}
public ResultSet executeQuery(String sql) throws SQLException {
return delegate.executeQuery(sql);
}
public int executeUpdate(String sql) throws SQLException {
return delegate.executeUpdate(sql);
}
public void close() throws SQLException {
delegate.close();
}
public int getMaxFieldSize() throws SQLException {
return delegate.getMaxFieldSize();
}
public void setMaxFieldSize(int max) throws SQLException {
delegate.setMaxFieldSize(max);
}
public int getMaxRows() throws SQLException {
return delegate.getMaxRows();
}
public void setMaxRows(int max) throws SQLException {
delegate.setMaxRows(max);
}
public void setEscapeProcessing(boolean enable) throws SQLException {
delegate.setEscapeProcessing(enable);
}
public int getQueryTimeout() throws SQLException {
return delegate.getQueryTimeout();
}
public void setQueryTimeout(int seconds) throws SQLException {
delegate.setQueryTimeout(seconds);
}
public void cancel() throws SQLException {
delegate.cancel();
}
public SQLWarning getWarnings() throws SQLException {
return delegate.getWarnings();
}
public void clearWarnings() throws SQLException {
delegate.clearWarnings();
}
public void setCursorName(String name) throws SQLException {
delegate.setCursorName(name);
}
public boolean execute(String sql) throws SQLException {
return delegate.execute(sql);
}
public ResultSet getResultSet() throws SQLException {
return delegate.getResultSet();
}
public int getUpdateCount() throws SQLException {
return delegate.getUpdateCount();
}
public boolean getMoreResults() throws SQLException {
return delegate.getMoreResults();
}
public void setFetchDirection(int direction) throws SQLException {
delegate.setFetchDirection(direction);
}
public int getFetchDirection() throws SQLException {
return delegate.getFetchDirection();
}
public void setFetchSize(int rows) throws SQLException {
delegate.setFetchSize(rows);
}
public int getFetchSize() throws SQLException {
return delegate.getFetchSize();
}
public int getResultSetConcurrency() throws SQLException {
return delegate.getResultSetConcurrency();
}
public int getResultSetType() throws SQLException {
return delegate.getResultSetType();
}
public void addBatch(String sql) throws SQLException {
delegate.addBatch(sql);
}
public void clearBatch() throws SQLException {
delegate.clearBatch();
}
public int[] executeBatch() throws SQLException {
return delegate.executeBatch();
}
public Connection getConnection() throws SQLException {
return delegate.getConnection();
}
public boolean getMoreResults(int current) throws SQLException {
return delegate.getMoreResults(current);
}
public ResultSet getGeneratedKeys() throws SQLException {
return delegate.getGeneratedKeys();
}
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.executeUpdate(sql, autoGeneratedKeys);
}
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
return delegate.executeUpdate(sql, columnIndexes);
}
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
return delegate.executeUpdate(sql, columnNames);
}
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
return delegate.execute(sql, autoGeneratedKeys);
}
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return delegate.execute(sql, columnIndexes);
}
public boolean execute(String sql, String[] columnNames) throws SQLException {
return delegate.execute(sql, columnNames);
}
public int getResultSetHoldability() throws SQLException {
return delegate.getResultSetHoldability();
}
public boolean isClosed() throws SQLException {
return delegate.isClosed();
}
public void setPoolable(boolean poolable) throws SQLException {
delegate.setPoolable(poolable);
}
public boolean isPoolable() throws SQLException {
return delegate.isPoolable();
}
public <T> T unwrap(Class<T> iface) throws SQLException {
return delegate.unwrap(iface);
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return delegate.isWrapperFor(iface);
}
}
@@ -252,6 +252,8 @@ public class DefaultAutoTuneService implements AutoTuneService {
}
Thread.sleep(waitMillis);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
logger.warn("Error while sleeping after System.gc() request.", e);
}
}
@@ -6,7 +6,6 @@ import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.cache.ServerCacheOptions;
import com.avaje.ebean.common.SpiContainer;
import com.avaje.ebean.config.ContainerConfig;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.PropertyMap;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.UnderscoreNamingConvention;
@@ -18,10 +17,10 @@ import com.avaje.ebeaninternal.server.cache.DefaultServerCacheFactory;
import com.avaje.ebeaninternal.server.cache.DefaultServerCacheManager;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.lib.ShutdownManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourceAlert;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePoolListener;
import com.avaje.ebeaninternal.server.lib.sql.SimpleDataSourceAlert;
import org.avaje.datasource.DataSourceAlertFactory;
import org.avaje.datasource.DataSourceConfig;
import org.avaje.datasource.DataSourceFactory;
import org.avaje.datasource.DataSourcePoolListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -109,7 +108,7 @@ public class DefaultContainer implements SpiContainer {
serverConfig.getDatabasePlatform().setDbEncrypt(serverConfig.getDbEncrypt());
}
// inform the NamingConvention of the associated DatabasePlaform
// inform the NamingConvention of the associated DatabasePlatform
serverConfig.getNamingConvention().setDatabasePlatform(serverConfig.getDatabasePlatform());
ServerCacheManager cacheManager = getCacheManager(serverConfig);
@@ -235,8 +234,7 @@ public class DefaultContainer implements SpiContainer {
*/
private void setNamingConvention(ServerConfig config) {
if (config.getNamingConvention() == null) {
UnderscoreNamingConvention nc = new UnderscoreNamingConvention();
config.setNamingConvention(nc);
config.setNamingConvention(new UnderscoreNamingConvention());
}
}
@@ -247,7 +245,6 @@ public class DefaultContainer implements SpiContainer {
DatabasePlatform dbPlatform = config.getDatabasePlatform();
if (dbPlatform == null) {
DatabasePlatformFactory factory = new DatabasePlatformFactory();
DatabasePlatform db = factory.create(config);
config.setDatabasePlatform(db);
@@ -260,8 +257,7 @@ public class DefaultContainer implements SpiContainer {
*/
private void setDataSource(ServerConfig config) {
if (config.getDataSource() == null) {
DataSource ds = getDataSourceFromConfig(config);
config.setDataSource(ds);
config.setDataSource(getDataSourceFromConfig(config));
}
}
@@ -295,18 +291,32 @@ public class DefaultContainer implements SpiContainer {
return null;
}
DataSourceAlert notify = new SimpleDataSourceAlert();
DataSourcePoolListener listener = createListener(config, dsConfig);
DataSourceFactory factory = config.service(DataSourceFactory.class);
if (factory == null) {
throw new IllegalStateException("No DataSourceFactory service implementation found in class path."
+ " Probably missing dependency to avaje-datasource?");
}
return new DataSourcePool(notify, config.getName(), dsConfig, listener);
DataSourceAlertFactory alertFactory = config.service(DataSourceAlertFactory.class);
if (alertFactory != null) {
dsConfig.setAlert(alertFactory.createAlert());
}
attachListener(config, dsConfig);
return factory.createPool(config.getName(), dsConfig);
}
/**
* Create and return a DataSourcePoolListener if it has been specified.
* Create and attach a DataSourcePoolListener if it has been specified via properties and there is not one already attached.
*/
private DataSourcePoolListener createListener(ServerConfig config, DataSourceConfig dsConfig) {
String poolListener = dsConfig.getPoolListener();
return poolListener != null ? (DataSourcePoolListener) config.getClassLoadConfig().newInstance(poolListener) : null;
private void attachListener(ServerConfig config, DataSourceConfig dsConfig) {
if (dsConfig.getListener() == null) {
String poolListener = dsConfig.getPoolListener();
if (poolListener != null) {
dsConfig.setListener((DataSourcePoolListener)config.getClassLoadConfig().newInstance(poolListener));
}
}
}
/**
@@ -315,7 +325,7 @@ public class DefaultContainer implements SpiContainer {
* If autoCommit is true this could be a real problem.
* </p>
* <p>
* If the Isolation level is not READ_COMMITED then optimistic concurrency
* If the Isolation level is not READ_COMMITTED then optimistic concurrency
* checking may not work as expected.
* </p>
*/
@@ -336,12 +346,9 @@ public class DefaultContainer implements SpiContainer {
Connection c = null;
try {
c = serverConfig.getDataSource().getConnection();
if (c.getAutoCommit()) {
String m = "DataSource [" + serverConfig.getName() + "] has autoCommit defaulting to true!";
logger.warn(m);
logger.warn("DataSource [{}] has autoCommit defaulting to true!", serverConfig.getName());
}
return true;
} catch (SQLException ex) {
@@ -352,7 +359,7 @@ public class DefaultContainer implements SpiContainer {
try {
c.close();
} catch (SQLException ex) {
logger.error(null, ex);
logger.error("Error closing connection", ex);
}
}
}
@@ -398,9 +398,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) {
logger.debug("Shutting down EbeanServer " + getName());
logger.debug("Shutting down EbeanServer {}", serverName);
if (shutdown) {
// Already shutdown
// already shutdown
return;
}
shutdownPlugins();
@@ -411,6 +411,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
// shutdown DataSource (if its an Ebean one)
transactionManager.shutdown(shutdownDataSource, deregisterDriver);
shutdown = true;
if (shutdownDataSource) {
// deregister the DataSource in case ServerConfig is re-used
serverConfig.setDataSource(null);
}
}
private void shutdownPlugins() {
@@ -29,7 +29,7 @@ import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.expression.DefaultExpressionFactory;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.persist.DefaultPersister;
import com.avaje.ebeaninternal.server.query.CQueryEngine;
@@ -361,7 +361,7 @@ public class InternalConfiguration {
return true;
}
DataSource dataSource = serverConfig.getDataSource();
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).getAutoCommit();
return dataSource instanceof DataSourcePool && ((DataSourcePool) dataSource).isAutoCommit();
}
/**
@@ -1,6 +1,5 @@
package com.avaje.ebeaninternal.server.deploy.meta;
import com.avaje.ebean.annotation.DocStoreEmbedded;
import com.avaje.ebeaninternal.server.deploy.BeanCascadeInfo;
import com.avaje.ebeaninternal.server.deploy.BeanTable;
@@ -140,8 +139,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
/**
* Set DocStoreEmbedded deployment information.
*/
public void setDocStoreEmbedded(DocStoreEmbedded embedded) {
docStoreDoc = embedded.doc();
public void setDocStoreEmbedded(String embeddedDoc) {
this.docStoreDoc = embeddedDoc;
}
public String getDocStoreDoc() {
@@ -91,9 +91,13 @@ public class AnnotationFields extends AnnotationParser {
prop.setEmbedded();
}
DocEmbedded docEmbedded = get(prop, DocEmbedded.class);
if (docEmbedded != null) {
prop.setDocStoreEmbedded(docEmbedded.doc());
}
DocStoreEmbedded docStoreEmbedded = get(prop, DocStoreEmbedded.class);
if (docStoreEmbedded != null) {
prop.setDocStoreEmbedded(docStoreEmbedded);
prop.setDocStoreEmbedded(docStoreEmbedded.doc());
}
if (prop instanceof DeployBeanPropertyAssocOne<?>) {
@@ -16,6 +16,7 @@ import com.avaje.ebean.search.TextSimple;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
import com.avaje.ebeaninternal.api.SpiQuery;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -365,6 +366,13 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
return new IdInExpression(idList);
}
/**
* Id IN a list of id values.
*/
public Expression idIn(Object... idValues) {
return new IdInExpression(Arrays.asList(idValues));
}
/**
* All Equal - Map containing property names and their values.
* <p>
@@ -33,7 +33,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
protected final Query<T> query;
protected final ExpressionList<T> parentExprList;
private final ExpressionList<T> parentExprList;
protected transient ExpressionFactory expr;
@@ -57,11 +57,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
this(query, query.getExpressionFactory(), parentExprList);
}
public DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList) {
DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList) {
this(query, expr, parentExprList, new ArrayList<SpiExpression>());
}
protected DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList, List<SpiExpression> list) {
DefaultExpressionList(Query<T> query, ExpressionFactory expr, ExpressionList<T> parentExprList, List<SpiExpression> list) {
this(query, expr, parentExprList, list, false);
}
@@ -219,7 +219,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
@Override
public Query<T> includeSoftDeletes() {
return query.includeSoftDeletes();
return setIncludeSoftDeletes();
}
@Override
public Query<T> setIncludeSoftDeletes() {
return query.setIncludeSoftDeletes();
}
@Override
@@ -667,6 +672,12 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return this;
}
@Override
public ExpressionList<T> idIn(Object... idValues) {
add(expr.idIn(idValues));
return this;
}
@Override
public ExpressionList<T> idIn(List<?> idList) {
add(expr.idIn(idList));
@@ -292,7 +292,12 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
@Override
public Query<T> includeSoftDeletes() {
return exprList.includeSoftDeletes();
return setIncludeSoftDeletes();
}
@Override
public Query<T> setIncludeSoftDeletes() {
return exprList.setIncludeSoftDeletes();
}
@Override
@@ -482,6 +487,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
return exprList.idEq(value);
}
@Override
public ExpressionList<T> idIn(Object... idValues) {
return exprList.idIn(idValues);
}
@Override
public ExpressionList<T> idIn(List<?> idValues) {
return exprList.idIn(idValues);
@@ -1,201 +0,0 @@
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.
* <p>
* All thread safety controlled externally (by PooledConnectionQueue).
* </p>
* <p>
* 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.
* </p>
*
* @author rbygrave
*/
class BusyConnectionBuffer {
private static final Logger logger = LoggerFactory.getLogger(BusyConnectionBuffer.class);
private PooledConnection[] slots;
private final 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];
//noinspection StatementWithEmptyBody
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?");
}
}
@@ -1,27 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
/**
* Listener for notifications about the DataSource such as when the DataSource
* goes down, up or gets close to it's maximum size.
* <p>
* The intention is to send email notifications to an administrator (or similar)
* when these events occur on the DataSource.
* </p>
*/
public interface DataSourceAlert {
/**
* Send an alert to say the dataSource is back up.
*/
void dataSourceUp(String dataSourceName);
/**
* Send an alert to say the dataSource is down.
*/
void dataSourceDown(String dataSourceName);
/**
* Send an alert to say the dataSource is getting close to its max size.
*/
void dataSourceWarning(String subject, String msg);
}
File diff suppressed because it is too large Load Diff
@@ -1,34 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.Connection;
/**
* A {@link DataSourcePool} listener which allows you to hook on the
* borrow/return process of getting or returning connections from the pool.
* <p>
* In the configuration use the poolListener key to configure which listener to
* use.
* </p>
* <p>
* Example: datasource.ora10.poolListener=my.very.fancy.PoolListener
* </p>
* <p>
* <p>
* Notice: This listener only works if you are using the default Avaje
* {@link DataSourcePool}.
* </p>
*/
public interface DataSourcePoolListener {
/**
* Called after a connection has been retrieved from the connection pool
*/
void onAfterBorrowConnection(Connection c);
/**
* Called before a connection will be put back to the connection pool
*/
void onBeforeReturnConnection(Connection c);
}
@@ -1,97 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
/**
* Represents aggregated statistics collected from the DataSourcePool.
* <p>
* The goal is to present insight into the overload load of the DataSourcePool.
* These statistics can be collected and reported regularly to show load over
* time.
* </p>
* <p>
* Each pooled connection collects statistics. When a pooled connection is fully
* closed it can report it's statistics to the pool to be included as part of
* the collected statistics.
* </p>
*/
public class DataSourcePoolStatistics {
private final long collectionStart;
private final long count;
private final long errorCount;
private final long hwmMicros;
private final long totalMicros;
/**
* No statistics collected.
*/
public DataSourcePoolStatistics() {
this.collectionStart = 0;
this.count = 0;
this.errorCount = 0;
this.hwmMicros = 0;
this.totalMicros = 0;
}
/**
* Construct with statistics collected.
*/
public DataSourcePoolStatistics(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) {
this.collectionStart = collectionStart;
this.count = count;
this.errorCount = errorCount;
this.hwmMicros = hwmMicros;
this.totalMicros = totalMicros;
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + totalMicros + "] hwmMicros[" + hwmMicros
+ "] avgMicros[" + getAvgMicros() + "]";
}
/**
* Return the start time this set of statistics was collected from.
*/
public long getCollectionStart() {
return collectionStart;
}
/**
* Return the total number of 'get connection' requests.
*/
public long getCount() {
return count;
}
/**
* Return the number of SQLExceptions reported.
*/
public long getErrorCount() {
return errorCount;
}
/**
* Return the high water mark for the duration a connection was busy/used.
*/
public long getHwmMicros() {
return hwmMicros;
}
/**
* Return the aggregate time connections were busy/used.
*/
public long getTotalMicros() {
return totalMicros;
}
/**
* Return the average time connections were busy/used.
*/
public long getAvgMicros() {
return (totalMicros == 0) ? 0 : totalMicros / count;
}
}
@@ -1,390 +0,0 @@
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.
* <p>
* 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.
* </p>
*/
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);
}
}
@@ -1,327 +0,0 @@
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.
* <p>
* 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.
* </p>
*/
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();
}
}
@@ -1,106 +0,0 @@
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.
* <p>
* All thread safety controlled externally (by PooledConnectionQueue).
* </p>
*/
class FreeConnectionBuffer {
private static final Logger logger = LoggerFactory.getLogger(FreeConnectionBuffer.class);
/**
* Buffer oriented for add and remove.
*/
private final LinkedList<PooledConnection> freeBuffer = new LinkedList<PooledConnection>();
protected FreeConnectionBuffer() {
}
protected int size() {
return freeBuffer.size();
}
protected boolean isEmpty() {
return freeBuffer.isEmpty();
}
/**
* Add connection to the free list.
*/
protected void add(PooledConnection pc) {
freeBuffer.addLast(pc);
}
/**
* Remove a connection from the free list.
*/
protected PooledConnection remove() {
return freeBuffer.removeFirst();
}
/**
* Close all connections in this buffer.
*/
protected void closeAll(boolean logErrors) {
// create a temporary list
List<PooledConnection> tempList = new ArrayList<PooledConnection>(freeBuffer.size());
// add all the connections into it
for (PooledConnection c : freeBuffer) {
tempList.add(c);
}
// clear the buffer (in case it takes some time to close these connections).
freeBuffer.clear();
logger.debug("... closing all {} connections from the free list with logErrors: {}", tempList.size(), logErrors);
for (int i = 0; i < tempList.size(); i++) {
PooledConnection pooledConnection = tempList.get(i);
logger.debug("... closing {} of {} connections from the free list", i, tempList.size());
pooledConnection.closeConnectionFully(logErrors);
}
}
/**
* Trim any inactive connections that have not been used since usedSince.
*/
protected int trim(long usedSince, long createdSince) {
int trimCount = 0;
Iterator<PooledConnection> iterator = freeBuffer.iterator();
while (iterator.hasNext()) {
PooledConnection pooledConnection = iterator.next();
if (pooledConnection.shouldTrim(usedSince, createdSince)) {
iterator.remove();
pooledConnection.closeConnectionFully(true);
trimCount++;
}
}
return trimCount;
}
/**
* Collect the load statistics from all the free connections.
*/
protected void collectStatistics(LoadValues values, boolean reset) {
for (PooledConnection c : freeBuffer) {
values.plus(c.getStatistics().getValues(reset));
}
}
}
@@ -1,970 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebeaninternal.jdbc.ConnectionDelegator;
/**
* Is a connection that belongs to a DataSourcePool.
* <p/>
* <p>
* It is designed to be part of DataSourcePool. Closing the connection puts it
* back into the pool.
* </p>
* <p/>
* <p>
* It defaults autoCommit and Transaction Isolation to the defaults of the
* DataSourcePool.
* </p>
* <p/>
* <p>
* It has caching of Statements and PreparedStatements. Remembers the last
* statement that was executed. Keeps statistics on how long it is in use.
* </p>
*/
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.
* <p>
* This means it should skip the suspected connection pool leak checking.
* </p>
*/
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 final 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.
* <p>
* close() will return the connection back to the pool , while
* closeDestroy() will close() the underlining connection properly.
* </p>
*/
public PooledConnection(DataSourcePool pool, int uniqueId, Connection connection) {
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 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 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.
* <p>
* The logErrors parameter exists so that expected errors are not logged
* such as when the database is known to be down.
* </p>
*
* @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 [" + getFullDescription() + "]", ex);
}
}
}
/**
* 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.
* <p>
* Any PooledConnection that has an error is checked to make sure it works
* before it is placed back into the connection pool.
* </p>
*/
public void addError(Throwable throwable) {
hadErrors = true;
}
/**
* close the connection putting it back into the connection pool.
* <p>
* 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).
* </p>
* <p>
* To close the connection fully use closeConnectionFully().
* </p>
*/
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
//noinspection MagicConstant
if (connection.getTransactionIsolation() != pool.getTransactionIsolation()) {
//noinspection MagicConstant
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;
}
return exceedsMaxAge(maxAgeMillis);
}
/**
* 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.
* <p>
* Used to detect busy connections that could be leaks.
* </p>
*/
public long getStartUseTime() {
return startUseTime;
}
/**
* Returns the time the connection was last used.
* <p>
* Used to close connections that have been idle for some time. Typically 5
* minutes.
* </p>
*/
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.
* <p>
* Note with addBatch() this will not really work.
* </p>
*/
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<String, Class<?>> getTypeMap() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getTypeMap()");
}
return connection.getTypeMap();
}
public SQLWarning getWarnings() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "getWarnings()");
}
return connection.getWarnings();
}
public boolean isClosed() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isClosed()");
}
return connection.isClosed();
}
public boolean isReadOnly() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "isReadOnly()");
}
return connection.isReadOnly();
}
public String nativeSQL(String sql) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "nativeSQL()");
}
lastStatement = sql;
return connection.nativeSQL(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()");
}
lastStatement = sql;
return connection.prepareCall(sql);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurreny) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "prepareCall()");
}
lastStatement = sql;
return connection.prepareCall(sql, resultSetType, resultSetConcurreny);
}
public void rollback() throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "rollback()");
}
try {
status = STATUS_ENDED;
connection.rollback();
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setAutoCommit()");
}
try {
connection.setAutoCommit(autoCommit);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public void setCatalog(String catalog) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setCatalog()");
}
connection.setCatalog(catalog);
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
if (status == STATUS_IDLE) {
throw new SQLException(IDLE_CONNECTION_ACCESSED_ERROR + "setTypeMap()");
}
connection.setTypeMap(map);
}
public Savepoint setSavepoint() throws SQLException {
try {
return connection.setSavepoint();
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public Savepoint setSavepoint(String savepointName) throws SQLException {
try {
return connection.setSavepoint(savepointName);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public void rollback(Savepoint sp) throws SQLException {
try {
connection.rollback(sp);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public void releaseSavepoint(Savepoint sp) throws SQLException {
try {
connection.releaseSavepoint(sp);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public void setHoldability(int i) throws SQLException {
try {
connection.setHoldability(i);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public int getHoldability() throws SQLException {
try {
return connection.getHoldability();
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public Statement createStatement(int i, int x, int y) throws SQLException {
try {
return connection.createStatement(i, x, y);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public PreparedStatement prepareStatement(String s, int i, int x, int y) throws SQLException {
try {
return connection.prepareStatement(s, i, x, y);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public PreparedStatement prepareStatement(String s, int[] i) throws SQLException {
try {
return connection.prepareStatement(s, i);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public PreparedStatement prepareStatement(String s, String[] s2) throws SQLException {
try {
return connection.prepareStatement(s, s2);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
public CallableStatement prepareCall(String s, int i, int x, int y) throws SQLException {
try {
return connection.prepareCall(s, i, x, y);
} catch (SQLException ex) {
addError(ex);
throw ex;
}
}
/**
* Returns the method that created the connection.
* <p>
* Used to help finding connection pool leaks.
* </p>
*/
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)) {
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.<init>")) {
// creating connection on future...
return true;
} else if (methodLine.startsWith("com.avaje.ebeaninternal.server.query.Callable")) {
// it is a future task being executed...
return false;
} else {
return methodLine.startsWith("com.avaje.ebeaninternal");
}
}
/**
* Set the stack trace to help find connection pool leaks.
*/
protected void setStackTrace(StackTraceElement[] stackTrace) {
this.stackTrace = stackTrace;
}
/**
* Return the stackTrace as a String for logging purposes.
*/
public String getStackTraceAsString() {
StackTraceElement[] stackTrace = getStackTrace();
if (stackTrace == null) {
return "";
}
return Arrays.toString(stackTrace);
}
/**
* Return the full stack trace that got the connection from the pool. You
* could use this if getCreatedByMethod() doesn't work for you.
*/
public StackTraceElement[] getStackTrace() {
if (stackTrace == null) {
return null;
}
// filter off the top of the stack that we are not interested in
ArrayList<StackTraceElement> filteredList = new ArrayList<StackTraceElement>();
boolean include = false;
for (int i = 0; i < stackTrace.length; i++) {
if (!include && !skipElement(stackTrace[i].toString())) {
include = true;
}
if (include && filteredList.size() < maxStackTrace) {
filteredList.add(stackTrace[i]);
}
}
return filteredList.toArray(new StackTraceElement[filteredList.size()]);
}
}
@@ -1,532 +0,0 @@
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.
* <p>
* This is typically done when a database down event occurs.
* </p>
*/
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.
* <p>
* These connections are considered to have leaked from the connection pool.
* </p>
* <p>
* 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.
* </p>
*/
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.
* <p>
* This is called whenever the pool grows in size (towards the max limit).
* </p>
*/
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();
}
}
}
@@ -1,160 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* Collects load statistics for a PooledConnection.
*/
public class PooledConnectionStatistics {
private final AtomicLong count = new AtomicLong();
private final AtomicLong errorCount = new AtomicLong();
private final AtomicLong hwmNanos = new AtomicLong();
private final AtomicLong totalNanos = new AtomicLong();
private final AtomicLong collectionStart;
public PooledConnectionStatistics() {
this.collectionStart = new AtomicLong(System.currentTimeMillis());
}
/**
* Add statistics from another collector.
*/
public void add(PooledConnectionStatistics other) {
errorCount.addAndGet(other.getErrorCount());
totalNanos.addAndGet(other.totalNanos.get());
count.addAndGet(other.getCount());
final long otherHwm = other.hwmNanos.get();
if (otherHwm > hwmNanos.get()) {
hwmNanos.set(otherHwm);
}
}
/**
* Add some time duration to the statistics.
*/
public void add(long durationNanos, boolean hasError) {
// This will be done in pretty much single threaded fashion
// as the Connections generally are not shared across threads
if (hasError) {
errorCount.incrementAndGet();
}
count.incrementAndGet();
totalNanos.addAndGet(durationNanos);
if (durationNanos > hwmNanos.get()) {
hwmNanos.set(durationNanos);
}
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + getTotalMicros() + "] hwmMicros[" + getHwmMicros() + "]";
}
public long getCollectionStart() {
return collectionStart.get();
}
public long getCount() {
return count.get();
}
public long getErrorCount() {
return errorCount.get();
}
public long getTotalMicros() {
return TimeUnit.MICROSECONDS.convert(totalNanos.get(), TimeUnit.NANOSECONDS);
}
public long getHwmMicros() {
return TimeUnit.MICROSECONDS.convert(hwmNanos.get(), TimeUnit.NANOSECONDS);
}
/**
* Get the current values and reset the statistics if necessary.
*/
public LoadValues getValues(boolean reset) {
LoadValues value = new LoadValues(collectionStart.get(), count.get(), errorCount.get(), getHwmMicros(), getTotalMicros());
if (reset) {
count.set(0);
errorCount.set(0);
hwmNanos.set(0);
totalNanos.set(0);
collectionStart.set(System.currentTimeMillis());
}
return value;
}
/**
* Values representing the load or activity of a PooledConnection.
* <p>
* These are aggregated up to get a total for the DataSourcePool.
* </p>
*/
public static class LoadValues {
private long collectionStart;
private long count;
private long errorCount;
private long hwmMicros;
private long totalMicros;
public LoadValues() {
}
public LoadValues(long collectionStart, long count, long errorCount, long hwmMicros, long totalMicros) {
this.collectionStart = collectionStart;
this.count = count;
this.errorCount = errorCount;
this.hwmMicros = hwmMicros;
this.totalMicros = totalMicros;
}
public void plus(LoadValues additional) {
collectionStart = (collectionStart == 0) ? additional.collectionStart : Math.min(collectionStart, additional.collectionStart);
count += additional.count;
errorCount += additional.errorCount;
hwmMicros = Math.max(hwmMicros, additional.hwmMicros);
totalMicros += additional.totalMicros;
}
public String toString() {
return "count[" + count + "] errors[" + errorCount + "] totalMicros[" + totalMicros + "] hwmMicros[" + hwmMicros + "] avgMicros[" + getAvgMicros() + "]";
}
public long getCollectionStart() {
return collectionStart;
}
public long getCount() {
return count;
}
public long getErrorCount() {
return errorCount;
}
public long getHwmMicros() {
return hwmMicros;
}
public long getTotalMicros() {
return totalMicros;
}
public long getAvgMicros() {
return (count == 0) ? 0 : totalMicros / count;
}
}
}
@@ -1,98 +0,0 @@
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(":");
return v.substring(1, p);
}
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]);
for (int i = 1; i < (msgbytes.length + 1); i++) {
encbytes[i] = az(msgbytes[i - 1], (oa[(i + key) % 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;
}
}
@@ -1,183 +0,0 @@
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 LinkedHashMap<String, ExtendedPreparedStatement> {
private static final Logger logger = LoggerFactory.getLogger(PstmtCache.class);
static final long serialVersionUID = -3096406924865550697L;
/**
* The name of the cache, for tracing purposes.
*/
protected final String cacheName;
/**
* The maximum size of the cache. When this is exceeded the oldest entry is removed.
*/
private final int maxSize;
/**
* The total number of entries removed from this cache.
*/
private int removeCounter;
/**
* The number of get hits.
*/
private int hitCounter;
/**
* The number of get() misses.
*/
private int missCounter;
/**
* The number of puts into this cache.
*/
private int putCounter;
public PstmtCache(String cacheName, int maxCacheSize) {
// note = access ordered list. This is what gives it the LRU order
super(maxCacheSize * 3, 0.75f, true);
this.cacheName = cacheName;
this.maxSize = maxCacheSize;
}
/**
* Return a summary description of this cache.
*/
public String getDescription() {
return "size[" + size() + "] max[" + maxSize + "] hits[" + hitCounter + "] miss[" + missCounter + "] hitRatio[" + getHitRatio() + "] removes[" + removeCounter + "]";
}
/**
* returns the current maximum size of the cache.
*/
public int getMaxSize() {
return maxSize;
}
/**
* Gets the hit ratio. A number between 0 and 100 indicating the number of
* hits to misses. A number approaching 100 is desirable.
*/
public int getHitRatio() {
if (hitCounter == 0) {
return 0;
} else {
return hitCounter * 100 / (hitCounter + missCounter);
}
}
/**
* The total number of hits against this cache.
*/
public int getHitCounter() {
return hitCounter;
}
/**
* The total number of misses against this cache.
*/
public int getMissCounter() {
return missCounter;
}
/**
* The total number of puts against this cache.
*/
public int getPutCounter() {
return putCounter;
}
/**
* Try to add the returning statement to the cache. If there is already a
* matching ExtendedPreparedStatement in the cache return false else add
* the statement to the cache and return true.
*/
public boolean returnStatement(ExtendedPreparedStatement pstmt) {
ExtendedPreparedStatement alreadyInCache = super.get(pstmt.getCacheKey());
if (alreadyInCache != null) {
return false;
}
// add the returning prepared statement to the cache.
// Note that the LRUCache will automatically close fully old unused
// PStmts when the cache has hit its maximum size.
put(pstmt.getCacheKey(), pstmt);
return true;
}
/**
* additionally maintains hit and miss statistics.
*/
public ExtendedPreparedStatement get(Object key) {
ExtendedPreparedStatement o = super.get(key);
if (o == null) {
missCounter++;
} else {
hitCounter++;
}
return o;
}
/**
* additionally maintains hit and miss statistics.
*/
public ExtendedPreparedStatement remove(Object key) {
ExtendedPreparedStatement o = super.remove(key);
if (o == null) {
missCounter++;
} else {
hitCounter++;
}
return o;
}
/**
* additionally maintains put counter statistics.
*/
public ExtendedPreparedStatement put(String key, ExtendedPreparedStatement value) {
putCounter++;
return super.put(key, value);
}
/**
* will check to see if we need to remove entries and
* if so call the cacheCleanup.cleanupEldestLRUCacheEntry() if
* one has been set.
*/
protected boolean removeEldestEntry(Map.Entry<String, ExtendedPreparedStatement> eldest) {
if (size() < maxSize) {
return false;
}
removeCounter++;
try {
ExtendedPreparedStatement pstmt = eldest.getValue();
pstmt.closeDestroy();
} catch (SQLException e) {
logger.error("Error closing ExtendedPreparedStatement", e);
}
return true;
}
}
@@ -1,106 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import com.avaje.ebeaninternal.server.lib.util.MailEvent;
import com.avaje.ebeaninternal.server.lib.util.MailListener;
import com.avaje.ebeaninternal.server.lib.util.MailMessage;
import com.avaje.ebeaninternal.server.lib.util.MailSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple smtp email alert that sends a email message on dataSourceDown and
* dataSourceUp etc.
* <ul>
* <li>alert.fromuser = the from user name
* <li>alert.fromemail = the from email account
* <li>alert.toemail = comma delimited list of email accounts to email
* <li>alert.mailserver = the smpt server name
* </ul>
*/
public class SimpleDataSourceAlert implements DataSourceAlert, MailListener {
private static final Logger logger = LoggerFactory.getLogger(SimpleDataSourceAlert.class);
private static final String alertMailServerName = System.getProperty("ebean.datasource.alert.mailserver");
private static final String fromUser = System.getProperty("ebean.datasource.alert.fromUser");
private static final String fromEmail = System.getProperty("ebean.datasource.alert.fromEmail");
private static final String toEmail = System.getProperty("ebean.datasource.alert.toEmail");
/**
* Create a SimpleAlerter.
*/
public SimpleDataSourceAlert() {
}
/**
* If the email failed then log the error.
*/
public void handleEvent(MailEvent event) {
Throwable e = event.getError();
if (e != null) {
logger.error(null, e);
}
}
/**
* Send the dataSource down alert.
*/
@Override
public void dataSourceDown(String dataSourceName) {
String msg = getSubject(true, dataSourceName);
sendMessage(msg, msg);
}
/**
* Send the dataSource up alert.
*/
@Override
public void dataSourceUp(String dataSourceName) {
String msg = getSubject(false, dataSourceName);
sendMessage(msg, msg);
}
/**
* Send the warning message.
*/
@Override
public void dataSourceWarning(String subject, String msg) {
sendMessage(subject, msg);
}
private String getSubject(boolean isDown, String dsName) {
String msg = "The DataSource " + dsName;
if (isDown) {
msg += " is DOWN!!";
} else {
msg += " is UP.";
}
return msg;
}
private void sendMessage(String subject, String msg) {
if (alertMailServerName == null) {
return;
}
MailMessage data = new MailMessage();
data.setSender(fromUser, fromEmail);
data.addBodyLine(msg);
data.setSubject(subject);
String[] toList = toEmail.split(",");
if (toList.length == 0) {
logger.error("alert.toemail has not been set?");
} else {
for (int i = 0; i < toList.length; i++) {
data.addRecipient(null, toList[i].trim());
}
MailSender sender = new MailSender(alertMailServerName);
sender.setMailListener(this);
sender.sendInBackground(data);
}
}
}
@@ -1,67 +0,0 @@
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.
* <p>Returned value is one of NONE, READ_COMMITTED,READ_UNCOMMITTED,
* REPEATABLE_READ or SERIALIZABLE.</p>
*
* @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.");
}
}
}
@@ -1,20 +0,0 @@
FATAL_ERROR=ERROR: A fatal error has occured. Check the error log. {0}
DATASOURCE_OK=DataSource {0} working ok.
SHUTTING_DOWN=DataSource shutdown all datasources in [{0}]
SHUT_DOWN_FINISHED=DataSource shutdown has finished.
CANT_FIND_PROPS=ERROR: Can't find the datasource props file.
ERROR_CREATING_FACTORY=ERROR: An error occured when creating the DataSourceFactory {0}.
CANT_SHUTDOWN_TYPE=WARN: Can't shutdown DataSource objects of this type {0}.
DB_DRIVER_NOTFOUND=ERROR: The JDBC Driver {0} can't be found.
POOL_IN_SHUTDOWN=ERROR: Trying to use the pool while it is shutting down.
WAIT_TIME_EXCEEDED=ERROR: Wait time {0} for connection exceeded. {1}.
SHUTDOWN_START=DataSource [{0}] Shutting down.
SHUTDOWN_LEAK=A Connection leak has been detected on shutdown {0}.
SHUTDOWN_END=DataSource [{0}] Shutdown ended.
METHOD_NOT_SUPPORTED=ERROR: this method is not supported.
SET_ALERT=WARN: Alert {0} has been set.
MISSING_PARAMETER=ERROR: A parameter {0} is missing from the props file.
IDLE_CONNECTION_ACCESSED=Pooled Connection has been accessed whilst idle in the pool, via method:
DEFAULT_DS_NOT_SPECIFIED=ERROR: No default dataSource has been specified.
@@ -1,12 +0,0 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
<TITLE>AvajeLib</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
Enhanced JDBC objects and connection pool.
<P>Provides Database meta data objects, Robust connection pooling, PreparedStatement Caching.
</P>
</Body>
</HTML>
@@ -1,43 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* An Email address with an associated alias.
*/
public class MailAddress {
final String alias;
final String emailAddress;
/**
* Create an address with an optional alias.
*/
public MailAddress(String alias, String emailAddress) {
this.alias = alias;
this.emailAddress = emailAddress;
}
/**
* Return the alias.
* If the alias is null this returns an empty string.
*/
public String getAlias() {
if (alias == null) {
return "";
}
return alias;
}
/**
* Return the email address.
*/
public String getEmailAddress() {
return emailAddress;
}
public String toString() {
return getAlias() + " " + "<" + getEmailAddress() + ">";
}
}
@@ -1,49 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* Represents the success or failure of a mail send.
*/
public class MailEvent {
/**
* The error indicating a send failure.
*/
final Throwable error;
/**
* The message that was sent.
*/
final MailMessage message;
/**
* The message send failed with an error.
*/
public MailEvent(MailMessage message, Throwable error) {
this.message = message;
this.error = error;
}
/**
* The message that we attempted to send.
*/
public MailMessage getMailMessage() {
return message;
}
/**
* Returns true if the message was sent successfully.
*/
public boolean wasSuccessful() {
return (error == null);
}
/**
* The error indicating the send failed.
*/
public Throwable getError() {
return error;
}
}
@@ -1,13 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
/**
* Listens to see if the message was successfully sent.
*/
public interface MailListener {
/**
* Handle the message event.
*/
void handleEvent(MailEvent event);
}
@@ -1,153 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
* A simple test message that can be sent via smtp.
*/
public class MailMessage {
/**
* The body content.
*/
final ArrayList<String> bodylines;
/**
* The sender email address.
*/
MailAddress senderAddress;
/**
* The headers.
*/
final HashMap<String, String> header = new HashMap<String, String>();
/**
* the recipient of the email.
*/
MailAddress currentRecipient;
/**
* The list of recipients.
*/
final ArrayList<MailAddress> recipientList = new ArrayList<MailAddress>();
/**
* Create the message.
*/
public MailMessage() {
bodylines = new ArrayList<String>();
}
/**
* Set the current recipient.
*/
public void setCurrentRecipient(MailAddress currentRecipient) {
this.currentRecipient = currentRecipient;
}
/**
* Return the current recipient.
*/
public MailAddress getCurrentRecipient() {
return currentRecipient;
}
/**
* Add a recipient.
*/
public void addRecipient(String alias, String emailAddress) {
recipientList.add(new MailAddress(alias, emailAddress));
}
/**
* Set the sender details.
*/
public void setSender(String alias, String senderEmail) {
this.senderAddress = new MailAddress(alias, senderEmail);
}
/**
* Return the sender address.
*/
public MailAddress getSender() {
return senderAddress;
}
/**
* Return the recipient list.
*/
public List<MailAddress> getRecipientList() {
return recipientList;
}
/**
* add a header to the message.
*/
public void addHeader(String key, String val) {
header.put(key, val);
}
/**
* Set the subject text.
*/
public void setSubject(String subject) {
addHeader("Subject", subject);
}
/**
* Return the subject text.
*/
public String getSubject() {
return getHeader("Subject");
}
/**
* Add text to the body.
*/
public void addBodyLine(String line) {
bodylines.add(line);
}
/**
* Return the body text.
*/
public List<String> getBodyLines() {
return bodylines;
}
/**
* Return the headers.
*/
public Collection<String> getHeaderFields() {
return header.keySet();
}
/**
* Return a given header.
*/
public String getHeader(String key) {
return header.get(key);
}
public String toString() {
StringBuilder sb = new StringBuilder(100);
sb.append("Sender: ").append(senderAddress).append("\tRecipient: ").append(recipientList).append("\n");
for (String key : header.keySet()) {
String hline = key + ": " + header.get(key) + "\n";
sb.append(hline);
}
sb.append("\n");
for (String line : bodylines) {
sb.append(line).append("\n");
}
return sb.toString();
}
}
@@ -1,206 +0,0 @@
package com.avaje.ebeaninternal.server.lib.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* Sends simple MailMessages via smtp.
*/
public class MailSender implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(MailSender.class);
int traceLevel = 0;
Socket sserver;
final String server;
BufferedReader in;
OutputStreamWriter out;
MailMessage message;
MailListener listener = null;
private static final int SMTP_PORT = 25;
/**
* Create for a given mail server.
*/
public MailSender(String server) {
this.server = server;
}
/**
* Set the listener to handle MessageEvents.
*/
public void setMailListener(MailListener listener) {
this.listener = listener;
}
/**
* Send the message.
*/
public void run() {
send(message);
}
/**
* Send the message in a background thread.
*/
public void sendInBackground(MailMessage message) {
this.message = message;
Thread thread = new Thread(this);
thread.start();
}
/**
* Send the message in the current thread.
*/
public void send(MailMessage message) {
try {
for (MailAddress recipientAddress : message.getRecipientList()) {
sserver = new Socket(server, SMTP_PORT);
send(message, sserver, recipientAddress);
sserver.close();
if (listener != null) {
MailEvent event = new MailEvent(message, null);
listener.handleEvent(event);
}
}
} catch (Exception ex) {
if (listener != null) {
MailEvent event = new MailEvent(message, ex);
listener.handleEvent(event);
} else {
logger.error(null, ex);
}
}
}
private void send(MailMessage message, Socket sserver, MailAddress recipientAddress) throws IOException {
// A bit convoluted, but doesn't depend on DNS in any way...
InetAddress localhost = sserver.getLocalAddress();
String localaddress = localhost.getHostAddress();
MailAddress sender = message.getSender();
message.setCurrentRecipient(recipientAddress);
// Mandatory header fields, Date and From
if (message.getHeader("Date") == null) {
message.addHeader("Date", new java.util.Date().toString());
}
if (message.getHeader("From") == null) {
message.addHeader("From", sender.getAlias() + " <" + sender.getEmailAddress() + ">");
}
// if (message.getHeader("From") == null){
message.addHeader("To", recipientAddress.getAlias() + " <" + recipientAddress.getEmailAddress() + ">");
// }
out = new OutputStreamWriter(sserver.getOutputStream());
in = new BufferedReader(new InputStreamReader(sserver.getInputStream()));
String sintro = readln();
if (!sintro.startsWith("220")) { // 220
logger.debug("SmtpSender: intro==" + sintro);
return;
}
writeln("EHLO " + localaddress);
if (!expect250()) {
return;
}
writeln("MAIL FROM:<" + sender.getEmailAddress() + ">");
if (!expect250()) {
return;
}
writeln("RCPT TO:<" + recipientAddress.getEmailAddress() + ">");
if (!expect250()) {
return;
}
writeln("DATA");
while (true) { // may be multiple 250 replies pending from server
String line = readln();
if (line.startsWith("3"))
break; // ready to send
if (!line.startsWith("2")) {
logger.debug("SmtpSender.send reponse to DATA: " + line);
return;
}
}
for (String key : message.getHeaderFields()) {
writeln(key + ": " + message.getHeader(key));
}
writeln(""); // end of header;
for (String bline : message.getBodyLines()) {
if (bline.startsWith(".")) {
bline = "." + bline;
}
writeln(bline);
}
writeln(".");
expect250();
writeln("QUIT");
}
private boolean expect250() throws IOException {
String line = readln();
if (!line.startsWith("2")) {
logger.info("SmtpSender.expect250: " + line);
return false;
}
return true;
}
private void writeln(String s) throws IOException {
if (traceLevel > 2) {
logger.debug("From client: " + s);
}
out.write(s + "\r\n");
out.flush();
}
private String readln() throws IOException {
String line = in.readLine();
if (traceLevel > 1) {
logger.debug("From server: " + line);
}
return line;
}
/**
* Set the trace level.
*/
public void setTraceLevel(int traceLevel) {
this.traceLevel = traceLevel;
}
/**
* Return the hostname of the local machine.
*/
public String getLocalHostName() {
try {
InetAddress ipaddress = InetAddress.getLocalHost();
String localHost = ipaddress.getHostName();
if (localHost == null) {
return "localhost";
} else {
return localHost;
}
} catch (UnknownHostException e) {
return "localhost";
}
}
}
@@ -344,7 +344,7 @@ public class DLoadContext implements LoadContext {
query.asDraft();
}
if (includeSoftDeletes) {
query.includeSoftDeletes();
query.setIncludeSoftDeletes();
}
if (disableReadAudit) {
query.setDisableReadAuditing();
@@ -729,7 +729,7 @@ public final class DefaultPersister implements Persister {
q.select(sb.toString());
if (!softDelete) {
// hard delete so we want this query to include logically deleted rows (if any)
q.includeSoftDeletes();
q.setIncludeSoftDeletes();
}
return q;
}
@@ -68,7 +68,7 @@ class DeleteUnloadedForeignKeys {
q.setPersistenceContextScope(PersistenceContextScope.QUERY);
q.setAutoTune(false);
q.select(sb.toString());
q.includeSoftDeletes();
q.setIncludeSoftDeletes();
q.where().idEq(id);
SpiTransaction t = request.getTransaction();
@@ -47,8 +47,12 @@ public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureLis
public List<T> getUnchecked() {
try {
return get();
} catch (InterruptedException e) {
// restore the interrupted status (so client can check for that)
Thread.currentThread().interrupt();
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
@@ -58,8 +62,12 @@ public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureLis
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException {
try {
return get(timeout, unit);
} catch (InterruptedException e) {
// restore the interrupted status (so client can check for that)
Thread.currentThread().interrupt();
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
@@ -358,6 +358,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
@Override
public Query<T> includeSoftDeletes() {
return setIncludeSoftDeletes();
}
@Override
public Query<T> setIncludeSoftDeletes() {
this.temporalMode = TemporalMode.SOFT_DELETED;
return this;
}
@@ -16,7 +16,7 @@ import com.avaje.ebeaninternal.api.TransactionEventTable.TableIUD;
import com.avaje.ebeaninternal.server.cluster.ClusterManager;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import org.avaje.datasource.DataSourcePool;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import org.slf4j.Logger;
@@ -27,7 +27,6 @@ import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
@@ -129,15 +128,6 @@ public class TransactionManager {
this.externalTransPrefix = "e";
this.onQueryOnly = initOnQueryOnly(config.getDatabasePlatform().getOnQueryOnly(), dataSource);
initialiseHeartbeat();
}
private void initialiseHeartbeat() {
if (dataSource instanceof DataSourcePool) {
DataSourcePool ds = (DataSourcePool) dataSource;
backgroundExecutor.executePeriodically(ds.getHeartbeatRunnable(), ds.getHeartbeatFreqSecs(), TimeUnit.SECONDS);
}
}
public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {
@@ -0,0 +1,4 @@
/**
* Mapping for document store integration.
*/
package com.avaje.ebeanservice.docstore.api.mapping;
@@ -0,0 +1,4 @@
/**
* The service API for document store integration.
*/
package com.avaje.ebeanservice.docstore.api;
@@ -0,0 +1,4 @@
/**
* Support objects for implementing integration.
*/
package com.avaje.ebeanservice.docstore.api.support;
@@ -0,0 +1,8 @@
/**
* "No op" implementation of document store.
* <p>
* This is 'placeholder' implementation used if there is no document store service found
* and if there is an attempt to use the document store features an error will be thrown.
* </p>
*/
package com.avaje.ebeanservice.docstore.none;
@@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class EbeanServerFactory_ServerConfigStart_Test {
@Test
public void test() {
public void test() throws InterruptedException {
ServerConfig config = new ServerConfig();
config.setName("h2");
@@ -34,6 +34,12 @@ public class EbeanServerFactory_ServerConfigStart_Test {
assertThat(OnStartupViaClass.calledWithConfig).isSameAs(config);
assertThat(ebeanServer).isNotNull();
// test server shutdown and restart using the same ServerConfig
ebeanServer.shutdown(true, false);
EbeanServer restartedServer = EbeanServerFactory.create(config);
restartedServer.shutdown(true, false);
}
public static class OnStartup implements ServerConfigStartup {
@@ -1,100 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
public class TestBusyBuffer extends BaseTestCase {
@Test
public void test() {
BusyConnectionBuffer b = new BusyConnectionBuffer(2, 4);
PooledConnection p0 = new PooledConnection("0");
PooledConnection p1 = new PooledConnection("1");
PooledConnection p2 = new PooledConnection("2");
PooledConnection p3 = new PooledConnection("3");
Assert.assertEquals(2, b.getCapacity());
b.add(p0);
b.add(p1);
Assert.assertEquals(2, b.getCapacity());
b.add(p2);
Assert.assertEquals(6, b.getCapacity());
b.add(p3);
Assert.assertEquals(0, p0.getSlotId());
Assert.assertEquals(1, p1.getSlotId());
Assert.assertEquals(2, p2.getSlotId());
Assert.assertEquals(3, p3.getSlotId());
b.remove(p2);
b.add(p2);
Assert.assertEquals(4, p2.getSlotId());
b.remove(p0);
b.add(p0);
Assert.assertEquals(5, p0.getSlotId());
b.remove(p2);
b.add(p2);
Assert.assertEquals(0, p2.getSlotId());
}
@Test
public void test_rotate() {
BusyConnectionBuffer b = new BusyConnectionBuffer(2, 2);
PooledConnection p0 = new PooledConnection("0");
PooledConnection p1 = new PooledConnection("1");
PooledConnection p2 = new PooledConnection("2");
PooledConnection p3 = new PooledConnection("3");
Assert.assertEquals(2, b.getCapacity());
Assert.assertEquals(0, b.size());
b.add(p0);
b.add(p1);
Assert.assertEquals(2, b.size());
Assert.assertEquals(2, b.getCapacity());
b.add(p2);
Assert.assertEquals(3, b.size());
Assert.assertEquals(4, b.getCapacity());
b.add(p3);
Assert.assertEquals(4, b.size());
Assert.assertEquals(4, b.getCapacity());
Assert.assertEquals(0, p0.getSlotId());
Assert.assertEquals(1, p1.getSlotId());
Assert.assertEquals(2, p2.getSlotId());
Assert.assertEquals(3, p3.getSlotId());
b.remove(p2);
Assert.assertEquals(3, b.size());
b.remove(p0);
Assert.assertEquals(2, b.size());
b.remove(p3);
Assert.assertEquals(1, b.size());
b.add(p2);
Assert.assertEquals(2, b.size());
Assert.assertEquals(0, p2.getSlotId());
b.remove(p0);
Assert.assertEquals(2, b.size());
b.add(p0);
Assert.assertEquals(3, b.size());
// p1 is still in it's slot
Assert.assertEquals(2, p0.getSlotId());
b.remove(p2);
b.add(p2);
Assert.assertEquals(3, p2.getSlotId());
}
}
@@ -1,131 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebeaninternal.server.core.DefaultBackgroundExecutor;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool.Status;
public class TestDataSourceMax extends BaseTestCase {
@Test
public void test() {
boolean skipThisTest = true;
if (skipThisTest) {
return;
}
String name = "mysql";
DataSourceConfig dsConfig = new DataSourceConfig();
dsConfig.loadSettings(name);
dsConfig.setMinConnections(2);
dsConfig.setMaxConnections(25);
dsConfig.setWaitTimeoutMillis(30000);
dsConfig.setCaptureStackTrace(true);
DataSourcePool pool = new DataSourcePool(null, name, dsConfig);
//pool.checkDataSource();
// if (true) {
// pool.shutdown(false);
// return;
// }
DefaultBackgroundExecutor bg = new DefaultBackgroundExecutor(1, 1, 2, 180, 30, "testDs");
try {
for (int i = 0; i < 12; i++) {
// Thread.sleep(10*i);
bg.execute(new ConnRunner(pool, 4000, i));
}
Thread.sleep(10000);
pool.getStatistics(true);
Thread.sleep(30000);
Status status = pool.getStatus(false);
System.out.println(status);
pool.shutdown(false);
} catch (Exception e) {
e.printStackTrace();
}
}
private static class ConnRunner implements Runnable {
final DataSourcePool pool;
final long sleepMillis;
final int position;
ConnRunner(DataSourcePool pool, long sleepMillis, int position) {
this.pool = pool;
this.sleepMillis = sleepMillis;
this.position = position;
}
private void waitSomeTime(long count) {
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e){
throw new RuntimeException(e);
}
}
public void run() {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
long count = -1;
try {
connection = pool.getConnection();
pstmt = connection.prepareStatement("select count(*) from o_customer");
rset = pstmt.executeQuery();
while (rset.next()) {
// do nothing actually
count = rset.getLong(1);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (rset != null) {
try {
rset.close();
} catch (Exception e) {
e.printStackTrace();
}
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
waitSomeTime(count);
}
}
}
}
@@ -1,74 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
public class TestFreeBuffer extends BaseTestCase {
@Test
public void test() {
FreeConnectionBuffer b = new FreeConnectionBuffer();
PooledConnection p0 = new PooledConnection("0");
PooledConnection p1 = new PooledConnection("1");
PooledConnection p2 = new PooledConnection("2");
// PooledConnection p3 = new PooledConnection("3");
Assert.assertEquals(0, b.size());
Assert.assertEquals(true, b.isEmpty());
b.add(p0);
Assert.assertEquals(1, b.size());
Assert.assertEquals(false, b.isEmpty());
PooledConnection r0 = b.remove();
Assert.assertTrue(p0 == r0);
Assert.assertEquals(0, b.size());
Assert.assertEquals(true, b.isEmpty());
b.add(p0);
b.add(p1);
b.add(p2);
Assert.assertEquals(3, b.size());
PooledConnection r1 = b.remove();
Assert.assertTrue(p0 == r1);
PooledConnection r2 = b.remove();
Assert.assertTrue(p1 == r2);
Assert.assertEquals(1, b.size());
b.add(p0);
Assert.assertEquals(2, b.size());
PooledConnection r3 = b.remove();
Assert.assertTrue(p2 == r3);
Assert.assertEquals(1, b.size());
PooledConnection r4 = b.remove();
Assert.assertTrue(p0 == r4);
Assert.assertEquals(0, b.size());
b.add(p2);
b.add(p1);
b.add(p0);
Assert.assertEquals(3, b.size());
PooledConnection r5 = b.remove();
Assert.assertTrue(p2 == r5);
Assert.assertEquals(2, b.size());
PooledConnection r6 = b.remove();
Assert.assertTrue(p1 == r6);
Assert.assertEquals(1, b.size());
PooledConnection r7 = b.remove();
Assert.assertTrue(p0 == r7);
Assert.assertEquals(0, b.size());
}
}
@@ -1,40 +0,0 @@
package com.avaje.ebeaninternal.server.lib.sql;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import com.avaje.ebean.BaseTestCase;
public class TestFreeBufferTrim extends BaseTestCase {
@Test
public void testWithTime() {
FreeConnectionBuffer b = new FreeConnectionBuffer();
Assert.assertEquals(0, b.size());
PooledConnection p0 = Mockito.mock(PooledConnection.class);
Mockito.when(p0.shouldTrim(1500, 0)).thenReturn(true);
PooledConnection p1 = Mockito.mock(PooledConnection.class);
Mockito.when(p1.shouldTrim(1500, 0)).thenReturn(true);
PooledConnection p2 = Mockito.mock(PooledConnection.class);
Mockito.when(p2.shouldTrim(1500, 0)).thenReturn(false);
b.add(p0);
b.add(p1);
b.add(p2);
Assert.assertEquals(3, b.size());
int trimCount = b.trim(1500, 0);
Assert.assertEquals(1, b.size());
Assert.assertEquals(2, trimCount);
}
}
@@ -12,11 +12,11 @@ import static org.assertj.core.api.Assertions.assertThat;
public class DLoadContextTest extends BaseTestCase {
OrmQueryRequest<Order> queryRequest(Query<Order> query) {
private OrmQueryRequest<Order> queryRequest(Query<Order> query) {
return OrmQueryRequestTestHelper.queryRequest(query);
}
Query<Order> query() {
private Query<Order> query() {
return server().find(Order.class);
}
@@ -25,10 +25,12 @@ public class DLoadContextTest extends BaseTestCase {
OrmQueryRequest<Order> queryRequest = queryRequest(query());
queryRequest.initTransIfRequired();
queryRequest.endTransIfRequired();
DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext();
DLoadBeanContext customer = graphContext.getBeanContext("customer");
assertThat(customer.firstBatchSize).isEqualTo(10);
assertThat(customer.secondaryBatchSize).isEqualTo(10);
}
@@ -38,6 +40,7 @@ public class DLoadContextTest extends BaseTestCase {
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer",new FetchConfig().query()));
queryRequest.initTransIfRequired();
queryRequest.endTransIfRequired();
DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext();
DLoadBeanContext customer = graphContext.getBeanContext("customer");
@@ -51,6 +54,7 @@ public class DLoadContextTest extends BaseTestCase {
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer",new FetchConfig().query(50)));
queryRequest.initTransIfRequired();
queryRequest.endTransIfRequired();
DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext();
DLoadBeanContext customer = graphContext.getBeanContext("customer");
@@ -64,6 +68,7 @@ public class DLoadContextTest extends BaseTestCase {
OrmQueryRequest<Order> queryRequest = queryRequest(query().fetch("customer",new FetchConfig().queryFirst(20).lazy(5)));
queryRequest.initTransIfRequired();
queryRequest.endTransIfRequired();
DLoadContext graphContext = (DLoadContext)queryRequest.getGraphContext();
DLoadBeanContext customer = graphContext.getBeanContext("customer");
@@ -8,7 +8,7 @@ import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.Query;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.config.DataSourceConfig;
import org.avaje.datasource.DataSourceConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.config.dbplatform.PostgresPlatform;
import com.avaje.tests.model.basic.TOne;
@@ -2,7 +2,7 @@ package com.avaje.tests.basic;
import java.sql.Connection;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePoolListener;
import org.avaje.datasource.DataSourcePoolListener;
public class MyTestDataSourcePoolListener implements DataSourcePoolListener
{
@@ -1,20 +1,19 @@
package com.avaje.tests.model.basic;
import java.sql.Timestamp;
import java.util.List;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.annotation.ChangeLog;
import com.avaje.ebean.annotation.CreatedTimestamp;
import com.avaje.ebean.annotation.DocEmbedded;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.Index;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import com.avaje.ebean.annotation.CacheStrategy;
import com.avaje.ebean.annotation.ChangeLog;
import com.avaje.ebean.annotation.CreatedTimestamp;
import com.avaje.ebean.annotation.DocStoreEmbedded;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.Index;
import java.sql.Timestamp;
import java.util.List;
@DocStore
@Index(columnNames = {"last_name","first_name"})
@@ -35,7 +34,7 @@ public class Contact {
String mobile;
String email;
@DocStoreEmbedded(doc="id,name")
@DocEmbedded(doc="id,name")
@ManyToOne(optional=false)
Customer customer;
@@ -4,7 +4,7 @@ import com.avaje.ebean.annotation.ChangeLog;
import com.avaje.ebean.annotation.ChangeLogInsertMode;
import com.avaje.ebean.annotation.DbComment;
import com.avaje.ebean.annotation.DbEnumValue;
import com.avaje.ebean.annotation.DocStoreEmbedded;
import com.avaje.ebean.annotation.DocEmbedded;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.JsonIgnore;
import com.avaje.ebean.annotation.Where;
@@ -80,11 +80,11 @@ public class Customer extends BasicDomain {
@NotNull(groups = { ValidationGroupSomething.class })
Date anniversary;
@DocStoreEmbedded(doc="*,country(*)")
@DocEmbedded(doc="*,country(*)")
@ManyToOne(cascade = CascadeType.ALL)
Address billingAddress;
@DocStoreEmbedded(doc="*,country(*)")
@DocEmbedded(doc="*,country(*)")
@ManyToOne(cascade = CascadeType.ALL)
Address shippingAddress;
@@ -1,6 +1,7 @@
package com.avaje.tests.model.basic;
import com.avaje.ebean.annotation.ChangeLog;
import com.avaje.ebean.annotation.DocEmbedded;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.DocStoreEmbedded;
import com.avaje.ebean.annotation.Formula;
@@ -86,7 +87,7 @@ public class Order implements Serializable {
@NotNull
@ManyToOne
@JoinColumn(name = "kcustomer_id")
@DocStoreEmbedded(doc = "id,name")
@DocEmbedded(doc = "id,name")
Customer customer;
@Column(name = "name", table = "o_customer")
@@ -1,15 +1,14 @@
package com.avaje.tests.model.basic;
import com.avaje.ebean.annotation.DocStoreEmbedded;
import java.io.Serializable;
import java.sql.Timestamp;
import com.avaje.ebean.annotation.DocEmbedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* Order Detail entity bean.
@@ -33,7 +32,7 @@ public class OrderDetail implements Serializable {
Double unitPrice;
@ManyToOne
@DocStoreEmbedded(doc = "id,name,sku")
@DocEmbedded(doc = "id,name,sku")
Product product;
Timestamp cretime;
@@ -1,15 +1,14 @@
package com.avaje.tests.query;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.ResetBasicData;
import org.junit.Test;
import java.util.List;
import static org.junit.Assert.assertNotNull;
public class TestQueryInIdTypeConversion extends BaseTestCase {
@@ -18,13 +17,8 @@ public class TestQueryInIdTypeConversion extends BaseTestCase {
ResetBasicData.reset();
List<String> idList = new ArrayList<String>();
idList.add("1");
idList.add("2");
List<Customer> list = Ebean.find(Customer.class).where().idIn(idList).findList();
Assert.assertNotNull(list);
List<Customer> list = Ebean.find(Customer.class).where().idIn("1", "2").findList();
assertNotNull(list);
}
}
@@ -37,7 +37,7 @@ public class TestSoftDeleteBasic extends BaseTestCase {
EBasicSoftDelete findInclude = Ebean.find(EBasicSoftDelete.class)
.setId(bean.getId())
.includeSoftDeletes()
.setIncludeSoftDeletes()
.findUnique();
assertThat(findInclude).isNotNull();
@@ -70,7 +70,7 @@ public class TestSoftDeleteBasic extends BaseTestCase {
// -- test includeSoftDeletes().findRowCount()
LoggedSqlCollector.start();
int rowCountFull = Ebean.find(EBasicSoftDelete.class).includeSoftDeletes().findRowCount();
int rowCountFull = Ebean.find(EBasicSoftDelete.class).setIncludeSoftDeletes().findRowCount();
assertThat(rowCountFull).isGreaterThan(rowCountAfter);
loggedSql = LoggedSqlCollector.stop();
@@ -5,16 +5,19 @@ import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.PropertyMap;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.tests.model.basic.UTDetail;
import com.avaje.tests.model.basic.UTMaster;
import org.avaje.datasource.DataSourceConfig;
import org.avaje.datasource.DataSourcePool;
import org.avaje.datasource.pool.ConnectionPool;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -24,11 +27,13 @@ public class TestAutoCommitDataSource extends BaseTestCase {
@Test
public void test() throws SQLException {
Properties properties = PropertyMap.defaultProperties();
DataSourceConfig dsConfig = new DataSourceConfig();
dsConfig.loadSettings("h2autocommit");//"pg"
dsConfig.loadSettings(properties, "h2autocommit");//"pg"
dsConfig.setAutoCommit(true);
DataSourcePool pool = new DataSourcePool(null, "h2autocommit", dsConfig);
DataSourcePool pool = new ConnectionPool("h2autocommit", dsConfig);
Connection connection = pool.getConnection();
assertTrue(connection.getAutoCommit());
@@ -5,16 +5,19 @@ import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import com.avaje.ebean.config.DataSourceConfig;
import com.avaje.ebean.config.PropertyMap;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebeaninternal.server.lib.sql.DataSourcePool;
import com.avaje.tests.model.basic.UTDetail;
import com.avaje.tests.model.basic.UTMaster;
import org.avaje.datasource.DataSourceConfig;
import org.avaje.datasource.DataSourcePool;
import org.avaje.datasource.pool.ConnectionPool;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -24,11 +27,13 @@ public class TestExplicitTransactionMode extends BaseTestCase {
@Test
public void test() throws SQLException {
Properties properties = PropertyMap.defaultProperties();
DataSourceConfig dsConfig = new DataSourceConfig();
dsConfig.loadSettings("h2autocommit");//"h2autocommit","pg"
dsConfig.loadSettings(properties, "h2autocommit");//"h2autocommit","pg"
dsConfig.setAutoCommit(true);
DataSourcePool pool = new DataSourcePool(null, "h2autocommit", dsConfig);
DataSourcePool pool = new ConnectionPool("h2autocommit", dsConfig);
Connection connection = pool.getConnection();
assertTrue(connection.getAutoCommit());
@@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.config.DataSourceConfig;
import org.avaje.datasource.DataSourceConfig;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.tests.model.basic.TOne;
+4 -4
View File
@@ -52,7 +52,7 @@ ebean.migration.migrationPath=dbmigration/myapp
datasource.h2.username=sa
datasource.h2.password=
datasource.h2.databaseUrl=jdbc:h2:mem:tests;DB_CLOSE_DELAY=-1
datasource.h2.databaseUrl=jdbc:h2:mem:tests
datasource.h2.databaseDriver=org.h2.Driver
datasource.h2.minConnections=1
datasource.h2.maxConnections=25
@@ -71,17 +71,17 @@ datasource.h2autocommit.databaseDriver=org.h2.Driver
datasource.h2other.username=sa
datasource.h2other.password=
datasource.h2other.databaseUrl=jdbc:h2:mem:h2other;DB_CLOSE_DELAY=-1
datasource.h2other.databaseUrl=jdbc:h2:mem:h2other
datasource.h2other.databaseDriver=org.h2.Driver
datasource.h2otherfind.username=sa
datasource.h2otherfind.password=
datasource.h2otherfind.databaseUrl=jdbc:h2:mem:h2otherfind;DB_CLOSE_DELAY=-1
datasource.h2otherfind.databaseUrl=jdbc:h2:mem:h2otherfind
datasource.h2otherfind.databaseDriver=org.h2.Driver
datasource.h2ebasicver.username=sa
datasource.h2ebasicver.password=
datasource.h2ebasicver.databaseUrl=jdbc:h2:mem:h2ebasicver;DB_CLOSE_DELAY=-1
datasource.h2ebasicver.databaseUrl=jdbc:h2:mem:h2ebasicver
datasource.h2ebasicver.databaseDriver=org.h2.Driver