Fix XA resource implementation.
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 15 Oct 2015 16:49:58 +0000 (16:49 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 15 Oct 2015 16:49:58 +0000 (16:49 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@8493 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java
org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java [new file with mode: 0644]

index 77c8fdc336d1e3bc66851db88a8ee5fc8a754ab7..d6d721f6dcc9e33c84a765566b6e128760870e92 100644 (file)
@@ -48,6 +48,8 @@ import org.osgi.service.useradmin.Role;
 import org.osgi.service.useradmin.User;
 import org.osgi.service.useradmin.UserAdmin;
 
+import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
+
 /**
  * Aggregates multiple {@link UserDirectory} and integrates them with this node
  * system roles.
@@ -75,6 +77,8 @@ public class NodeUserAdmin implements UserAdmin, KernelConstants {
        private Repository repository;
        private Session adminSession;
 
+       private final String cacheName = UserDirectory.class.getName();
+
        public NodeUserAdmin(TransactionManager transactionManager,
                        Repository repository) {
                this.repository = repository;
@@ -127,7 +131,15 @@ public class NodeUserAdmin implements UserAdmin, KernelConstants {
                        if (userAdmins.get(name) instanceof UserDirectory) {
                                UserDirectory userDirectory = (UserDirectory) userAdmins
                                                .get(name);
+                               try {
+                                       // FIXME Make it less bitronix dependant
+                                       EhCacheXAResourceProducer.unregisterXAResource(cacheName,
+                                                       userDirectory.getXaResource());
+                               } catch (Exception e) {
+                                       log.error("Cannot unregister resource from Bitronix", e);
+                               }
                                userDirectory.destroy();
+
                        }
                }
        }
@@ -195,7 +207,7 @@ public class NodeUserAdmin implements UserAdmin, KernelConstants {
        //
        // USER ADMIN AGGREGATOR
        //
-       public synchronized void addUserAdmin(String baseDn, UserAdmin userAdmin) {
+       public void addUserAdmin(String baseDn, UserAdmin userAdmin) {
                if (userAdmins.containsKey(baseDn))
                        throw new UserDirectoryException(
                                        "There is already a user admin for " + baseDn);
@@ -205,6 +217,15 @@ public class NodeUserAdmin implements UserAdmin, KernelConstants {
                        throw new UserDirectoryException("Badly formatted base DN "
                                        + baseDn, e);
                }
+               if (userAdmin instanceof UserDirectory) {
+                       try {
+                               // FIXME Make it less bitronix dependant
+                               EhCacheXAResourceProducer.registerXAResource(cacheName,
+                                               ((UserDirectory) userAdmin).getXaResource());
+                       } catch (Exception e) {
+                               log.error("Cannot register resource to Bitronix", e);
+                       }
+               }
        }
 
        private UserAdmin findUserAdmin(String name) {
index 58eb41ca377f0d3254825960ef75a8ec9bebcc18..671f634969bd8f49db28a20eabace94a02df6c17 100644 (file)
@@ -26,7 +26,6 @@ import javax.naming.ldap.Rdn;
 import javax.transaction.SystemException;
 import javax.transaction.Transaction;
 import javax.transaction.TransactionManager;
-import javax.transaction.xa.Xid;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -60,8 +59,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                        .asList(new String[] { LdifName.userpassword.name() });
 
        private TransactionManager transactionManager;
-       private ThreadLocal<UserDirectoryWorkingCopy> workingCopy = new ThreadLocal<UserDirectoryWorkingCopy>();
-       private Xid editingTransactionXid = null;
+       // private TransactionSynchronizationRegistry transactionRegistry;
+       // private Xid editingTransactionXid = null;
+       private WcXaResource xaResource = new WcXaResource(this);
 
        AbstractUserDirectory(Dictionary<String, ?> props) {
                properties = new Hashtable<String, Object>();
@@ -112,19 +112,21 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
        }
 
        boolean isEditing() {
-               if (editingTransactionXid == null)
-                       return false;
-               return workingCopy.get() != null;
+               // if (editingTransactionXid == null)
+               // return false;
+               // return workingCopy.get() != null;
+               return xaResource.wc() != null;
        }
 
        protected UserDirectoryWorkingCopy getWorkingCopy() {
-               UserDirectoryWorkingCopy wc = workingCopy.get();
+               // UserDirectoryWorkingCopy wc = workingCopy.get();
+               UserDirectoryWorkingCopy wc = xaResource.wc();
                if (wc == null)
                        return null;
-               if (wc.getXid() == null) {
-                       workingCopy.set(null);
-                       return null;
-               }
+               // if (wc.getXid() == null) {
+               // workingCopy.set(null);
+               // return null;
+               // }
                return wc;
        }
 
@@ -138,23 +140,26 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                if (transaction == null)
                        throw new UserDirectoryException(
                                        "A transaction needs to be active in order to edit");
-               if (editingTransactionXid == null) {
-                       UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this);
+               if (xaResource.wc() == null) {
+                       // UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this);
                        try {
-                               transaction.enlistResource(wc);
-                               editingTransactionXid = wc.getXid();
-                               workingCopy.set(wc);
+                               transaction.enlistResource(xaResource);
+                               // editingTransactionXid = wc.getXid();
+                               // workingCopy.set(wc);
                        } catch (Exception e) {
-                               throw new UserDirectoryException("Cannot enlist " + wc, e);
+                               throw new UserDirectoryException("Cannot enlist " + xaResource,
+                                               e);
                        }
                } else {
-                       if (workingCopy.get() == null)
-                               throw new UserDirectoryException("Transaction "
-                                               + editingTransactionXid + " already editing");
-                       else if (!editingTransactionXid.equals(workingCopy.get().getXid()))
-                               throw new UserDirectoryException("Working copy Xid "
-                                               + workingCopy.get().getXid() + " inconsistent with"
-                                               + editingTransactionXid);
+                       // UserDirectoryWorkingCopy wc = xaResource.wc();
+                       // if (wc == null)
+                       // throw new UserDirectoryException("Transaction "
+                       // + editingTransactionXid + " already editing");
+                       // else if
+                       // (!editingTransactionXid.equals(workingCopy.get().getXid()))
+                       // throw new UserDirectoryException("Working copy Xid "
+                       // + workingCopy.get().getXid() + " inconsistent with"
+                       // + editingTransactionXid);
                }
        }
 
@@ -353,9 +358,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
 
        }
 
-       void clearEditingTransactionXid() {
-               editingTransactionXid = null;
-       }
+       // void clearEditingTransactionXid() {
+       // editingTransactionXid = null;
+       // }
 
        // UTILITIES
        protected LdapName toDn(String name) {
@@ -433,4 +438,8 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
                this.transactionManager = transactionManager;
        }
 
+       public WcXaResource getXaResource() {
+               return xaResource;
+       }
+
 }
index 9e4cc25ad10c062dd496201407a1b76b373f36fe..f13ad4922da1805883b8eb209ede7e5d381a1bfa 100644 (file)
@@ -3,6 +3,7 @@ package org.argeo.osgi.useradmin;
 import java.util.Dictionary;
 
 import javax.transaction.TransactionManager;
+import javax.transaction.xa.XAResource;
 
 import org.osgi.service.useradmin.UserAdmin;
 
@@ -21,4 +22,6 @@ public interface UserDirectory {
        public void init();
 
        public void destroy();
+
+       public XAResource getXaResource();
 }
index d2bd9ffb611b3328772579ec574a57ea4fc29a5e..0e25bdfa12c441459e4f6bbee26353aa588ec5b2 100644 (file)
@@ -5,52 +5,18 @@ import java.util.Map;
 
 import javax.naming.directory.Attributes;
 import javax.naming.ldap.LdapName;
-import javax.transaction.xa.XAException;
 import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import bitronix.tm.resource.ehcache.EhCacheXAResourceProducer;
 
 /** {@link XAResource} for a user directory being edited. */
-class UserDirectoryWorkingCopy implements XAResource {
-       private final static Log log = LogFactory
-                       .getLog(UserDirectoryWorkingCopy.class);
-       private final String cacheName = getClass().getName();
-
-       private final AbstractUserDirectory userDirectory;
-
-       private Xid xid;
-       private int transactionTimeout = 0;
+class UserDirectoryWorkingCopy {
+       // private final static Log log = LogFactory
+       // .getLog(UserDirectoryWorkingCopy.class);
 
        private Map<LdapName, DirectoryUser> newUsers = new HashMap<LdapName, DirectoryUser>();
        private Map<LdapName, Attributes> modifiedUsers = new HashMap<LdapName, Attributes>();
        private Map<LdapName, DirectoryUser> deletedUsers = new HashMap<LdapName, DirectoryUser>();
 
-       public UserDirectoryWorkingCopy(AbstractUserDirectory userDirectory) {
-               this.userDirectory = userDirectory;
-               try {
-                       // FIXME Make it less bitronix dependant
-                       EhCacheXAResourceProducer.registerXAResource(cacheName, this);
-               } catch (Exception e) {
-                       log.error("Cannot register resource to Bitronix", e);
-               }
-       }
-
-       @Override
-       public void start(Xid xid, int flags) throws XAException {
-               this.xid = xid;
-       }
-
-       @Override
-       public void end(Xid xid, int flags) throws XAException {
-               checkXid(xid);
-
-       }
-
-       private void cleanUp() {
+       void cleanUp() {
                // clean collections
                newUsers.clear();
                newUsers = null;
@@ -58,98 +24,6 @@ class UserDirectoryWorkingCopy implements XAResource {
                modifiedUsers = null;
                deletedUsers.clear();
                deletedUsers = null;
-
-               // clean IDs
-               this.xid = null;
-               userDirectory.clearEditingTransactionXid();
-
-               try {
-                       // FIXME Make it less bitronix dependant
-                       EhCacheXAResourceProducer.unregisterXAResource(cacheName, this);
-               } catch (Exception e) {
-                       log.error("Cannot unregister resource from Bitronix", e);
-               }
-       }
-
-       @Override
-       public int prepare(Xid xid) throws XAException {
-               checkXid(xid);
-               if (noModifications())
-                       return XA_RDONLY;
-               try {
-                       userDirectory.prepare(this);
-               } catch (Exception e) {
-                       log.error("Cannot prepare " + xid, e);
-                       throw new XAException(XAException.XA_RBOTHER);
-               }
-               return XA_OK;
-       }
-
-       @Override
-       public void commit(Xid xid, boolean onePhase) throws XAException {
-               try {
-                       checkXid(xid);
-                       if (noModifications())
-                               return;
-                       if (onePhase)
-                               userDirectory.prepare(this);
-                       userDirectory.commit(this);
-               } catch (Exception e) {
-                       log.error("Cannot commit " + xid, e);
-                       throw new XAException(XAException.XA_RBOTHER);
-               } finally {
-                       cleanUp();
-               }
-       }
-
-       @Override
-       public void rollback(Xid xid) throws XAException {
-               try {
-                       checkXid(xid);
-                       userDirectory.rollback(this);
-               } catch (Exception e) {
-                       log.error("Cannot rollback " + xid, e);
-                       throw new XAException(XAException.XA_HEURMIX);
-               } finally {
-                       cleanUp();
-               }
-       }
-
-       @Override
-       public void forget(Xid xid) throws XAException {
-               throw new UnsupportedOperationException();
-       }
-
-       @Override
-       public boolean isSameRM(XAResource xares) throws XAException {
-               return xares == this;
-       }
-
-       @Override
-       public Xid[] recover(int flag) throws XAException {
-               return new Xid[0];
-       }
-
-       @Override
-       public int getTransactionTimeout() throws XAException {
-               return transactionTimeout;
-       }
-
-       @Override
-       public boolean setTransactionTimeout(int seconds) throws XAException {
-               transactionTimeout = seconds;
-               return true;
-       }
-
-       Xid getXid() {
-               return xid;
-       }
-
-       private void checkXid(Xid xid) throws XAException {
-               if (this.xid == null)
-                       throw new XAException(XAException.XAER_OUTSIDE);
-               if (!this.xid.equals(xid))
-                       throw new XAException(XAException.XAER_NOTA);
        }
 
        public boolean noModifications() {
@@ -181,5 +55,4 @@ class UserDirectoryWorkingCopy implements XAResource {
        public Map<LdapName, Attributes> getModifiedUsers() {
                return modifiedUsers;
        }
-
 }
diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java
new file mode 100644 (file)
index 0000000..f438a2a
--- /dev/null
@@ -0,0 +1,147 @@
+package org.argeo.osgi.useradmin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/** {@link XAResource} for a user directory being edited. */
+class WcXaResource implements XAResource {
+       private final static Log log = LogFactory.getLog(WcXaResource.class);
+
+       private final AbstractUserDirectory userDirectory;
+
+       private Map<Xid, UserDirectoryWorkingCopy> workingCopies = new HashMap<Xid, UserDirectoryWorkingCopy>();
+       private Xid editingXid = null;
+       private int transactionTimeout = 0;
+
+       public WcXaResource(AbstractUserDirectory userDirectory) {
+               this.userDirectory = userDirectory;
+       }
+
+       @Override
+       public void start(Xid xid, int flags) throws XAException {
+               if (editingXid != null)
+                       throw new UserDirectoryException("Already editing " + editingXid);
+               UserDirectoryWorkingCopy wc = workingCopies.put(xid,
+                               new UserDirectoryWorkingCopy());
+               if (wc != null)
+                       throw new UserDirectoryException(
+                                       "There is already a working copy for " + xid);
+               this.editingXid = xid;
+       }
+
+       @Override
+       public void end(Xid xid, int flags) throws XAException {
+               checkXid(xid);
+
+       }
+
+       private UserDirectoryWorkingCopy wc(Xid xid) {
+               return workingCopies.get(xid);
+       }
+
+       UserDirectoryWorkingCopy wc() {
+               if (editingXid == null)
+                       return null;
+               UserDirectoryWorkingCopy wc = workingCopies.get(editingXid);
+               if (wc == null)
+                       throw new UserDirectoryException("No working copy found for "
+                                       + editingXid);
+               return wc;
+       }
+
+       private void cleanUp(Xid xid) {
+               // clean collections
+               wc(xid).cleanUp();
+               workingCopies.remove(xid);
+
+               // clean IDs
+               // userDirectory.clearEditingTransactionXid();
+       }
+
+       @Override
+       public int prepare(Xid xid) throws XAException {
+               checkXid(xid);
+               UserDirectoryWorkingCopy wc = wc(xid);
+               if (wc.noModifications())
+                       return XA_RDONLY;
+               try {
+                       userDirectory.prepare(wc);
+               } catch (Exception e) {
+                       log.error("Cannot prepare " + xid, e);
+                       throw new XAException(XAException.XA_RBOTHER);
+               }
+               return XA_OK;
+       }
+
+       @Override
+       public void commit(Xid xid, boolean onePhase) throws XAException {
+               try {
+                       checkXid(xid);
+                       UserDirectoryWorkingCopy wc = wc(xid);
+                       if (wc.noModifications())
+                               return;
+                       if (onePhase)
+                               userDirectory.prepare(wc);
+                       userDirectory.commit(wc);
+               } catch (Exception e) {
+                       log.error("Cannot commit " + xid, e);
+                       throw new XAException(XAException.XA_RBOTHER);
+               } finally {
+                       cleanUp(xid);
+               }
+       }
+
+       @Override
+       public void rollback(Xid xid) throws XAException {
+               try {
+                       checkXid(xid);
+                       userDirectory.rollback(wc(xid));
+               } catch (Exception e) {
+                       log.error("Cannot rollback " + xid, e);
+                       throw new XAException(XAException.XA_HEURMIX);
+               } finally {
+                       cleanUp(xid);
+               }
+       }
+
+       @Override
+       public void forget(Xid xid) throws XAException {
+               throw new UnsupportedOperationException();
+       }
+
+       @Override
+       public boolean isSameRM(XAResource xares) throws XAException {
+               return xares == this;
+       }
+
+       @Override
+       public Xid[] recover(int flag) throws XAException {
+               return new Xid[0];
+       }
+
+       @Override
+       public int getTransactionTimeout() throws XAException {
+               return transactionTimeout;
+       }
+
+       @Override
+       public boolean setTransactionTimeout(int seconds) throws XAException {
+               transactionTimeout = seconds;
+               return true;
+       }
+
+       private void checkXid(Xid xid) throws XAException {
+               if (xid == null)
+                       throw new XAException(XAException.XAER_OUTSIDE);
+               if (!xid.equals(xid))
+                       throw new XAException(XAException.XAER_NOTA);
+       }
+
+}