+ //
+ // XA RESOURCE
+ //
+ protected class WorkingCopy implements XAResource {
+ private Xid xid;
+ private int transactionTimeout = 0;
+
+ 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>();
+
+ @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<LdapName, DirectoryUser> getNewUsers() {
+ return newUsers;
+ }
+
+ public Map<LdapName, DirectoryUser> getDeletedUsers() {
+ return deletedUsers;
+ }
+
+ public Map<LdapName, Attributes> getModifiedUsers() {
+ return modifiedUsers;
+ }
+
+ }