You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2022/01/21 23:06:15 UTC
[empire-db] branch version3 updated: EMPIREDB-364 new class DBRollbackManager
This is an automated email from the ASF dual-hosted git repository.
doebele pushed a commit to branch version3
in repository https://gitbox.apache.org/repos/asf/empire-db.git
The following commit(s) were added to refs/heads/version3 by this push:
new e8337ff EMPIREDB-364 new class DBRollbackManager
e8337ff is described below
commit e8337ff2af837489eeba313dfb4fee7822d0e18e
Author: Rainer Döbele <do...@apache.org>
AuthorDate: Sat Jan 22 00:06:13 2022 +0100
EMPIREDB-364 new class DBRollbackManager
---
.../org/apache/empire/samples/db/SampleApp.java | 2 -
.../websample/web/pages/EmployeeDetailPage.java | 7 +-
.../org/apache/empire/rest/service/Service.java | 13 ++-
.../org/apache/empire/jsf2/app/WebApplication.java | 114 ++++++++++-----------
.../org/apache/empire/jsf2/app/WebDBContext.java | 72 ++++---------
.../apache/empire/db/context/DBContextBase.java | 111 ++++++++++----------
.../apache/empire/db/context/DBContextStatic.java | 32 ++++--
.../empire/db/context/DBRollbackManager.java | 100 ++++++++++++++++++
8 files changed, 270 insertions(+), 181 deletions(-)
diff --git a/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java b/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
index 8d7d90a..edd76a4 100644
--- a/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
+++ b/empire-db-examples/empire-db-example-basic/src/main/java/org/apache/empire/samples/db/SampleApp.java
@@ -135,11 +135,9 @@ public class SampleApp
// commit
context.commit();
- /*
int idEmp = testTransactionCreate(idDevDep);
testTransactionUpdate(idEmp);
testTransactionDelete(idEmp);
- */
// STEP 7: Update Records (by setting the phone Number)
System.out.println("*** Step 7: updateEmployee() ***");
diff --git a/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/websample/web/pages/EmployeeDetailPage.java b/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/websample/web/pages/EmployeeDetailPage.java
index 7bcf4f1..a6682db 100644
--- a/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/websample/web/pages/EmployeeDetailPage.java
+++ b/empire-db-examples/empire-db-example-jsf2/src/main/java/org/apache/empire/jsf2/websample/web/pages/EmployeeDetailPage.java
@@ -18,8 +18,10 @@
*/
package org.apache.empire.jsf2.websample.web.pages;
+import org.apache.empire.exceptions.MiscellaneousErrorException;
import org.apache.empire.jsf2.pageelements.RecordPageElement;
import org.apache.empire.jsf2.pages.PageOutcome;
+import org.apache.empire.jsf2.websample.db.SampleDB;
import org.apache.empire.jsf2.websample.db.records.EmployeeRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -101,11 +103,12 @@ public class EmployeeDetailPage extends SamplePage
getEmployeeRecord().update();
- /* test transaction
+ getEmployeeRecord().getContext().getConnection();
+
+ /* test transaction */
SampleDB db = this.getDatabase();
if (getEmployeeRecord().isNull(db.T_EMPLOYEES.PHONE_NUMBER))
throw new MiscellaneousErrorException("Phone number must not be empty!");
- */
return getParentOutcome(true);
}
diff --git a/empire-db-examples/empire-db-example-vue/src/main/java/org/apache/empire/rest/service/Service.java b/empire-db-examples/empire-db-example-vue/src/main/java/org/apache/empire/rest/service/Service.java
index 2116e71..b263e09 100644
--- a/empire-db-examples/empire-db-example-vue/src/main/java/org/apache/empire/rest/service/Service.java
+++ b/empire-db-examples/empire-db-example-vue/src/main/java/org/apache/empire/rest/service/Service.java
@@ -31,6 +31,7 @@ import javax.ws.rs.core.Response;
import org.apache.empire.db.DBDatabaseDriver;
import org.apache.empire.db.context.DBContextBase;
+import org.apache.empire.db.context.DBRollbackManager;
import org.apache.empire.exceptions.EmpireException;
import org.apache.empire.rest.app.SampleServiceApp;
import org.apache.empire.rest.app.TextResolver;
@@ -90,15 +91,21 @@ public abstract class Service {
}
@Override
- public Connection getConnection()
+ public TextResolver getTextResolver()
+ {
+ return textResolver;
+ }
+
+ @Override
+ protected Connection getConnection(boolean required)
{
return conn;
}
@Override
- public TextResolver getTextResolver()
+ protected DBRollbackManager getRollbackManager(boolean required)
{
- return textResolver;
+ return null; /* No Rollbacks allowed */
}
}
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
index 9956457..31c0ba0 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebApplication.java
@@ -20,10 +20,8 @@ package org.apache.empire.jsf2.app;
import java.sql.Connection;
import java.sql.SQLException;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
@@ -44,6 +42,8 @@ import javax.sql.DataSource;
import org.apache.empire.commons.StringUtils;
import org.apache.empire.data.DataType;
import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.context.DBRollbackManager;
+import org.apache.empire.db.context.DBRollbackManager.ReleaseAction;
import org.apache.empire.exceptions.InternalException;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.apache.empire.exceptions.NotSupportedException;
@@ -59,7 +59,9 @@ public abstract class WebApplication
{
private static final Logger log = LoggerFactory.getLogger(WebApplication.class);
- private static final String CONNECTION_CONTEXT_INFO_MAP = "CONNECTION_CONTEXT_INFO_MAP";
+ private static final String REQUEST_CONNECTION_MAP = "requestConnectionMap";
+
+ private static final String CONN_ROLLBACK_MANAGER = "connRollbackManager";
public static String APPLICATION_BEAN_NAME = "webApplication";
@@ -455,55 +457,48 @@ public abstract class WebApplication
}
}
- /**
- * Internally used to manage Connections and Contexts
- */
- private static class ConnectionContextInfo
+ public DBRollbackManager getRollbackManagerForRequest(FacesContext fc, boolean create)
{
- ConnectionContextInfo(Connection conn, WebDBContext<? extends DBDatabase> ctx)
- {
- this.connection = conn;
- ctxList.add(ctx);
+ DBRollbackManager dbrm = (DBRollbackManager)FacesUtils.getRequestAttribute(fc, CONN_ROLLBACK_MANAGER);
+ if (dbrm==null && create)
+ { dbrm = new DBRollbackManager(1, 8);
+ FacesUtils.setRequestAttribute(fc, CONN_ROLLBACK_MANAGER, dbrm);
}
- public Connection connection;
- public final List<WebDBContext<? extends DBDatabase>> ctxList = new ArrayList<WebDBContext<? extends DBDatabase>>(1);
+ return dbrm;
}
-
+
/**
* Obtains a connection for the current request
* A WebDBContext must be provided which must store the connection util releaseConnection is called
*/
- public Connection getConnectionForRequest(FacesContext fc, WebDBContext<? extends DBDatabase> context)
+ public Connection getConnectionForRequest(FacesContext fc, DBDatabase db, boolean create)
{
if (fc == null)
throw new InvalidArgumentException("FacesContext", fc);
- if (context == null)
- throw new InvalidArgumentException("WebDBContext", context);
+ if (db == null)
+ throw new InvalidArgumentException("DBDatabase", db);
// Get the ConnectionContextInfo map
@SuppressWarnings("unchecked")
- Map<DBDatabase, ConnectionContextInfo> cciMap = (Map<DBDatabase, ConnectionContextInfo>) FacesUtils.getRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP);
- if (cciMap== null)
- { cciMap = new HashMap<DBDatabase, ConnectionContextInfo>();
- FacesUtils.setRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP, cciMap);
+ Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+ if (connMap== null && create)
+ { connMap = new HashMap<DBDatabase, Connection>(1);
+ FacesUtils.setRequestAttribute(fc, REQUEST_CONNECTION_MAP, connMap);
+ }
+ else if (connMap==null)
+ { // Nothing to do
+ return null;
}
- DBDatabase db = context.getDatabase();
- ConnectionContextInfo cci = cciMap.get(db);
- if (cci==null)
+ Connection conn = connMap.get(db);
+ if (conn==null && create)
{ // Get Pooled Connection
- Connection conn = getConnection(db);
+ conn = getConnection(db);
if (conn== null)
throw new UnexpectedReturnValueException(this, "getConnection");
// Add to map
- cci = new ConnectionContextInfo(conn, context);
- cciMap.put(db, cci);
- }
- else
- { // add context
- if (cci.ctxList.contains(context)==false)
- cci.ctxList.add(context);
+ connMap.put(db, conn);
}
// done
- return cci.connection;
+ return conn;
}
/**
@@ -514,20 +509,22 @@ public abstract class WebApplication
public void releaseAllConnections(final FacesContext fc, boolean commit)
{
@SuppressWarnings("unchecked")
- Map<DBDatabase, ConnectionContextInfo> cciMap = (Map<DBDatabase, ConnectionContextInfo>) FacesUtils.getRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP);
- if (cciMap != null)
- { // Walk the connection map
- for (Map.Entry<DBDatabase, ConnectionContextInfo> e : cciMap.entrySet())
- {
- ConnectionContextInfo cci = e.getValue();
- releaseConnection(e.getKey(), cci.connection, commit);
- // release connection
- for (WebDBContext<? extends DBDatabase> ctx : cci.ctxList)
- ctx.releaseConnection(commit);
- }
- // remove from request map
- FacesUtils.setRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP, null);
+ Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+ if (connMap == null)
+ return; // Nothing to do
+ // Walk the connection map
+ DBRollbackManager dbrm = getRollbackManagerForRequest(fc, false);
+ ReleaseAction action = (commit ? ReleaseAction.Discard : ReleaseAction.Rollback);
+ for (Map.Entry<DBDatabase, Connection> e : connMap.entrySet())
+ {
+ Connection conn = e.getValue();
+ releaseConnection(e.getKey(), conn, commit);
+ // release connection
+ if (dbrm!=null)
+ dbrm.releaseConnection(conn, action);
}
+ // remove from request map
+ FacesUtils.setRequestAttribute(fc, REQUEST_CONNECTION_MAP, null);
}
/**
@@ -549,19 +546,18 @@ public abstract class WebApplication
public void releaseConnection(final FacesContext fc, DBDatabase db, boolean commit)
{
@SuppressWarnings("unchecked")
- Map<DBDatabase, ConnectionContextInfo> cciMap = (Map<DBDatabase, ConnectionContextInfo>) FacesUtils.getRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP);
- if (cciMap != null && cciMap.containsKey(db))
- {
- ConnectionContextInfo cci = cciMap.get(db);
- releaseConnection(db, cci.connection, commit);
- // release connection
- for (WebDBContext<? extends DBDatabase> ctx : cci.ctxList)
- ctx.releaseConnection(commit);
- // Walk the connection map
- cciMap.remove(db);
- if (cciMap.isEmpty())
- FacesUtils.setRequestAttribute(fc, CONNECTION_CONTEXT_INFO_MAP, null);
- }
+ Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+ if (connMap == null || !connMap.containsKey(db))
+ return; // Nothing to do;
+ // Release Connection
+ Connection conn = connMap.get(db);
+ releaseConnection(db, conn, commit);
+ // release connection
+ DBRollbackManager dbrm = getRollbackManagerForRequest(fc, false);
+ if (dbrm!=null)
+ dbrm.releaseConnection(conn, (commit ? ReleaseAction.Discard : ReleaseAction.Rollback));
+ // Remove from map
+ connMap.remove(db);
}
/**
diff --git a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebDBContext.java b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebDBContext.java
index b383ab7..6dfd633 100644
--- a/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebDBContext.java
+++ b/empire-db-jsf2/src/main/java/org/apache/empire/jsf2/app/WebDBContext.java
@@ -7,6 +7,7 @@ import javax.faces.context.FacesContext;
import org.apache.empire.db.DBDatabase;
import org.apache.empire.db.DBDatabaseDriver;
import org.apache.empire.db.context.DBContextBase;
+import org.apache.empire.db.context.DBRollbackManager;
import org.apache.empire.exceptions.NotSupportedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -21,17 +22,16 @@ import org.slf4j.LoggerFactory;
*/
public class WebDBContext<DB extends DBDatabase> extends DBContextBase
{
- private static final Logger log = LoggerFactory.getLogger(WebDBContext.class);
+ private static final Logger log = LoggerFactory.getLogger(WebDBContext.class);
- private final WebApplication app;
- private final DB database;
-
- // Held for request only
- private Connection conn;
+ private final WebApplication app;
+ private final DBDatabaseDriver driver;
+ private final DB database;
public WebDBContext(WebApplication app, DB db)
{
this.app = app;
+ this.driver = db.getDriver();
this.database = db;
// check driver
if (db.getDriver() == null)
@@ -46,50 +46,7 @@ public class WebDBContext<DB extends DBDatabase> extends DBContextBase
@Override
public DBDatabaseDriver getDriver()
{
- return database.getDriver();
- }
-
- /**
- * gets a Connection for the current request
- * IMPORTANT: Do not hold the connection!
- */
- @Override
- public Connection getConnection()
- {
- if (conn==null)
- { // get a new connection
- FacesContext fc = FacesContext.getCurrentInstance();
- conn = this.app.getConnectionForRequest(fc, this);
- }
- return conn;
- }
-
- @Override
- public void commit()
- {
- if (conn!=null)
- super.commit();
- else
- log.info("No Connection to commmit changes");
- }
-
- @Override
- public void rollback()
- {
- if (conn!=null)
- super.rollback();
- else
- log.info("No Connection to rollback changes");
- }
-
- public void releaseConnection(boolean commitPerformed)
- {
- this.conn = null;
- // commit or rollback?
- if (commitPerformed)
- discardAllHandlers();
- else
- rollbackAllHandlers();
+ return driver;
}
/**
@@ -107,4 +64,19 @@ public class WebDBContext<DB extends DBDatabase> extends DBContextBase
*/
throw new NotSupportedException(this, "discard");
}
+
+ @Override
+ protected Connection getConnection(boolean create)
+ {
+ FacesContext fc = FacesContext.getCurrentInstance();
+ return this.app.getConnectionForRequest(fc, database, create);
+ }
+
+ @Override
+ protected DBRollbackManager getRollbackManager(boolean create)
+ {
+ FacesContext fc = FacesContext.getCurrentInstance();
+ return this.app.getRollbackManagerForRequest(fc, create);
+ }
+
}
diff --git a/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java b/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java
index bd15277..2e66b75 100644
--- a/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java
+++ b/empire-db/src/main/java/org/apache/empire/db/context/DBContextBase.java
@@ -5,12 +5,11 @@ package org.apache.empire.db.context;
import java.sql.Connection;
import java.sql.SQLException;
-import java.util.LinkedHashMap;
-import java.util.Map;
import org.apache.empire.db.DBContext;
import org.apache.empire.db.DBObject;
import org.apache.empire.db.DBUtils;
+import org.apache.empire.db.context.DBRollbackManager.ReleaseAction;
import org.apache.empire.db.exceptions.EmpireSQLException;
import org.apache.empire.exceptions.InvalidArgumentException;
import org.slf4j.Logger;
@@ -26,9 +25,18 @@ public abstract class DBContextBase implements DBContext
// Logger
private static final Logger log = LoggerFactory.getLogger(DBContextBase.class);
- private Map<DBObject, DBRollbackHandler> rollbackHandler;
-
private DBUtils utils = null;
+
+ private boolean noRollbacksWarnOnce = true;
+
+ /**
+ * Factory function for Utils creation
+ * @return the utils implementation
+ */
+ protected DBUtils createUtils()
+ {
+ return new DBUtils(this);
+ }
@SuppressWarnings("unchecked")
@Override
@@ -38,20 +46,34 @@ public abstract class DBContextBase implements DBContext
utils = createUtils();
return ((T)utils);
}
+
+ protected abstract Connection getConnection(boolean required);
+
+ protected abstract DBRollbackManager getRollbackManager(boolean required);
+
+ @Override
+ public Connection getConnection()
+ {
+ return getConnection(true);
+ }
@Override
public void commit()
{
try
{ // Check argument
- Connection conn = getConnection();
+ Connection conn = getConnection(false);
if (conn==null)
- throw new InvalidArgumentException("conn", conn);
+ { log.info("No Connection to commmit changes");
+ return; // Nothing to do
+ }
// Commit
if (conn.getAutoCommit()==false)
conn.commit();
// discard rollbacks
- discardAllHandlers();
+ DBRollbackManager dbrm = getRollbackManager(false);
+ if (dbrm!=null)
+ dbrm.releaseConnection(conn, ReleaseAction.Discard);
// Done
return;
} catch (SQLException sqle) {
@@ -72,14 +94,18 @@ public abstract class DBContextBase implements DBContext
{
try
{ // Check argument
- Connection conn = getConnection();
+ Connection conn = getConnection(false);
if (conn==null)
- throw new InvalidArgumentException("conn", conn);
+ { log.info("No Connection to rollback changes");
+ return; // Nothing to do
+ }
// rollback
log.info("Database rollback issued!");
conn.rollback();
- // rollback
- rollbackAllHandlers();
+ // perform Rollback
+ DBRollbackManager dbrm = getRollbackManager(false);
+ if (dbrm!=null)
+ dbrm.releaseConnection(conn, ReleaseAction.Rollback);
// Done
return;
} catch (SQLException sqle) {
@@ -91,23 +117,27 @@ public abstract class DBContextBase implements DBContext
@Override
public void addRollbackHandler(DBRollbackHandler handler)
{
- if (rollbackHandler==null)
- rollbackHandler = new LinkedHashMap<DBObject, DBRollbackHandler>();
- // check
- DBObject object = handler.getObject();
- if (rollbackHandler.containsKey(object))
- rollbackHandler.get(object).combine(handler);
- else
- rollbackHandler.put(object, handler);
+ if (handler==null || handler.getObject()==null)
+ throw new InvalidArgumentException("handler", handler);
+ // Add handler
+ DBRollbackManager dbrm = getRollbackManager(true);
+ if (dbrm!=null)
+ dbrm.addHandler(getConnection(true), handler);
+ else if (noRollbacksWarnOnce)
+ { log.warn("*** No DBRollbackManager provided. Rollbacks will be disabled! ***");
+ noRollbacksWarnOnce = false;
+ }
}
@Override
public void removeRollbackHandler(DBObject object)
{
if (object==null)
- rollbackHandler=null; // remove all
- else if (rollbackHandler!=null && rollbackHandler.remove(object)!=null)
- log.info("Rollback handler for object {} was removed", object.getClass().getSimpleName());
+ throw new InvalidArgumentException("object", object);
+ // Remove handler
+ DBRollbackManager dbrm = getRollbackManager(false);
+ if (dbrm!=null)
+ dbrm.removeHandler(getConnection(false), object);
}
/**
@@ -118,48 +148,13 @@ public abstract class DBContextBase implements DBContext
public void discard()
{
/* don't close connection! */
- discardAllHandlers();
- }
-
- /**
- * Discards all rollback handlers
- */
- protected void discardAllHandlers()
- { // rollback
- if (rollbackHandler==null)
- return;
- for (DBRollbackHandler handler : rollbackHandler.values())
- handler.discard();
- rollbackHandler=null;
- }
-
- /**
- * Performs rollback on all rollback handlers
- */
- protected void rollbackAllHandlers()
- { // rollback
- if (rollbackHandler==null)
- return;
- for (DBRollbackHandler handler : rollbackHandler.values())
- handler.rollback();
- rollbackHandler=null;
- }
-
- /**
- * Factory function for Utils creation
- * @return the utils implementation
- */
- protected DBUtils createUtils()
- {
- return new DBUtils(this);
}
/**
* helper to close a connection on discard
*/
protected void closeConnection()
- {
- try
+ { try
{ // close connection
Connection conn = getConnection();
conn.close();
diff --git a/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java b/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java
index 1afbe4e..9897866 100644
--- a/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java
+++ b/empire-db/src/main/java/org/apache/empire/db/context/DBContextStatic.java
@@ -6,6 +6,7 @@ package org.apache.empire.db.context;
import java.sql.Connection;
import org.apache.empire.db.DBDatabaseDriver;
+import org.apache.empire.db.context.DBRollbackManager.ReleaseAction;
public class DBContextStatic extends DBContextBase
{
@@ -13,6 +14,14 @@ public class DBContextStatic extends DBContextBase
private final Connection conn;
private final boolean closeOnDiscard;
+ /**
+ * Global DBRollbackManager
+ *
+ * initialConnectionCapacity = 2
+ * initialObjectCapacity = 16
+ */
+ private static final DBRollbackManager staticRollbackManager = new DBRollbackManager(2, 16);
+
public DBContextStatic(DBDatabaseDriver driver, Connection conn, boolean closeOnDiscard)
{
this.driver = driver;
@@ -30,19 +39,28 @@ public class DBContextStatic extends DBContextBase
{
return driver;
}
-
- @Override
- public Connection getConnection()
- {
- return conn;
- }
@Override
public void discard()
{
super.discard();
// close
- if (closeOnDiscard)
+ if (closeOnDiscard)
+ { // Close the connection
closeConnection();
+ staticRollbackManager.releaseConnection(conn, ReleaseAction.Discard);
+ }
+ }
+
+ @Override
+ protected Connection getConnection(boolean create)
+ {
+ return conn;
+ }
+
+ @Override
+ protected DBRollbackManager getRollbackManager(boolean create)
+ {
+ return staticRollbackManager;
}
}
diff --git a/empire-db/src/main/java/org/apache/empire/db/context/DBRollbackManager.java b/empire-db/src/main/java/org/apache/empire/db/context/DBRollbackManager.java
new file mode 100644
index 0000000..17c6175
--- /dev/null
+++ b/empire-db/src/main/java/org/apache/empire/db/context/DBRollbackManager.java
@@ -0,0 +1,100 @@
+package org.apache.empire.db.context;
+
+import java.sql.Connection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.empire.db.DBObject;
+import org.apache.empire.exceptions.ObjectNotValidException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DBRollbackManager
+{ // Logger
+ private static final Logger log = LoggerFactory.getLogger(DBRollbackManager.class);
+
+ /**
+ * Connection release action
+ * @author rainer
+ */
+ public enum ReleaseAction
+ {
+ Discard,
+ Rollback;
+ }
+
+ private final Map<Integer, Map<DBObject, DBRollbackHandler>> connectionMap;
+ private final int initialObjectCapacity;
+
+ public DBRollbackManager(int initialConnectionCapacity, int initialObjectCapacity)
+ {
+ this.connectionMap = new HashMap<Integer, Map<DBObject, DBRollbackHandler>>(initialConnectionCapacity);
+ this.initialObjectCapacity = initialObjectCapacity;
+ }
+
+ /**
+ * Add a rollback handler for a particular Connection
+ * @param conn
+ * @param handler
+ */
+ public synchronized void addHandler(Connection conn, DBRollbackHandler handler)
+ {
+ Map<DBObject, DBRollbackHandler> handlerMap = connectionMap.get(conn.hashCode());
+ if (handlerMap==null)
+ { handlerMap = new LinkedHashMap<DBObject, DBRollbackHandler>(this.initialObjectCapacity);
+ connectionMap.put(conn.hashCode(), handlerMap);
+ }
+ // check
+ DBObject object = handler.getObject();
+ if (object==null)
+ throw new ObjectNotValidException(handler);
+ // Append or combine
+ if (handlerMap.containsKey(object))
+ handlerMap.get(object).combine(handler);
+ else
+ handlerMap.put(object, handler);
+ }
+
+ /**
+ * Remove the rollback handler for a particular Connection and Object
+ * @param conn
+ * @param object
+ */
+ public synchronized void removeHandler(Connection conn, DBObject object)
+ {
+ if (object==null)
+ { // Discard all
+ releaseConnection(conn, ReleaseAction.Discard);
+ return;
+ }
+ Map<DBObject, DBRollbackHandler> handlerMap = connectionMap.get(conn.hashCode());
+ if (handlerMap==null)
+ return; // Nothing to do
+ // Remover handler
+ DBRollbackHandler handler = handlerMap.remove(object);
+ if (handler==null)
+ return; // No handler
+ // dispose
+ log.info("Rollback handler for object {} was removed", object.getClass().getSimpleName());
+ handler.discard();
+ }
+
+ /**
+ * releaseConnection from handler and perform
+ */
+ public synchronized void releaseConnection(Connection conn, ReleaseAction action)
+ {
+ Map<DBObject, DBRollbackHandler> handlerMap = connectionMap.get(conn.hashCode());
+ if (handlerMap==null)
+ return; // Nothing to do
+ // rollback
+ for (DBRollbackHandler handler : handlerMap.values())
+ if (action==ReleaseAction.Rollback)
+ handler.rollback();
+ else
+ handler.discard();
+ // cleanup
+ connectionMap.remove(conn.hashCode());
+ }
+}