From f708c8d4cfc7ca9446b61b9f26244394cb447e4a Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Fri, 9 Oct 2015 16:03:22 +0000 Subject: [PATCH] Use Bitronix as transaction manager. git-svn-id: https://svn.argeo.org/commons/trunk@8469 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../org/argeo/cms/internal/kernel/Kernel.java | 50 ++++- .../cms/internal/kernel/KernelConstants.java | 2 + .../cms/internal/kernel/KernelUtils.java | 16 +- org.argeo.security.core/bnd.bnd | 1 + .../osgi/useradmin/AbstractUserDirectory.java | 189 ++---------------- .../argeo/osgi/useradmin/LdapUserAdmin.java | 6 +- .../org/argeo/osgi/useradmin/LdifUser.java | 3 +- .../argeo/osgi/useradmin/LdifUserAdmin.java | 6 +- .../useradmin/UserDirectoryWorkingCopy.java | 185 +++++++++++++++++ pom.xml | 2 +- 10 files changed, 269 insertions(+), 191 deletions(-) create mode 100644 org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index 026b12116..32672bef5 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -1,8 +1,14 @@ package org.argeo.cms.internal.kernel; +import static bitronix.tm.TransactionManagerServices.getTransactionManager; +import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizationRegistry; +import static org.argeo.cms.internal.kernel.KernelUtils.getFrameworkProp; +import static org.argeo.cms.internal.kernel.KernelUtils.getOsgiInstancePath; import static org.argeo.jcr.ArgeoJcrConstants.ALIAS_NODE; import static org.argeo.jcr.ArgeoJcrConstants.JCR_REPOSITORY_ALIAS; +import static org.osgi.framework.Constants.FRAMEWORK_UUID; +import java.io.File; import java.lang.management.ManagementFactory; import java.security.PrivilegedAction; import java.util.HashMap; @@ -13,6 +19,7 @@ import javax.jcr.Repository; import javax.jcr.RepositoryFactory; import javax.security.auth.Subject; import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; @@ -21,7 +28,6 @@ import org.apache.jackrabbit.util.TransientFileFactory; import org.argeo.ArgeoException; import org.argeo.ArgeoLogger; import org.argeo.cms.CmsException; -import org.argeo.cms.internal.transaction.SimpleTransactionManager; import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; import org.argeo.jcr.ArgeoJcrConstants; import org.eclipse.equinox.http.servlet.ExtendedHttpService; @@ -32,6 +38,11 @@ import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.useradmin.UserAdmin; +import bitronix.tm.BitronixTransactionManager; +import bitronix.tm.BitronixTransactionSynchronizationRegistry; +import bitronix.tm.Configuration; +import bitronix.tm.TransactionManagerServices; + /** * Argeo CMS Kernel. Responsible for : * */ -final class Kernel implements ServiceListener { +final class Kernel implements KernelConstants, ServiceListener { /* * REGISTERED SERVICES */ private ServiceRegistration loggerReg; private ServiceRegistration tmReg; private ServiceRegistration utReg; - // private ServiceRegistration tsrReg; + private ServiceRegistration tsrReg; private ServiceRegistration repositoryReg; private ServiceRegistration repositoryFactoryReg; private ServiceRegistration userAdminReg; @@ -59,7 +70,8 @@ final class Kernel implements ServiceListener { * SERVICES IMPLEMENTATIONS */ private NodeLogger logger; - private SimpleTransactionManager transactionManager; + private BitronixTransactionManager transactionManager; + private BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry; private OsgiJackrabbitRepositoryFactory repositoryFactory; NodeRepository repository; private NodeUserAdmin userAdmin; @@ -97,7 +109,8 @@ final class Kernel implements ServiceListener { try { // Initialise services logger = new NodeLogger(); - transactionManager = new SimpleTransactionManager(); + // transactionManager = new SimpleTransactionManager(); + initBitronixTransactionManager(); repository = new NodeRepository(bc); repositoryFactory = new OsgiJackrabbitRepositoryFactory(); userAdmin = new NodeUserAdmin(transactionManager, repository); @@ -131,10 +144,28 @@ final class Kernel implements ServiceListener { directorsCut(initDuration); } + private void initBitronixTransactionManager() { + Configuration tmConf = TransactionManagerServices.getConfiguration(); + tmConf.setServerId(getFrameworkProp(FRAMEWORK_UUID)); + + File tmBaseDir = new File(getFrameworkProp(TRANSACTIONS_HOME, + getOsgiInstancePath("transactions"))); + File tmDir1 = new File(tmBaseDir, "btm1"); + tmDir1.mkdirs(); + tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog") + .getAbsolutePath()); + File tmDir2 = new File(tmBaseDir, "btm2"); + tmDir2.mkdirs(); + tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog") + .getAbsolutePath()); + transactionManager = getTransactionManager(); + transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); + } + private void publish() { // Listen to service publication (also ours) bc.addServiceListener(Kernel.this); - + // Logging loggerReg = bc.registerService(ArgeoLogger.class, logger, null); // Transaction @@ -142,8 +173,8 @@ final class Kernel implements ServiceListener { transactionManager, null); utReg = bc.registerService(UserTransaction.class, transactionManager, null); - // tsrReg = bc.registerService(TransactionSynchronizationRegistry.class, - // transactionManager.getTsr(), null); + tsrReg = bc.registerService(TransactionSynchronizationRegistry.class, + transactionSynchronizationRegistry, null); // User admin userAdminReg = bc.registerService(UserAdmin.class, userAdmin, userAdmin.currentState()); @@ -168,6 +199,8 @@ final class Kernel implements ServiceListener { userAdmin.destroy(); if (repository != null) repository.destroy(); + if (transactionManager != null) + transactionManager.shutdown(); bc.removeServiceListener(this); @@ -189,6 +222,7 @@ final class Kernel implements ServiceListener { repositoryReg.unregister(); tmReg.unregister(); utReg.unregister(); + tsrReg.unregister(); loggerReg.unregister(); } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java index 663c2b937..f528f6f70 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelConstants.java @@ -16,6 +16,8 @@ public interface KernelConstants { final static String REPO_SEARCH_CACHE_SIZE = "argeo.node.repo.searchCacheSize"; final static String REPO_MAX_VOLATILE_INDEX_SIZE = "argeo.node.repo.maxVolatileIndexSize"; + final static String TRANSACTIONS_HOME = "argeo.node.transactions.home"; + // Node Security final static String ROLES_URI = "argeo.node.roles.uri"; /** URI to an LDIF file or LDAP server used as initialization or backend */ diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java index b2fb03d8e..c1e208924 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/KernelUtils.java @@ -47,6 +47,19 @@ class KernelUtils implements KernelConstants { .getAbsoluteFile(); } + static String getOsgiInstancePath(String relativePath) { + try { + if (relativePath == null) + return getOsgiInstanceDir().getCanonicalPath(); + else + return new File(getOsgiInstanceDir(), relativePath) + .getCanonicalPath(); + } catch (IOException e) { + throw new CmsException("Cannot get instance path for " + + relativePath, e); + } + } + static File getOsgiConfigurationFile(String relativePath) { try { return new File(new URI(Activator.getBundleContext().getProperty( @@ -74,7 +87,8 @@ class KernelUtils implements KernelConstants { Subject subject = new Subject(); LoginContext lc; try { - lc = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, subject); + lc = new LoginContext(AuthConstants.LOGIN_CONTEXT_ANONYMOUS, + subject); lc.login(); return subject; } catch (LoginException e) { diff --git a/org.argeo.security.core/bnd.bnd b/org.argeo.security.core/bnd.bnd index d31b9e77a..6bee2a949 100644 --- a/org.argeo.security.core/bnd.bnd +++ b/org.argeo.security.core/bnd.bnd @@ -1,4 +1,5 @@ Import-Package:org.bouncycastle.*;resolution:=optional,\ +bitronix.tm.*;resolution:=optional,\ javax.jcr.security,\ org.apache.commons.codec,\ org.apache.commons.codec.digest,\ 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 ae931039d..e79800f10 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 @@ -13,11 +13,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; import java.util.Enumeration; -import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; -import java.util.Map; import javax.naming.InvalidNameException; import javax.naming.directory.Attributes; @@ -28,12 +26,8 @@ import javax.naming.ldap.Rdn; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; -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 org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -44,9 +38,6 @@ import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { - private final static Log log = LogFactory - .getLog(AbstractUserDirectory.class); - private final Hashtable properties; private final String baseDn; private final String userObjectClass; @@ -64,7 +55,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { .asList(new String[] { LdifName.userpassword.name() }); private TransactionManager transactionManager; - private ThreadLocal workingCopy = new ThreadLocal(); + private ThreadLocal workingCopy = new ThreadLocal(); private Xid editingTransactionXid = null; AbstractUserDirectory(Dictionary props) { @@ -121,11 +112,11 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return workingCopy.get() != null; } - protected WorkingCopy getWorkingCopy() { - WorkingCopy wc = workingCopy.get(); + protected UserDirectoryWorkingCopy getWorkingCopy() { + UserDirectoryWorkingCopy wc = workingCopy.get(); if (wc == null) return null; - if (wc.xid == null) { + if (wc.getXid() == null) { workingCopy.set(null); return null; } @@ -143,7 +134,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { throw new UserDirectoryException( "A transaction needs to be active in order to edit"); if (editingTransactionXid == null) { - WorkingCopy wc = new WorkingCopy(); + UserDirectoryWorkingCopy wc = new UserDirectoryWorkingCopy(this); try { transaction.enlistResource(wc); editingTransactionXid = wc.getXid(); @@ -192,7 +183,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } protected DirectoryUser doGetRole(LdapName dn) { - WorkingCopy wc = getWorkingCopy(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); DirectoryUser user = daoGetRole(dn); if (wc != null) { if (user == null && wc.getNewUsers().containsKey(dn)) @@ -206,7 +197,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @SuppressWarnings("unchecked") @Override public Role[] getRoles(String filter) throws InvalidSyntaxException { - WorkingCopy wc = getWorkingCopy(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; List res = doGetRoles(f); if (wc != null) { @@ -274,7 +265,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @Override public Role createRole(String name, int type) { checkEdit(); - WorkingCopy wc = getWorkingCopy(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); LdapName dn = toDn(name); if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) @@ -323,7 +314,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @Override public boolean removeRole(String name) { checkEdit(); - WorkingCopy wc = getWorkingCopy(); + UserDirectoryWorkingCopy wc = getWorkingCopy(); LdapName dn = toDn(name); boolean actuallyDeleted; if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) { @@ -342,16 +333,20 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } // TRANSACTION - protected void prepare(WorkingCopy wc) { + protected void prepare(UserDirectoryWorkingCopy wc) { } - protected void commit(WorkingCopy wc) { + protected void commit(UserDirectoryWorkingCopy wc) { } - protected void rollback(WorkingCopy wc) { + protected void rollback(UserDirectoryWorkingCopy wc) { + + } + void clearEditingTransactionXid() { + editingTransactionXid = null; } // UTILITIES @@ -430,156 +425,4 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { this.transactionManager = transactionManager; } - // - // XA RESOURCE - // - protected class WorkingCopy implements XAResource { - private Xid xid; - private int transactionTimeout = 0; - - private Map newUsers = new HashMap(); - private Map modifiedUsers = new HashMap(); - private Map deletedUsers = new HashMap(); - - @Override - public void start(Xid xid, int flags) throws XAException { - if (editingTransactionXid != null) - throw new UserDirectoryException("Transaction " - + editingTransactionXid + " already editing"); - this.xid = xid; - } - - @Override - public void end(Xid xid, int flags) throws XAException { - checkXid(xid); - - // clean collections - newUsers.clear(); - newUsers = null; - modifiedUsers.clear(); - modifiedUsers = null; - deletedUsers.clear(); - deletedUsers = null; - - // clean IDs - this.xid = null; - editingTransactionXid = null; - } - - @Override - public int prepare(Xid xid) throws XAException { - checkXid(xid); - if (noModifications()) - return XA_RDONLY; - try { - AbstractUserDirectory.this.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 { - checkXid(xid); - if (noModifications()) - return; - try { - if (onePhase) - AbstractUserDirectory.this.prepare(this); - AbstractUserDirectory.this.commit(this); - } catch (Exception e) { - log.error("Cannot commit " + xid, e); - throw new XAException(XAException.XA_RBOTHER); - } - } - - @Override - public void rollback(Xid xid) throws XAException { - checkXid(xid); - try { - AbstractUserDirectory.this.rollback(this); - } catch (Exception e) { - log.error("Cannot rollback " + xid, e); - throw new XAException(XAException.XA_HEURMIX); - } - } - - @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 { - throw new UnsupportedOperationException(); - } - - @Override - public int getTransactionTimeout() throws XAException { - return transactionTimeout; - } - - @Override - public boolean setTransactionTimeout(int seconds) throws XAException { - transactionTimeout = seconds; - return true; - } - - private 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); - } - - @Override - protected void finalize() throws Throwable { - if (editingTransactionXid != null) - log.warn("Editing transaction still referenced but no working copy " - + editingTransactionXid); - editingTransactionXid = null; - } - - public boolean noModifications() { - return newUsers.size() == 0 && modifiedUsers.size() == 0 - && deletedUsers.size() == 0; - } - - public Attributes getAttributes(LdapName dn) { - if (modifiedUsers.containsKey(dn)) - return modifiedUsers.get(dn); - return null; - } - - public void startEditing(DirectoryUser user) { - LdapName dn = user.getDn(); - if (modifiedUsers.containsKey(dn)) - throw new UserDirectoryException("Already editing " + dn); - modifiedUsers.put(dn, (Attributes) user.getAttributes().clone()); - } - - public Map getNewUsers() { - return newUsers; - } - - public Map getDeletedUsers() { - return deletedUsers; - } - - public Map getModifiedUsers() { - return modifiedUsers; - } - - } } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java index ef212fa27..1dc4df981 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -183,7 +183,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void prepare(WorkingCopy wc) { + protected void prepare(UserDirectoryWorkingCopy wc) { try { getLdapContext().reconnect(getLdapContext().getConnectControls()); // delete @@ -214,7 +214,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void commit(WorkingCopy wc) { + protected void commit(UserDirectoryWorkingCopy wc) { try { // delete for (LdapName dn : wc.getDeletedUsers().keySet()) { @@ -237,7 +237,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void rollback(WorkingCopy wc) { + protected void rollback(UserDirectoryWorkingCopy wc) { // prepare not impacting } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java index 4f0a56a60..41f33c3ba 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUser.java @@ -20,7 +20,6 @@ import javax.naming.ldap.LdapName; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; -import org.argeo.osgi.useradmin.AbstractUserDirectory.WorkingCopy; /** Directory user implementation */ class LdifUser implements DirectoryUser { @@ -144,7 +143,7 @@ class LdifUser implements DirectoryUser { return getWc() != null && getModifiedAttributes() != null; } - private synchronized WorkingCopy getWc() { + private synchronized UserDirectoryWorkingCopy getWc() { return userAdmin.getWorkingCopy(); } diff --git a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java index 4613ef528..bee73e54d 100644 --- a/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -166,7 +166,7 @@ public class LdifUserAdmin extends AbstractUserDirectory { } @Override - protected void prepare(WorkingCopy wc) { + protected void prepare(UserDirectoryWorkingCopy wc) { // delete for (LdapName dn : wc.getDeletedUsers().keySet()) { if (users.containsKey(dn)) @@ -204,12 +204,12 @@ public class LdifUserAdmin extends AbstractUserDirectory { } @Override - protected void commit(WorkingCopy wc) { + protected void commit(UserDirectoryWorkingCopy wc) { save(); } @Override - protected void rollback(WorkingCopy wc) { + protected void rollback(UserDirectoryWorkingCopy wc) { init(); } 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 new file mode 100644 index 000000000..d2bd9ffb6 --- /dev/null +++ b/org.argeo.security.core/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java @@ -0,0 +1,185 @@ +package org.argeo.osgi.useradmin; + +import java.util.HashMap; +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; + + 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() { + // clean collections + newUsers.clear(); + newUsers = null; + modifiedUsers.clear(); + 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() { + return newUsers.size() == 0 && modifiedUsers.size() == 0 + && deletedUsers.size() == 0; + } + + public Attributes getAttributes(LdapName dn) { + if (modifiedUsers.containsKey(dn)) + return modifiedUsers.get(dn); + return null; + } + + public void startEditing(DirectoryUser user) { + LdapName dn = user.getDn(); + if (modifiedUsers.containsKey(dn)) + throw new UserDirectoryException("Already editing " + dn); + modifiedUsers.put(dn, (Attributes) user.getAttributes().clone()); + } + + public Map getNewUsers() { + return newUsers; + } + + public Map getDeletedUsers() { + return deletedUsers; + } + + public Map getModifiedUsers() { + return modifiedUsers; + } + +} diff --git a/pom.xml b/pom.xml index 7e7ad7591..04d5d5b6c 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ 2.1.29-SNAPSHOT 2.1 2012-12-19 - 2.1.10 + 2.1.11 6 /srv/rpmfactory/argeo-osgi-2-staging/6/x86_64 -- 2.30.2