From d74f9b604d0132a6b66c7a2dc189be2c2798b7c4 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 22 Jun 2022 07:57:08 +0200 Subject: [PATCH] Introduce transaction working copy abstraction --- .../osgi/useradmin/AbstractUserDirectory.java | 66 ++++----- .../useradmin/DirectoryUserWorkingCopy.java | 19 +++ .../argeo/osgi/useradmin/LdapConnection.java | 22 +-- .../argeo/osgi/useradmin/LdapUserAdmin.java | 6 +- .../org/argeo/osgi/useradmin/LdifGroup.java | 3 + .../org/argeo/osgi/useradmin/LdifUser.java | 5 +- .../argeo/osgi/useradmin/LdifUserAdmin.java | 16 +-- .../argeo/osgi/useradmin/OsUserDirectory.java | 13 ++ .../src/org/argeo/osgi/useradmin/Person.java | 5 - .../useradmin/UserDirectoryWorkingCopy.java | 57 -------- .../argeo/osgi/useradmin/WcXaResource.java | 14 +- .../directory}/FunctionalGroup.java | 2 +- .../directory}/Organization.java | 2 +- .../src/org/argeo/util/directory/Person.java | 5 + .../directory}/SystemPermissions.java | 2 +- .../util/transaction/AbstractWorkingCopy.java | 48 +++++++ .../argeo/util/transaction/WorkingCopy.java | 18 +++ .../transaction/WorkingCopyProcessor.java | 11 ++ .../transaction/WorkingCopyXaResource.java | 135 ++++++++++++++++++ .../util/transaction/XAResourceProvider.java | 7 + 20 files changed, 327 insertions(+), 129 deletions(-) create mode 100644 org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java delete mode 100644 org.argeo.util/src/org/argeo/osgi/useradmin/Person.java delete mode 100644 org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory}/FunctionalGroup.java (53%) rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory}/Organization.java (51%) create mode 100644 org.argeo.util/src/org/argeo/util/directory/Person.java rename org.argeo.util/src/org/argeo/{osgi/useradmin => util/directory}/SystemPermissions.java (54%) create mode 100644 org.argeo.util/src/org/argeo/util/transaction/AbstractWorkingCopy.java create mode 100644 org.argeo.util/src/org/argeo/util/transaction/WorkingCopy.java create mode 100644 org.argeo.util/src/org/argeo/util/transaction/WorkingCopyProcessor.java create mode 100644 org.argeo.util/src/org/argeo/util/transaction/WorkingCopyXaResource.java create mode 100644 org.argeo.util/src/org/argeo/util/transaction/XAResourceProvider.java diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index e5576b91d..838c2ce0b 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -30,10 +30,14 @@ import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; +import javax.transaction.xa.XAResource; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.argeo.util.transaction.WorkControl; +import org.argeo.util.transaction.WorkingCopyProcessor; +import org.argeo.util.transaction.WorkingCopyXaResource; +import org.argeo.util.transaction.XAResourceProvider; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -43,7 +47,8 @@ import org.osgi.service.useradmin.User; import org.osgi.service.useradmin.UserAdmin; /** Base class for a {@link UserDirectory}. */ -abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { +abstract class AbstractUserDirectory + implements UserAdmin, UserDirectory, WorkingCopyProcessor, XAResourceProvider { static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name"; static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password"; @@ -71,7 +76,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { // Transaction // private TransactionManager transactionManager; private WorkControl transactionControl; - private WcXaResource xaResource = new WcXaResource(this); + private WorkingCopyXaResource xaResource = new WorkingCopyXaResource<>(this); private String forcedPassword; @@ -248,8 +253,8 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return xaResource.wc() != null; } - protected UserDirectoryWorkingCopy getWorkingCopy() { - UserDirectoryWorkingCopy wc = xaResource.wc(); + protected DirectoryUserWorkingCopy getWorkingCopy() { + DirectoryUserWorkingCopy wc = xaResource.wc(); if (wc == null) return null; return wc; @@ -317,7 +322,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } protected DirectoryUser doGetRole(LdapName dn) { - UserDirectoryWorkingCopy wc = getWorkingCopy(); + DirectoryUserWorkingCopy wc = getWorkingCopy(); DirectoryUser user; try { user = daoGetRole(dn); @@ -325,9 +330,9 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { user = null; } if (wc != null) { - if (user == null && wc.getNewUsers().containsKey(dn)) - user = wc.getNewUsers().get(dn); - else if (wc.getDeletedUsers().containsKey(dn)) + if (user == null && wc.getNewData().containsKey(dn)) + user = wc.getNewData().get(dn); + else if (wc.getDeletedData().containsKey(dn)) user = null; } return user; @@ -340,17 +345,17 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { } List getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException { - UserDirectoryWorkingCopy wc = getWorkingCopy(); + DirectoryUserWorkingCopy wc = getWorkingCopy(); Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null; List res = doGetRoles(searchBase, f, deep); if (wc != null) { for (Iterator it = res.iterator(); it.hasNext();) { DirectoryUser user = it.next(); LdapName dn = user.getDn(); - if (wc.getDeletedUsers().containsKey(dn)) + if (wc.getDeletedData().containsKey(dn)) it.remove(); } - for (DirectoryUser user : wc.getNewUsers().values()) { + for (DirectoryUser user : wc.getNewData().values()) { if (f == null || f.match(user.getProperties())) res.add(user); } @@ -427,23 +432,23 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @Override public Role createRole(String name, int type) { checkEdit(); - UserDirectoryWorkingCopy wc = getWorkingCopy(); + DirectoryUserWorkingCopy wc = getWorkingCopy(); LdapName dn = toLdapName(name); - if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn)) + if ((daoHasRole(dn) && !wc.getDeletedData().containsKey(dn)) || wc.getNewData().containsKey(dn)) throw new IllegalArgumentException("Already a role " + name); BasicAttributes attrs = new BasicAttributes(true); // attrs.put(LdifName.dn.name(), dn.toString()); Rdn nameRdn = dn.getRdn(dn.size() - 1); // TODO deal with multiple attr RDN attrs.put(nameRdn.getType(), nameRdn.getValue()); - if (wc.getDeletedUsers().containsKey(dn)) { - wc.getDeletedUsers().remove(dn); - wc.getModifiedUsers().put(dn, attrs); + if (wc.getDeletedData().containsKey(dn)) { + wc.getDeletedData().remove(dn); + wc.getModifiedData().put(dn, attrs); return getRole(name); } else { - wc.getModifiedUsers().put(dn, attrs); + wc.getModifiedData().put(dn, attrs); DirectoryUser newRole = newRole(dn, type, attrs); - wc.getNewUsers().put(dn, newRole); + wc.getNewData().put(dn, newRole); return newRole; } } @@ -479,12 +484,12 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { @Override public boolean removeRole(String name) { checkEdit(); - UserDirectoryWorkingCopy wc = getWorkingCopy(); + DirectoryUserWorkingCopy wc = getWorkingCopy(); LdapName dn = toLdapName(name); boolean actuallyDeleted; - if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) { + if (daoHasRole(dn) || wc.getNewData().containsKey(dn)) { DirectoryUser user = (DirectoryUser) getRole(name); - wc.getDeletedUsers().put(dn, user); + wc.getDeletedData().put(dn, user); actuallyDeleted = true; } else {// just removing from groups (e.g. system roles) actuallyDeleted = false; @@ -496,17 +501,12 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { return actuallyDeleted; } - // TRANSACTION - protected void prepare(UserDirectoryWorkingCopy wc) { - - } - - protected void commit(UserDirectoryWorkingCopy wc) { - - } - - protected void rollback(UserDirectoryWorkingCopy wc) { - + /* + * TRANSACTION + */ + @Override + public DirectoryUserWorkingCopy newWorkingCopy() { + return new DirectoryUserWorkingCopy(); } /* @@ -684,7 +684,7 @@ abstract class AbstractUserDirectory implements UserAdmin, UserDirectory { this.transactionControl = transactionControl; } - public WcXaResource getXaResource() { + public XAResource getXaResource() { return xaResource; } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java new file mode 100644 index 000000000..2aed145c7 --- /dev/null +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/DirectoryUserWorkingCopy.java @@ -0,0 +1,19 @@ +package org.argeo.osgi.useradmin; + +import javax.naming.directory.Attributes; +import javax.naming.ldap.LdapName; + +import org.argeo.util.transaction.AbstractWorkingCopy; + +/** Working copy for a user directory being edited. */ +class DirectoryUserWorkingCopy extends AbstractWorkingCopy { + @Override + protected LdapName getId(DirectoryUser user) { + return user.getDn(); + } + + @Override + protected Attributes cloneAttributes(DirectoryUser user) { + return (Attributes) user.getAttributes().clone(); + } +} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java index 1eaf1e7d6..1fe7eb9df 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapConnection.java @@ -98,23 +98,23 @@ class LdapConnection { } } - synchronized void prepareChanges(UserDirectoryWorkingCopy wc) throws NamingException { + synchronized void prepareChanges(DirectoryUserWorkingCopy wc) throws NamingException { // make sure connection will work reconnect(); // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { + for (LdapName dn : wc.getDeletedData().keySet()) { if (!entryExists(dn)) throw new IllegalStateException("User to delete no found " + dn); } // add - for (LdapName dn : wc.getNewUsers().keySet()) { + for (LdapName dn : wc.getNewData().keySet()) { if (entryExists(dn)) throw new IllegalStateException("User to create found " + dn); } // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn)) + for (LdapName dn : wc.getModifiedData().keySet()) { + if (!wc.getNewData().containsKey(dn) && !entryExists(dn)) throw new IllegalStateException("User to modify not found " + dn); } @@ -128,19 +128,19 @@ class LdapConnection { } } - synchronized void commitChanges(UserDirectoryWorkingCopy wc) throws NamingException { + synchronized void commitChanges(DirectoryUserWorkingCopy wc) throws NamingException { // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { + for (LdapName dn : wc.getDeletedData().keySet()) { getLdapContext().destroySubcontext(dn); } // add - for (LdapName dn : wc.getNewUsers().keySet()) { - DirectoryUser user = wc.getNewUsers().get(dn); + for (LdapName dn : wc.getNewData().keySet()) { + DirectoryUser user = wc.getNewData().get(dn); getLdapContext().createSubcontext(dn, user.getAttributes()); } // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); + for (LdapName dn : wc.getModifiedData().keySet()) { + Attributes modifiedAttrs = wc.getModifiedData().get(dn); getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs); } } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java index 82e890b7c..879d5da04 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdapUserAdmin.java @@ -165,7 +165,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void prepare(UserDirectoryWorkingCopy wc) { + public void prepare(DirectoryUserWorkingCopy wc) { try { ldapConnection.prepareChanges(wc); } catch (NamingException e) { @@ -174,7 +174,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void commit(UserDirectoryWorkingCopy wc) { + public void commit(DirectoryUserWorkingCopy wc) { try { ldapConnection.commitChanges(wc); } catch (NamingException e) { @@ -183,7 +183,7 @@ public class LdapUserAdmin extends AbstractUserDirectory { } @Override - protected void rollback(UserDirectoryWorkingCopy wc) { + public void rollback(DirectoryUserWorkingCopy wc) { // prepare not impacting } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java index 80bff5947..72b08a8c3 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifGroup.java @@ -10,6 +10,9 @@ import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.FunctionalGroup; +import org.argeo.util.directory.Organization; +import org.argeo.util.directory.SystemPermissions; import org.osgi.service.useradmin.Role; /** Directory group implementation */ diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java index a20ad85c1..aaac50272 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUser.java @@ -21,6 +21,7 @@ import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.ldap.LdapName; +import org.argeo.util.directory.Person; import org.argeo.util.naming.LdapAttrs; import org.argeo.util.naming.LdapObjs; import org.argeo.util.naming.SharedSecret; @@ -182,14 +183,14 @@ abstract class LdifUser implements DirectoryUser { /** Should only be called from working copy thread. */ private synchronized Attributes getModifiedAttributes() { assert getWc() != null; - return getWc().getAttributes(getDn()); + return getWc().getModifiedData().get(getDn()); } protected synchronized boolean isEditing() { return getWc() != null && getModifiedAttributes() != null; } - private synchronized UserDirectoryWorkingCopy getWc() { + private synchronized DirectoryUserWorkingCopy getWc() { return userAdmin.getWorkingCopy(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java index bf455949d..26d3d134c 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/LdifUserAdmin.java @@ -264,9 +264,9 @@ public class LdifUserAdmin extends AbstractUserDirectory { } @Override - protected void prepare(UserDirectoryWorkingCopy wc) { + public void prepare(DirectoryUserWorkingCopy wc) { // delete - for (LdapName dn : wc.getDeletedUsers().keySet()) { + for (LdapName dn : wc.getDeletedData().keySet()) { if (users.containsKey(dn)) users.remove(dn); else if (groups.containsKey(dn)) @@ -275,8 +275,8 @@ public class LdifUserAdmin extends AbstractUserDirectory { throw new IllegalStateException("User to delete not found " + dn); } // add - for (LdapName dn : wc.getNewUsers().keySet()) { - DirectoryUser user = wc.getNewUsers().get(dn); + for (LdapName dn : wc.getNewData().keySet()) { + DirectoryUser user = wc.getNewData().get(dn); if (users.containsKey(dn) || groups.containsKey(dn)) throw new IllegalStateException("User to create found " + dn); else if (Role.USER == user.getType()) @@ -287,8 +287,8 @@ public class LdifUserAdmin extends AbstractUserDirectory { throw new IllegalStateException("Unsupported role type " + user.getType() + " for new user " + dn); } // modify - for (LdapName dn : wc.getModifiedUsers().keySet()) { - Attributes modifiedAttrs = wc.getModifiedUsers().get(dn); + for (LdapName dn : wc.getModifiedData().keySet()) { + Attributes modifiedAttrs = wc.getModifiedData().get(dn); DirectoryUser user; if (users.containsKey(dn)) user = users.get(dn); @@ -301,12 +301,12 @@ public class LdifUserAdmin extends AbstractUserDirectory { } @Override - protected void commit(UserDirectoryWorkingCopy wc) { + public void commit(DirectoryUserWorkingCopy wc) { save(); } @Override - protected void rollback(UserDirectoryWorkingCopy wc) { + public void rollback(DirectoryUserWorkingCopy wc) { init(); } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java b/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java index 3ded7a7a6..69c06c848 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/OsUserDirectory.java @@ -75,4 +75,17 @@ public class OsUserDirectory extends AbstractUserDirectory { return new ArrayList<>(); } + public void prepare(DirectoryUserWorkingCopy wc) { + + } + + public void commit(DirectoryUserWorkingCopy wc) { + + } + + public void rollback(DirectoryUserWorkingCopy wc) { + + } + + } diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/Person.java b/org.argeo.util/src/org/argeo/osgi/useradmin/Person.java deleted file mode 100644 index 8f6980b97..000000000 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/Person.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.argeo.osgi.useradmin; - -public interface Person { - -} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java b/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java deleted file mode 100644 index 35a34bdbe..000000000 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/UserDirectoryWorkingCopy.java +++ /dev/null @@ -1,57 +0,0 @@ -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.XAResource; - -/** {@link XAResource} for a user directory being edited. */ -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(); - - void cleanUp() { - // clean collections - newUsers.clear(); - newUsers = null; - modifiedUsers.clear(); - modifiedUsers = null; - deletedUsers.clear(); - deletedUsers = 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 IllegalStateException("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.util/src/org/argeo/osgi/useradmin/WcXaResource.java b/org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java index af0f351c7..32bb401bc 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java +++ b/org.argeo.util/src/org/argeo/osgi/useradmin/WcXaResource.java @@ -11,7 +11,7 @@ import javax.transaction.xa.Xid; class WcXaResource implements XAResource { private final AbstractUserDirectory userDirectory; - private Map workingCopies = new HashMap(); + private Map workingCopies = new HashMap(); private Xid editingXid = null; private int transactionTimeout = 0; @@ -23,7 +23,7 @@ class WcXaResource implements XAResource { public synchronized void start(Xid xid, int flags) throws XAException { if (editingXid != null) throw new IllegalStateException("Already editing " + editingXid); - UserDirectoryWorkingCopy wc = workingCopies.put(xid, new UserDirectoryWorkingCopy()); + DirectoryUserWorkingCopy wc = workingCopies.put(xid, new DirectoryUserWorkingCopy()); if (wc != null) throw new IllegalStateException("There is already a working copy for " + xid); this.editingXid = xid; @@ -34,14 +34,14 @@ class WcXaResource implements XAResource { checkXid(xid); } - private UserDirectoryWorkingCopy wc(Xid xid) { + private DirectoryUserWorkingCopy wc(Xid xid) { return workingCopies.get(xid); } - synchronized UserDirectoryWorkingCopy wc() { + synchronized DirectoryUserWorkingCopy wc() { if (editingXid == null) return null; - UserDirectoryWorkingCopy wc = workingCopies.get(editingXid); + DirectoryUserWorkingCopy wc = workingCopies.get(editingXid); if (wc == null) throw new IllegalStateException("No working copy found for " + editingXid); return wc; @@ -56,7 +56,7 @@ class WcXaResource implements XAResource { @Override public int prepare(Xid xid) throws XAException { checkXid(xid); - UserDirectoryWorkingCopy wc = wc(xid); + DirectoryUserWorkingCopy wc = wc(xid); if (wc.noModifications()) return XA_RDONLY; try { @@ -72,7 +72,7 @@ class WcXaResource implements XAResource { public void commit(Xid xid, boolean onePhase) throws XAException { try { checkXid(xid); - UserDirectoryWorkingCopy wc = wc(xid); + DirectoryUserWorkingCopy wc = wc(xid); if (wc.noModifications()) return; if (onePhase) diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/FunctionalGroup.java b/org.argeo.util/src/org/argeo/util/directory/FunctionalGroup.java similarity index 53% rename from org.argeo.util/src/org/argeo/osgi/useradmin/FunctionalGroup.java rename to org.argeo.util/src/org/argeo/util/directory/FunctionalGroup.java index 5f17d8b68..89511aba5 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/FunctionalGroup.java +++ b/org.argeo.util/src/org/argeo/util/directory/FunctionalGroup.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory; public interface FunctionalGroup { diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/Organization.java b/org.argeo.util/src/org/argeo/util/directory/Organization.java similarity index 51% rename from org.argeo.util/src/org/argeo/osgi/useradmin/Organization.java rename to org.argeo.util/src/org/argeo/util/directory/Organization.java index 85b1280a5..bbbdcd923 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/Organization.java +++ b/org.argeo.util/src/org/argeo/util/directory/Organization.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory; public interface Organization { diff --git a/org.argeo.util/src/org/argeo/util/directory/Person.java b/org.argeo.util/src/org/argeo/util/directory/Person.java new file mode 100644 index 000000000..d782ee481 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/directory/Person.java @@ -0,0 +1,5 @@ +package org.argeo.util.directory; + +public interface Person { + +} diff --git a/org.argeo.util/src/org/argeo/osgi/useradmin/SystemPermissions.java b/org.argeo.util/src/org/argeo/util/directory/SystemPermissions.java similarity index 54% rename from org.argeo.util/src/org/argeo/osgi/useradmin/SystemPermissions.java rename to org.argeo.util/src/org/argeo/util/directory/SystemPermissions.java index c386a411e..3ab16b898 100644 --- a/org.argeo.util/src/org/argeo/osgi/useradmin/SystemPermissions.java +++ b/org.argeo.util/src/org/argeo/util/directory/SystemPermissions.java @@ -1,4 +1,4 @@ -package org.argeo.osgi.useradmin; +package org.argeo.util.directory; public interface SystemPermissions { diff --git a/org.argeo.util/src/org/argeo/util/transaction/AbstractWorkingCopy.java b/org.argeo.util/src/org/argeo/util/transaction/AbstractWorkingCopy.java new file mode 100644 index 000000000..0da35ac7b --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/transaction/AbstractWorkingCopy.java @@ -0,0 +1,48 @@ +package org.argeo.util.transaction; + +import java.util.HashMap; +import java.util.Map; + +public abstract class AbstractWorkingCopy implements WorkingCopy { + private Map newData = new HashMap(); + private Map modifiedData = new HashMap(); + private Map deletedData = new HashMap(); + + protected abstract ID getId(DATA data); + + protected abstract ATTR cloneAttributes(DATA data); + + public void cleanUp() { + // clean collections + newData.clear(); + newData = null; + modifiedData.clear(); + modifiedData = null; + deletedData.clear(); + deletedData = null; + } + + public boolean noModifications() { + return newData.size() == 0 && modifiedData.size() == 0 && deletedData.size() == 0; + } + + public void startEditing(DATA user) { + ID id = getId(user); + if (modifiedData.containsKey(id)) + throw new IllegalStateException("Already editing " + id); + modifiedData.put(id, cloneAttributes(user)); + } + + public Map getNewData() { + return newData; + } + + public Map getDeletedData() { + return deletedData; + } + + public Map getModifiedData() { + return modifiedData; + } + +} diff --git a/org.argeo.util/src/org/argeo/util/transaction/WorkingCopy.java b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopy.java new file mode 100644 index 000000000..9dd3fc537 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopy.java @@ -0,0 +1,18 @@ +package org.argeo.util.transaction; + +import java.util.Map; + +public interface WorkingCopy { + void startEditing(DATA user); + + boolean noModifications(); + + void cleanUp(); + + Map getNewData(); + + Map getDeletedData(); + + Map getModifiedData(); + +} diff --git a/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyProcessor.java b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyProcessor.java new file mode 100644 index 000000000..cdd640488 --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyProcessor.java @@ -0,0 +1,11 @@ +package org.argeo.util.transaction; + +public interface WorkingCopyProcessor> { + void prepare(WC wc); + + void commit(WC wc); + + void rollback(WC wc); + + WC newWorkingCopy(); +} diff --git a/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyXaResource.java b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyXaResource.java new file mode 100644 index 000000000..ebafd267f --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/transaction/WorkingCopyXaResource.java @@ -0,0 +1,135 @@ +package org.argeo.util.transaction; + +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +/** {@link XAResource} for a user directory being edited. */ +public class WorkingCopyXaResource> implements XAResource { + private final WorkingCopyProcessor processor; + + private Map workingCopies = new HashMap(); + private Xid editingXid = null; + private int transactionTimeout = 0; + + public WorkingCopyXaResource(WorkingCopyProcessor processor) { + this.processor = processor; + } + + @Override + public synchronized void start(Xid xid, int flags) throws XAException { + if (editingXid != null) + throw new IllegalStateException("Already editing " + editingXid); + WC wc = workingCopies.put(xid, processor.newWorkingCopy()); + if (wc != null) + throw new IllegalStateException("There is already a working copy for " + xid); + this.editingXid = xid; + } + + @Override + public void end(Xid xid, int flags) throws XAException { + checkXid(xid); + } + + private WC wc(Xid xid) { + return workingCopies.get(xid); + } + + public synchronized WC wc() { + if (editingXid == null) + return null; + WC wc = workingCopies.get(editingXid); + if (wc == null) + throw new IllegalStateException("No working copy found for " + editingXid); + return wc; + } + + private synchronized void cleanUp(Xid xid) { + wc(xid).cleanUp(); + workingCopies.remove(xid); + editingXid = null; + } + + @Override + public int prepare(Xid xid) throws XAException { + checkXid(xid); + WC wc = wc(xid); + if (wc.noModifications()) + return XA_RDONLY; + try { + processor.prepare(wc); + } catch (Exception e) { + e.printStackTrace(); + throw new XAException(XAException.XAER_RMERR); + } + return XA_OK; + } + + @Override + public void commit(Xid xid, boolean onePhase) throws XAException { + try { + checkXid(xid); + WC wc = wc(xid); + if (wc.noModifications()) + return; + if (onePhase) + processor.prepare(wc); + processor.commit(wc); + } catch (Exception e) { + e.printStackTrace(); + throw new XAException(XAException.XAER_RMERR); + } finally { + cleanUp(xid); + } + } + + @Override + public void rollback(Xid xid) throws XAException { + try { + checkXid(xid); + processor.rollback(wc(xid)); + } catch (Exception e) { + e.printStackTrace(); + throw new XAException(XAException.XAER_RMERR); + } 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); + } + +} diff --git a/org.argeo.util/src/org/argeo/util/transaction/XAResourceProvider.java b/org.argeo.util/src/org/argeo/util/transaction/XAResourceProvider.java new file mode 100644 index 000000000..b0b211bad --- /dev/null +++ b/org.argeo.util/src/org/argeo/util/transaction/XAResourceProvider.java @@ -0,0 +1,7 @@ +package org.argeo.util.transaction; + +import javax.transaction.xa.XAResource; + +public interface XAResourceProvider { + XAResource getXaResource(); +} -- 2.30.2