import org.argeo.cms.security.CryptoKeyring;
import org.argeo.cms.swt.dialogs.CmsMessageDialog;
import org.argeo.eclipse.ui.dialogs.ErrorFeedback;
-import org.argeo.osgi.transaction.WorkTransaction;
+import org.argeo.util.transaction.WorkTransaction;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.jface.dialogs.Dialog;
import org.argeo.eclipse.ui.parts.LdifUsersTable;
import org.argeo.jcr.JcrException;
import org.argeo.jcr.JcrUtils;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.util.naming.LdapAttrs;
+import org.argeo.util.transaction.WorkTransaction;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ToolBarManager;
package org.argeo.cms.e4.users;
-import org.argeo.osgi.transaction.WorkTransaction;
+import org.argeo.util.transaction.WorkTransaction;
/** First effort to centralize back end methods used by the user admin UI */
public class UiAdminUtils {
import org.argeo.api.cms.CmsConstants;
import org.argeo.cms.CmsException;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.osgi.useradmin.UserAdminConf;
import org.argeo.osgi.useradmin.UserDirectory;
+import org.argeo.util.transaction.WorkTransaction;
import org.osgi.service.useradmin.UserAdmin;
import org.osgi.service.useradmin.UserAdminEvent;
import org.osgi.service.useradmin.UserAdminListener;
import org.argeo.eclipse.ui.ColumnDefinition;
import org.argeo.eclipse.ui.EclipseUiUtils;
import org.argeo.eclipse.ui.parts.LdifUsersTable;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.util.naming.LdapAttrs;
import org.argeo.util.naming.LdapObjs;
+import org.argeo.util.transaction.WorkTransaction;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.MessageDialog;
import org.argeo.cms.jcr.CmsJcrUtils;
import org.argeo.jcr.Jcr;
import org.argeo.jcr.JcrUtils;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.util.naming.Distinguished;
+import org.argeo.util.transaction.WorkTransaction;
import org.osgi.service.useradmin.Group;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.UserAdmin;
import javax.naming.ldap.LdapName;
import org.argeo.api.cms.CmsLog;
-import org.argeo.osgi.transaction.WorkTransaction;
+import org.argeo.util.transaction.WorkTransaction;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.UserAdmin;
import org.argeo.cms.CmsUserManager;
import org.argeo.cms.auth.CurrentUser;
import org.argeo.cms.auth.UserAdminUtils;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.osgi.useradmin.TokenUtils;
import org.argeo.osgi.useradmin.UserAdminConf;
import org.argeo.osgi.useradmin.UserDirectory;
import org.argeo.util.naming.LdapAttrs;
import org.argeo.util.naming.NamingUtils;
import org.argeo.util.naming.SharedSecret;
+import org.argeo.util.transaction.WorkTransaction;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Group;
import org.argeo.api.cms.CmsLog;
import org.argeo.cms.internal.http.client.HttpCredentialProvider;
import org.argeo.cms.internal.http.client.SpnegoAuthScheme;
-import org.argeo.osgi.transaction.WorkControl;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.osgi.useradmin.AggregatingUserAdmin;
import org.argeo.osgi.useradmin.LdapUserAdmin;
import org.argeo.osgi.useradmin.LdifUserAdmin;
import org.argeo.osgi.useradmin.UserAdminConf;
import org.argeo.osgi.useradmin.UserDirectory;
import org.argeo.util.naming.dns.DnsBrowser;
+import org.argeo.util.transaction.WorkControl;
+import org.argeo.util.transaction.WorkTransaction;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.argeo.cms.internal.runtime.CmsStateImpl;
import org.argeo.cms.internal.runtime.CmsUserAdmin;
import org.argeo.cms.internal.runtime.DeployedContentRepository;
-import org.argeo.osgi.transaction.SimpleTransactionManager;
-import org.argeo.osgi.transaction.WorkControl;
-import org.argeo.osgi.transaction.WorkTransaction;
import org.argeo.osgi.useradmin.UserDirectory;
import org.argeo.util.register.Component;
import org.argeo.util.register.SimpleRegister;
+import org.argeo.util.transaction.SimpleTransactionManager;
+import org.argeo.util.transaction.WorkControl;
+import org.argeo.util.transaction.WorkTransaction;
import org.osgi.service.useradmin.UserAdmin;
/**
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-/** JTA transaction status. */
-public class JtaStatusAdapter implements TransactionStatusAdapter<Integer> {
- private static final Integer STATUS_ACTIVE = 0;
- private static final Integer STATUS_COMMITTED = 3;
- private static final Integer STATUS_COMMITTING = 8;
- private static final Integer STATUS_MARKED_ROLLBACK = 1;
- private static final Integer STATUS_NO_TRANSACTION = 6;
- private static final Integer STATUS_PREPARED = 2;
- private static final Integer STATUS_PREPARING = 7;
- private static final Integer STATUS_ROLLEDBACK = 4;
- private static final Integer STATUS_ROLLING_BACK = 9;
-// private static final Integer STATUS_UNKNOWN = 5;
-
- @Override
- public Integer getActiveStatus() {
- return STATUS_ACTIVE;
- }
-
- @Override
- public Integer getPreparingStatus() {
- return STATUS_PREPARING;
- }
-
- @Override
- public Integer getMarkedRollbackStatus() {
- return STATUS_MARKED_ROLLBACK;
- }
-
- @Override
- public Integer getPreparedStatus() {
- return STATUS_PREPARED;
- }
-
- @Override
- public Integer getCommittingStatus() {
- return STATUS_COMMITTING;
- }
-
- @Override
- public Integer getCommittedStatus() {
- return STATUS_COMMITTED;
- }
-
- @Override
- public Integer getRollingBackStatus() {
- return STATUS_ROLLING_BACK;
- }
-
- @Override
- public Integer getRolledBackStatus() {
- return STATUS_ROLLEDBACK;
- }
-
- @Override
- public Integer getNoTransactionStatus() {
- return STATUS_NO_TRANSACTION;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-/** Internal unchecked rollback exception. */
-class SimpleRollbackException extends RuntimeException {
- private static final long serialVersionUID = 8055601819719780566L;
-
- public SimpleRollbackException() {
- super();
- }
-
- public SimpleRollbackException(Throwable cause) {
- super(cause);
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.transaction.xa.XAException;
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
-
-/** Simple implementation of an XA transaction. */
-class SimpleTransaction<T>
-//implements Transaction, Status
-{
- private final Xid xid;
- private T status;
- private final List<XAResource> xaResources = new ArrayList<XAResource>();
-
- private final SimpleTransactionManager transactionManager;
- private TransactionStatusAdapter<T> tsa;
-
- public SimpleTransaction(SimpleTransactionManager transactionManager, TransactionStatusAdapter<T> tsa) {
- this.tsa = tsa;
- this.status = tsa.getActiveStatus();
- this.xid = new UuidXid();
- this.transactionManager = transactionManager;
- }
-
- public synchronized void commit()
-// throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
-// SecurityException, IllegalStateException, SystemException
- {
- status = tsa.getPreparingStatus();
- for (XAResource xaRes : xaResources) {
- if (status.equals(tsa.getMarkedRollbackStatus()))
- break;
- try {
- xaRes.prepare(xid);
- } catch (XAException e) {
- status = tsa.getMarkedRollbackStatus();
- error("Cannot prepare " + xaRes + " for " + xid, e);
- }
- }
- if (status.equals(tsa.getMarkedRollbackStatus())) {
- rollback();
- throw new SimpleRollbackException();
- }
- status = tsa.getPreparedStatus();
-
- status = tsa.getCommittingStatus();
- for (XAResource xaRes : xaResources) {
- if (status.equals(tsa.getMarkedRollbackStatus()))
- break;
- try {
- xaRes.commit(xid, false);
- } catch (XAException e) {
- status = tsa.getMarkedRollbackStatus();
- error("Cannot prepare " + xaRes + " for " + xid, e);
- }
- }
- if (status.equals(tsa.getMarkedRollbackStatus())) {
- rollback();
- throw new SimpleRollbackException();
- }
-
- // complete
- status = tsa.getCommittedStatus();
- clearResources(XAResource.TMSUCCESS);
- transactionManager.unregister(xid);
- }
-
- public synchronized void rollback()
-// throws IllegalStateException, SystemException
- {
- status = tsa.getRollingBackStatus();
- for (XAResource xaRes : xaResources) {
- try {
- xaRes.rollback(xid);
- } catch (XAException e) {
- error("Cannot rollback " + xaRes + " for " + xid, e);
- }
- }
-
- // complete
- status = tsa.getRolledBackStatus();
- clearResources(XAResource.TMFAIL);
- transactionManager.unregister(xid);
- }
-
- public synchronized boolean enlistResource(XAResource xaRes)
-// throws RollbackException, IllegalStateException, SystemException
- {
- if (xaResources.add(xaRes)) {
- try {
- xaRes.start(getXid(), XAResource.TMNOFLAGS);
- return true;
- } catch (XAException e) {
- error("Cannot enlist " + xaRes, e);
- return false;
- }
- } else
- return false;
- }
-
- public synchronized boolean delistResource(XAResource xaRes, int flag)
-// throws IllegalStateException, SystemException
- {
- if (xaResources.remove(xaRes)) {
- try {
- xaRes.end(getXid(), flag);
- } catch (XAException e) {
- error("Cannot delist " + xaRes, e);
- return false;
- }
- return true;
- } else
- return false;
- }
-
- protected void clearResources(int flag) {
- for (XAResource xaRes : xaResources)
- try {
- xaRes.end(getXid(), flag);
- } catch (XAException e) {
- error("Cannot end " + xaRes, e);
- }
- xaResources.clear();
- }
-
- protected void error(Object obj, Exception e) {
- System.err.println(obj);
- e.printStackTrace();
- }
-
- public synchronized T getStatus()
-// throws SystemException
- {
- return status;
- }
-
-// public void registerSynchronization(Synchronization sync)
-// throws RollbackException, IllegalStateException, SystemException {
-// throw new UnsupportedOperationException();
-// }
-
- public void setRollbackOnly()
-// throws IllegalStateException, SystemException
- {
- status = tsa.getMarkedRollbackStatus();
- }
-
- @Override
- public int hashCode() {
- return xid.hashCode();
- }
-
- Xid getXid() {
- return xid;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
-
-import javax.transaction.xa.XAResource;
-import javax.transaction.xa.Xid;
-
-/**
- * Simple implementation of an XA transaction manager.
- */
-public class SimpleTransactionManager
-// implements TransactionManager, UserTransaction
- implements WorkControl, WorkTransaction {
- private ThreadLocal<SimpleTransaction<Integer>> current = new ThreadLocal<SimpleTransaction<Integer>>();
-
- private Map<Xid, SimpleTransaction<Integer>> knownTransactions = Collections
- .synchronizedMap(new HashMap<Xid, SimpleTransaction<Integer>>());
- private TransactionStatusAdapter<Integer> tsa = new JtaStatusAdapter();
-// private SyncRegistry syncRegistry = new SyncRegistry();
-
- /*
- * WORK IMPLEMENTATION
- */
- @Override
- public <T> T required(Callable<T> work) {
- T res;
- begin();
- try {
- res = work.call();
- commit();
- } catch (Exception e) {
- rollback();
- throw new SimpleRollbackException(e);
- }
- return res;
- }
-
- @Override
- public WorkContext getWorkContext() {
- return new WorkContext() {
-
- @Override
- public void registerXAResource(XAResource resource, String recoveryId) {
- getTransaction().enlistResource(resource);
- }
- };
- }
-
- /*
- * WORK TRANSACTION IMPLEMENTATION
- */
-
- @Override
- public boolean isNoTransactionStatus() {
- return tsa.getNoTransactionStatus().equals(getStatus());
- }
-
- /*
- * JTA IMPLEMENTATION
- */
-
- public void begin()
-// throws NotSupportedException, SystemException
- {
- if (getCurrent() != null)
- throw new UnsupportedOperationException("Nested transactions are not supported");
- SimpleTransaction<Integer> transaction = new SimpleTransaction<Integer>(this, tsa);
- knownTransactions.put(transaction.getXid(), transaction);
- current.set(transaction);
- }
-
- public void commit()
-// throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
-// SecurityException, IllegalStateException, SystemException
- {
- if (getCurrent() == null)
- throw new IllegalStateException("No transaction registered with the current thread.");
- getCurrent().commit();
- }
-
- public int getStatus()
-// throws SystemException
- {
- if (getCurrent() == null)
- return tsa.getNoTransactionStatus();
- return getTransaction().getStatus();
- }
-
- public SimpleTransaction<Integer> getTransaction()
-// throws SystemException
- {
- return getCurrent();
- }
-
- protected SimpleTransaction<Integer> getCurrent()
-// throws SystemException
- {
- SimpleTransaction<Integer> transaction = current.get();
- if (transaction == null)
- return null;
- Integer status = transaction.getStatus();
- if (status.equals(tsa.getCommittedStatus()) || status.equals(tsa.getRolledBackStatus())) {
- current.remove();
- return null;
- }
- return transaction;
- }
-
- void unregister(Xid xid) {
- knownTransactions.remove(xid);
- }
-
- public void resume(SimpleTransaction<Integer> tobj)
-// throws InvalidTransactionException, IllegalStateException, SystemException
- {
- if (getCurrent() != null)
- throw new IllegalStateException("Transaction " + current.get() + " already registered");
- current.set(tobj);
- }
-
- public void rollback()
-// throws IllegalStateException, SecurityException, SystemException
- {
- if (getCurrent() == null)
- throw new IllegalStateException("No transaction registered with the current thread.");
- getCurrent().rollback();
- }
-
- public void setRollbackOnly()
-// throws IllegalStateException, SystemException
- {
- if (getCurrent() == null)
- throw new IllegalStateException("No transaction registered with the current thread.");
- getCurrent().setRollbackOnly();
- }
-
- public void setTransactionTimeout(int seconds)
-// throws SystemException
- {
- throw new UnsupportedOperationException();
- }
-
- public SimpleTransaction<Integer> suspend()
-// throws SystemException
- {
- SimpleTransaction<Integer> transaction = getCurrent();
- current.remove();
- return transaction;
- }
-
-// public TransactionSynchronizationRegistry getTsr() {
-// return syncRegistry;
-// }
-//
-// private class SyncRegistry implements TransactionSynchronizationRegistry {
-// @Override
-// public Object getTransactionKey() {
-// try {
-// SimpleTransaction transaction = getCurrent();
-// if (transaction == null)
-// return null;
-// return getCurrent().getXid();
-// } catch (SystemException e) {
-// throw new IllegalStateException("Cannot get transaction key", e);
-// }
-// }
-//
-// @Override
-// public void putResource(Object key, Object value) {
-// throw new UnsupportedOperationException();
-// }
-//
-// @Override
-// public Object getResource(Object key) {
-// throw new UnsupportedOperationException();
-// }
-//
-// @Override
-// public void registerInterposedSynchronization(Synchronization sync) {
-// throw new UnsupportedOperationException();
-// }
-//
-// @Override
-// public int getTransactionStatus() {
-// try {
-// return getStatus();
-// } catch (SystemException e) {
-// throw new IllegalStateException("Cannot get status", e);
-// }
-// }
-//
-// @Override
-// public boolean getRollbackOnly() {
-// try {
-// return getStatus() == Status.STATUS_MARKED_ROLLBACK;
-// } catch (SystemException e) {
-// throw new IllegalStateException("Cannot get status", e);
-// }
-// }
-//
-// @Override
-// public void setRollbackOnly() {
-// try {
-// getCurrent().setRollbackOnly();
-// } catch (Exception e) {
-// throw new IllegalStateException("Cannot set rollback only", e);
-// }
-// }
-//
-// }
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-/** Abstract the various approaches to represent transaction status. */
-public interface TransactionStatusAdapter<T> {
- T getActiveStatus();
-
- T getPreparingStatus();
-
- T getMarkedRollbackStatus();
-
- T getPreparedStatus();
-
- T getCommittingStatus();
-
- T getCommittedStatus();
-
- T getRollingBackStatus();
-
- T getRolledBackStatus();
-
- T getNoTransactionStatus();
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.UUID;
-
-import javax.transaction.xa.Xid;
-
-/**
- * Implementation of {@link Xid} based on {@link UUID}, using max significant
- * bits as global transaction id, and least significant bits as branch
- * qualifier.
- */
-public class UuidXid implements Xid, Serializable {
- private static final long serialVersionUID = -5380531989917886819L;
- public final static int FORMAT = (int) serialVersionUID;
-
- private final static int BYTES_PER_LONG = Long.SIZE / Byte.SIZE;
-
- private final int format;
- private final byte[] globalTransactionId;
- private final byte[] branchQualifier;
- private final String uuid;
- private final int hashCode;
-
- public UuidXid() {
- this(UUID.randomUUID());
- }
-
- public UuidXid(UUID uuid) {
- this.format = FORMAT;
- this.globalTransactionId = uuidToBytes(uuid.getMostSignificantBits());
- this.branchQualifier = uuidToBytes(uuid.getLeastSignificantBits());
- this.uuid = uuid.toString();
- this.hashCode = uuid.hashCode();
- }
-
- public UuidXid(Xid xid) {
- this(xid.getFormatId(), xid.getGlobalTransactionId(), xid
- .getBranchQualifier());
- }
-
- private UuidXid(int format, byte[] globalTransactionId,
- byte[] branchQualifier) {
- this.format = format;
- this.globalTransactionId = globalTransactionId;
- this.branchQualifier = branchQualifier;
- this.uuid = bytesToUUID(globalTransactionId, branchQualifier)
- .toString();
- this.hashCode = uuid.hashCode();
- }
-
- @Override
- public int getFormatId() {
- return format;
- }
-
- @Override
- public byte[] getGlobalTransactionId() {
- return Arrays.copyOf(globalTransactionId, globalTransactionId.length);
- }
-
- @Override
- public byte[] getBranchQualifier() {
- return Arrays.copyOf(branchQualifier, branchQualifier.length);
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj instanceof UuidXid) {
- UuidXid that = (UuidXid) obj;
- return Arrays.equals(globalTransactionId, that.globalTransactionId)
- && Arrays.equals(branchQualifier, that.branchQualifier);
- }
- if (obj instanceof Xid) {
- Xid that = (Xid) obj;
- return Arrays.equals(globalTransactionId,
- that.getGlobalTransactionId())
- && Arrays
- .equals(branchQualifier, that.getBranchQualifier());
- }
- return uuid.equals(obj.toString());
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return new UuidXid(format, globalTransactionId, branchQualifier);
- }
-
- @Override
- public String toString() {
- return uuid;
- }
-
- public UUID asUuid() {
- return bytesToUUID(globalTransactionId, branchQualifier);
- }
-
- public static byte[] uuidToBytes(long bits) {
- ByteBuffer buffer = ByteBuffer.allocate(BYTES_PER_LONG);
- buffer.putLong(0, bits);
- return buffer.array();
- }
-
- public static UUID bytesToUUID(byte[] most, byte[] least) {
- if (most.length < BYTES_PER_LONG)
- most = Arrays.copyOf(most, BYTES_PER_LONG);
- if (least.length < BYTES_PER_LONG)
- least = Arrays.copyOf(least, BYTES_PER_LONG);
- ByteBuffer buffer = ByteBuffer.allocate(2 * BYTES_PER_LONG);
- buffer.put(most, 0, BYTES_PER_LONG);
- buffer.put(least, 0, BYTES_PER_LONG);
- buffer.flip();
- return new UUID(buffer.getLong(), buffer.getLong());
- }
-
- // public static void main(String[] args) {
- // UUID uuid = UUID.randomUUID();
- // System.out.println(uuid);
- // uuid = bytesToUUID(uuidToBytes(uuid.getMostSignificantBits()),
- // uuidToBytes(uuid.getLeastSignificantBits()));
- // System.out.println(uuid);
- // }
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-import javax.transaction.xa.XAResource;
-
-/**
- * A minimalistic interface similar to OSGi transaction context in order to
- * register XA resources.
- */
-public interface WorkContext {
- void registerXAResource(XAResource resource, String recoveryId);
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-import java.util.concurrent.Callable;
-
-/**
- * A minimalistic interface inspired by OSGi transaction control in order to
- * commit units of work externally.
- */
-public interface WorkControl {
- <T> T required(Callable<T> work);
-
- void setRollbackOnly();
-
- WorkContext getWorkContext();
-}
+++ /dev/null
-package org.argeo.osgi.transaction;
-
-/**
- * A minimalistic interface inspired by JTA user transaction in order to commit
- * units of work externally.
- */
-public interface WorkTransaction {
- void begin();
-
- void commit();
-
- void rollback();
-
- boolean isNoTransactionStatus();
-}
+++ /dev/null
-/** Minimalistic and partial XA transaction manager implementation. */
-package org.argeo.osgi.transaction;
\ No newline at end of file
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
-import org.argeo.osgi.transaction.WorkControl;
import org.argeo.util.naming.LdapAttrs;
import org.argeo.util.naming.LdapObjs;
+import org.argeo.util.transaction.WorkControl;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import java.util.Optional;
-import org.argeo.osgi.transaction.WorkControl;
+import org.argeo.util.transaction.WorkControl;
import org.osgi.service.useradmin.Role;
/** Information about a user directory. */
--- /dev/null
+package org.argeo.util.transaction;
+
+/** JTA transaction status. */
+public class JtaStatusAdapter implements TransactionStatusAdapter<Integer> {
+ private static final Integer STATUS_ACTIVE = 0;
+ private static final Integer STATUS_COMMITTED = 3;
+ private static final Integer STATUS_COMMITTING = 8;
+ private static final Integer STATUS_MARKED_ROLLBACK = 1;
+ private static final Integer STATUS_NO_TRANSACTION = 6;
+ private static final Integer STATUS_PREPARED = 2;
+ private static final Integer STATUS_PREPARING = 7;
+ private static final Integer STATUS_ROLLEDBACK = 4;
+ private static final Integer STATUS_ROLLING_BACK = 9;
+// private static final Integer STATUS_UNKNOWN = 5;
+
+ @Override
+ public Integer getActiveStatus() {
+ return STATUS_ACTIVE;
+ }
+
+ @Override
+ public Integer getPreparingStatus() {
+ return STATUS_PREPARING;
+ }
+
+ @Override
+ public Integer getMarkedRollbackStatus() {
+ return STATUS_MARKED_ROLLBACK;
+ }
+
+ @Override
+ public Integer getPreparedStatus() {
+ return STATUS_PREPARED;
+ }
+
+ @Override
+ public Integer getCommittingStatus() {
+ return STATUS_COMMITTING;
+ }
+
+ @Override
+ public Integer getCommittedStatus() {
+ return STATUS_COMMITTED;
+ }
+
+ @Override
+ public Integer getRollingBackStatus() {
+ return STATUS_ROLLING_BACK;
+ }
+
+ @Override
+ public Integer getRolledBackStatus() {
+ return STATUS_ROLLEDBACK;
+ }
+
+ @Override
+ public Integer getNoTransactionStatus() {
+ return STATUS_NO_TRANSACTION;
+ }
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+/** Internal unchecked rollback exception. */
+class SimpleRollbackException extends RuntimeException {
+ private static final long serialVersionUID = 8055601819719780566L;
+
+ public SimpleRollbackException() {
+ super();
+ }
+
+ public SimpleRollbackException(Throwable cause) {
+ super(cause);
+ }
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+/** Simple implementation of an XA transaction. */
+class SimpleTransaction<T>
+//implements Transaction, Status
+{
+ private final Xid xid;
+ private T status;
+ private final List<XAResource> xaResources = new ArrayList<XAResource>();
+
+ private final SimpleTransactionManager transactionManager;
+ private TransactionStatusAdapter<T> tsa;
+
+ public SimpleTransaction(SimpleTransactionManager transactionManager, TransactionStatusAdapter<T> tsa) {
+ this.tsa = tsa;
+ this.status = tsa.getActiveStatus();
+ this.xid = new UuidXid();
+ this.transactionManager = transactionManager;
+ }
+
+ public synchronized void commit()
+// throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
+// SecurityException, IllegalStateException, SystemException
+ {
+ status = tsa.getPreparingStatus();
+ for (XAResource xaRes : xaResources) {
+ if (status.equals(tsa.getMarkedRollbackStatus()))
+ break;
+ try {
+ xaRes.prepare(xid);
+ } catch (XAException e) {
+ status = tsa.getMarkedRollbackStatus();
+ error("Cannot prepare " + xaRes + " for " + xid, e);
+ }
+ }
+ if (status.equals(tsa.getMarkedRollbackStatus())) {
+ rollback();
+ throw new SimpleRollbackException();
+ }
+ status = tsa.getPreparedStatus();
+
+ status = tsa.getCommittingStatus();
+ for (XAResource xaRes : xaResources) {
+ if (status.equals(tsa.getMarkedRollbackStatus()))
+ break;
+ try {
+ xaRes.commit(xid, false);
+ } catch (XAException e) {
+ status = tsa.getMarkedRollbackStatus();
+ error("Cannot prepare " + xaRes + " for " + xid, e);
+ }
+ }
+ if (status.equals(tsa.getMarkedRollbackStatus())) {
+ rollback();
+ throw new SimpleRollbackException();
+ }
+
+ // complete
+ status = tsa.getCommittedStatus();
+ clearResources(XAResource.TMSUCCESS);
+ transactionManager.unregister(xid);
+ }
+
+ public synchronized void rollback()
+// throws IllegalStateException, SystemException
+ {
+ status = tsa.getRollingBackStatus();
+ for (XAResource xaRes : xaResources) {
+ try {
+ xaRes.rollback(xid);
+ } catch (XAException e) {
+ error("Cannot rollback " + xaRes + " for " + xid, e);
+ }
+ }
+
+ // complete
+ status = tsa.getRolledBackStatus();
+ clearResources(XAResource.TMFAIL);
+ transactionManager.unregister(xid);
+ }
+
+ public synchronized boolean enlistResource(XAResource xaRes)
+// throws RollbackException, IllegalStateException, SystemException
+ {
+ if (xaResources.add(xaRes)) {
+ try {
+ xaRes.start(getXid(), XAResource.TMNOFLAGS);
+ return true;
+ } catch (XAException e) {
+ error("Cannot enlist " + xaRes, e);
+ return false;
+ }
+ } else
+ return false;
+ }
+
+ public synchronized boolean delistResource(XAResource xaRes, int flag)
+// throws IllegalStateException, SystemException
+ {
+ if (xaResources.remove(xaRes)) {
+ try {
+ xaRes.end(getXid(), flag);
+ } catch (XAException e) {
+ error("Cannot delist " + xaRes, e);
+ return false;
+ }
+ return true;
+ } else
+ return false;
+ }
+
+ protected void clearResources(int flag) {
+ for (XAResource xaRes : xaResources)
+ try {
+ xaRes.end(getXid(), flag);
+ } catch (XAException e) {
+ error("Cannot end " + xaRes, e);
+ }
+ xaResources.clear();
+ }
+
+ protected void error(Object obj, Exception e) {
+ System.err.println(obj);
+ e.printStackTrace();
+ }
+
+ public synchronized T getStatus()
+// throws SystemException
+ {
+ return status;
+ }
+
+// public void registerSynchronization(Synchronization sync)
+// throws RollbackException, IllegalStateException, SystemException {
+// throw new UnsupportedOperationException();
+// }
+
+ public void setRollbackOnly()
+// throws IllegalStateException, SystemException
+ {
+ status = tsa.getMarkedRollbackStatus();
+ }
+
+ @Override
+ public int hashCode() {
+ return xid.hashCode();
+ }
+
+ Xid getXid() {
+ return xid;
+ }
+
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+/**
+ * Simple implementation of an XA transaction manager.
+ */
+public class SimpleTransactionManager
+// implements TransactionManager, UserTransaction
+ implements WorkControl, WorkTransaction {
+ private ThreadLocal<SimpleTransaction<Integer>> current = new ThreadLocal<SimpleTransaction<Integer>>();
+
+ private Map<Xid, SimpleTransaction<Integer>> knownTransactions = Collections
+ .synchronizedMap(new HashMap<Xid, SimpleTransaction<Integer>>());
+ private TransactionStatusAdapter<Integer> tsa = new JtaStatusAdapter();
+// private SyncRegistry syncRegistry = new SyncRegistry();
+
+ /*
+ * WORK IMPLEMENTATION
+ */
+ @Override
+ public <T> T required(Callable<T> work) {
+ T res;
+ begin();
+ try {
+ res = work.call();
+ commit();
+ } catch (Exception e) {
+ rollback();
+ throw new SimpleRollbackException(e);
+ }
+ return res;
+ }
+
+ @Override
+ public WorkContext getWorkContext() {
+ return new WorkContext() {
+
+ @Override
+ public void registerXAResource(XAResource resource, String recoveryId) {
+ getTransaction().enlistResource(resource);
+ }
+ };
+ }
+
+ /*
+ * WORK TRANSACTION IMPLEMENTATION
+ */
+
+ @Override
+ public boolean isNoTransactionStatus() {
+ return tsa.getNoTransactionStatus().equals(getStatus());
+ }
+
+ /*
+ * JTA IMPLEMENTATION
+ */
+
+ public void begin()
+// throws NotSupportedException, SystemException
+ {
+ if (getCurrent() != null)
+ throw new UnsupportedOperationException("Nested transactions are not supported");
+ SimpleTransaction<Integer> transaction = new SimpleTransaction<Integer>(this, tsa);
+ knownTransactions.put(transaction.getXid(), transaction);
+ current.set(transaction);
+ }
+
+ public void commit()
+// throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
+// SecurityException, IllegalStateException, SystemException
+ {
+ if (getCurrent() == null)
+ throw new IllegalStateException("No transaction registered with the current thread.");
+ getCurrent().commit();
+ }
+
+ public int getStatus()
+// throws SystemException
+ {
+ if (getCurrent() == null)
+ return tsa.getNoTransactionStatus();
+ return getTransaction().getStatus();
+ }
+
+ public SimpleTransaction<Integer> getTransaction()
+// throws SystemException
+ {
+ return getCurrent();
+ }
+
+ protected SimpleTransaction<Integer> getCurrent()
+// throws SystemException
+ {
+ SimpleTransaction<Integer> transaction = current.get();
+ if (transaction == null)
+ return null;
+ Integer status = transaction.getStatus();
+ if (status.equals(tsa.getCommittedStatus()) || status.equals(tsa.getRolledBackStatus())) {
+ current.remove();
+ return null;
+ }
+ return transaction;
+ }
+
+ void unregister(Xid xid) {
+ knownTransactions.remove(xid);
+ }
+
+ public void resume(SimpleTransaction<Integer> tobj)
+// throws InvalidTransactionException, IllegalStateException, SystemException
+ {
+ if (getCurrent() != null)
+ throw new IllegalStateException("Transaction " + current.get() + " already registered");
+ current.set(tobj);
+ }
+
+ public void rollback()
+// throws IllegalStateException, SecurityException, SystemException
+ {
+ if (getCurrent() == null)
+ throw new IllegalStateException("No transaction registered with the current thread.");
+ getCurrent().rollback();
+ }
+
+ public void setRollbackOnly()
+// throws IllegalStateException, SystemException
+ {
+ if (getCurrent() == null)
+ throw new IllegalStateException("No transaction registered with the current thread.");
+ getCurrent().setRollbackOnly();
+ }
+
+ public void setTransactionTimeout(int seconds)
+// throws SystemException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public SimpleTransaction<Integer> suspend()
+// throws SystemException
+ {
+ SimpleTransaction<Integer> transaction = getCurrent();
+ current.remove();
+ return transaction;
+ }
+
+// public TransactionSynchronizationRegistry getTsr() {
+// return syncRegistry;
+// }
+//
+// private class SyncRegistry implements TransactionSynchronizationRegistry {
+// @Override
+// public Object getTransactionKey() {
+// try {
+// SimpleTransaction transaction = getCurrent();
+// if (transaction == null)
+// return null;
+// return getCurrent().getXid();
+// } catch (SystemException e) {
+// throw new IllegalStateException("Cannot get transaction key", e);
+// }
+// }
+//
+// @Override
+// public void putResource(Object key, Object value) {
+// throw new UnsupportedOperationException();
+// }
+//
+// @Override
+// public Object getResource(Object key) {
+// throw new UnsupportedOperationException();
+// }
+//
+// @Override
+// public void registerInterposedSynchronization(Synchronization sync) {
+// throw new UnsupportedOperationException();
+// }
+//
+// @Override
+// public int getTransactionStatus() {
+// try {
+// return getStatus();
+// } catch (SystemException e) {
+// throw new IllegalStateException("Cannot get status", e);
+// }
+// }
+//
+// @Override
+// public boolean getRollbackOnly() {
+// try {
+// return getStatus() == Status.STATUS_MARKED_ROLLBACK;
+// } catch (SystemException e) {
+// throw new IllegalStateException("Cannot get status", e);
+// }
+// }
+//
+// @Override
+// public void setRollbackOnly() {
+// try {
+// getCurrent().setRollbackOnly();
+// } catch (Exception e) {
+// throw new IllegalStateException("Cannot set rollback only", e);
+// }
+// }
+//
+// }
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+/** Abstract the various approaches to represent transaction status. */
+public interface TransactionStatusAdapter<T> {
+ T getActiveStatus();
+
+ T getPreparingStatus();
+
+ T getMarkedRollbackStatus();
+
+ T getPreparedStatus();
+
+ T getCommittingStatus();
+
+ T getCommittedStatus();
+
+ T getRollingBackStatus();
+
+ T getRolledBackStatus();
+
+ T getNoTransactionStatus();
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.UUID;
+
+import javax.transaction.xa.Xid;
+
+/**
+ * Implementation of {@link Xid} based on {@link UUID}, using max significant
+ * bits as global transaction id, and least significant bits as branch
+ * qualifier.
+ */
+public class UuidXid implements Xid, Serializable {
+ private static final long serialVersionUID = -5380531989917886819L;
+ public final static int FORMAT = (int) serialVersionUID;
+
+ private final static int BYTES_PER_LONG = Long.SIZE / Byte.SIZE;
+
+ private final int format;
+ private final byte[] globalTransactionId;
+ private final byte[] branchQualifier;
+ private final String uuid;
+ private final int hashCode;
+
+ public UuidXid() {
+ this(UUID.randomUUID());
+ }
+
+ public UuidXid(UUID uuid) {
+ this.format = FORMAT;
+ this.globalTransactionId = uuidToBytes(uuid.getMostSignificantBits());
+ this.branchQualifier = uuidToBytes(uuid.getLeastSignificantBits());
+ this.uuid = uuid.toString();
+ this.hashCode = uuid.hashCode();
+ }
+
+ public UuidXid(Xid xid) {
+ this(xid.getFormatId(), xid.getGlobalTransactionId(), xid
+ .getBranchQualifier());
+ }
+
+ private UuidXid(int format, byte[] globalTransactionId,
+ byte[] branchQualifier) {
+ this.format = format;
+ this.globalTransactionId = globalTransactionId;
+ this.branchQualifier = branchQualifier;
+ this.uuid = bytesToUUID(globalTransactionId, branchQualifier)
+ .toString();
+ this.hashCode = uuid.hashCode();
+ }
+
+ @Override
+ public int getFormatId() {
+ return format;
+ }
+
+ @Override
+ public byte[] getGlobalTransactionId() {
+ return Arrays.copyOf(globalTransactionId, globalTransactionId.length);
+ }
+
+ @Override
+ public byte[] getBranchQualifier() {
+ return Arrays.copyOf(branchQualifier, branchQualifier.length);
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj instanceof UuidXid) {
+ UuidXid that = (UuidXid) obj;
+ return Arrays.equals(globalTransactionId, that.globalTransactionId)
+ && Arrays.equals(branchQualifier, that.branchQualifier);
+ }
+ if (obj instanceof Xid) {
+ Xid that = (Xid) obj;
+ return Arrays.equals(globalTransactionId,
+ that.getGlobalTransactionId())
+ && Arrays
+ .equals(branchQualifier, that.getBranchQualifier());
+ }
+ return uuid.equals(obj.toString());
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return new UuidXid(format, globalTransactionId, branchQualifier);
+ }
+
+ @Override
+ public String toString() {
+ return uuid;
+ }
+
+ public UUID asUuid() {
+ return bytesToUUID(globalTransactionId, branchQualifier);
+ }
+
+ public static byte[] uuidToBytes(long bits) {
+ ByteBuffer buffer = ByteBuffer.allocate(BYTES_PER_LONG);
+ buffer.putLong(0, bits);
+ return buffer.array();
+ }
+
+ public static UUID bytesToUUID(byte[] most, byte[] least) {
+ if (most.length < BYTES_PER_LONG)
+ most = Arrays.copyOf(most, BYTES_PER_LONG);
+ if (least.length < BYTES_PER_LONG)
+ least = Arrays.copyOf(least, BYTES_PER_LONG);
+ ByteBuffer buffer = ByteBuffer.allocate(2 * BYTES_PER_LONG);
+ buffer.put(most, 0, BYTES_PER_LONG);
+ buffer.put(least, 0, BYTES_PER_LONG);
+ buffer.flip();
+ return new UUID(buffer.getLong(), buffer.getLong());
+ }
+
+ // public static void main(String[] args) {
+ // UUID uuid = UUID.randomUUID();
+ // System.out.println(uuid);
+ // uuid = bytesToUUID(uuidToBytes(uuid.getMostSignificantBits()),
+ // uuidToBytes(uuid.getLeastSignificantBits()));
+ // System.out.println(uuid);
+ // }
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import javax.transaction.xa.XAResource;
+
+/**
+ * A minimalistic interface similar to OSGi transaction context in order to
+ * register XA resources.
+ */
+public interface WorkContext {
+ void registerXAResource(XAResource resource, String recoveryId);
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+import java.util.concurrent.Callable;
+
+/**
+ * A minimalistic interface inspired by OSGi transaction control in order to
+ * commit units of work externally.
+ */
+public interface WorkControl {
+ <T> T required(Callable<T> work);
+
+ void setRollbackOnly();
+
+ WorkContext getWorkContext();
+}
--- /dev/null
+package org.argeo.util.transaction;
+
+/**
+ * A minimalistic interface inspired by JTA user transaction in order to commit
+ * units of work externally.
+ */
+public interface WorkTransaction {
+ void begin();
+
+ void commit();
+
+ void rollback();
+
+ boolean isNoTransactionStatus();
+}
--- /dev/null
+/** Minimalistic and partial XA transaction manager implementation. */
+package org.argeo.util.transaction;
\ No newline at end of file