From: Mathieu Baudier Date: Thu, 15 Oct 2015 16:49:58 +0000 (+0000) Subject: Fix XA resource implementation. X-Git-Tag: argeo-commons-2.1.30~84 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=070656b6431e699a0f6ac8dc1839bce3fc2509ce;p=lgpl%2Fargeo-commons.git Fix XA resource implementation. git-svn-id: https://svn.argeo.org/commons/trunk@8493 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java index 77c8fdc33..d6d721f6d 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -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) { diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index 58eb41ca3..671f63496 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -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 workingCopy = new ThreadLocal(); - private Xid editingTransactionXid = null; + // private TransactionSynchronizationRegistry transactionRegistry; + // private Xid editingTransactionXid = null; + private WcXaResource xaResource = new WcXaResource(this); AbstractUserDirectory(Dictionary props) { properties = new Hashtable(); @@ -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; + } + } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java index 9e4cc25ad..f13ad4922 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectory.java @@ -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(); } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java index d2bd9ffb6..0e25bdfa1 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java @@ -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 newUsers = new HashMap(); private Map modifiedUsers = new HashMap(); private Map deletedUsers = new HashMap(); - 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 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 index 000000000..f438a2a67 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/WcXaResource.java @@ -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 workingCopies = new HashMap(); + 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); + } + +}