From: Mathieu Baudier Date: Tue, 7 Dec 2021 08:47:02 +0000 (+0100) Subject: Remove dependency to JTA X-Git-Tag: argeo-commons-2.3.5~131 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=5986e55820cba0821f0c16627c4ab144863c82ab;p=lgpl%2Fargeo-commons.git Remove dependency to JTA --- diff --git a/dep/org.argeo.dep.cms.client/pom.xml b/dep/org.argeo.dep.cms.client/pom.xml index 2381d27b3..3c99ba16c 100644 --- a/dep/org.argeo.dep.cms.client/pom.xml +++ b/dep/org.argeo.dep.cms.client/pom.xml @@ -45,6 +45,8 @@ org.argeo.tp.javax javax.enterprise.cdi-api + + org.argeo.tp.javax javax.transaction-api diff --git a/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml b/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml index 6fa13d402..a267aa519 100644 --- a/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml +++ b/org.argeo.cms.e4/OSGI-INF/userAdminWrapper.xml @@ -1,7 +1,7 @@ - + diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java index 52756376b..fe05253c1 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/handlers/ChangePassword.java @@ -11,13 +11,13 @@ import java.util.Arrays; import javax.inject.Inject; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; -import javax.transaction.UserTransaction; import org.argeo.api.security.CryptoKeyring; import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; import org.argeo.cms.ui.dialogs.CmsMessageDialog; import org.argeo.eclipse.ui.dialogs.ErrorFeedback; +import org.argeo.osgi.transaction.WorkTransaction; import org.eclipse.e4.core.di.annotations.Execute; import org.eclipse.e4.core.di.annotations.Optional; import org.eclipse.jface.dialogs.Dialog; @@ -38,7 +38,7 @@ public class ChangePassword { @Inject private UserAdmin userAdmin; @Inject - private UserTransaction userTransaction; + private WorkTransaction userTransaction; @Inject @Optional private CryptoKeyring keyring = null; diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java index 2c7504971..9ba7af72f 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/GroupEditor.java @@ -17,7 +17,6 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; -import javax.transaction.UserTransaction; import org.argeo.api.NodeConstants; import org.argeo.api.NodeInstance; @@ -36,6 +35,7 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.jcr.JcrUtils; import org.argeo.naming.LdapAttrs; +import org.argeo.osgi.transaction.WorkTransaction; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ToolBarManager; @@ -514,7 +514,7 @@ public class GroupEditor extends AbstractRoleEditor { userAdminWrapper.notifyListeners(new UserAdminEvent(null, UserAdminEvent.ROLE_CHANGED, myGroup)); } else if (role.getType() == Role.USER) { // TODO check if the group is already member of this group - UserTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); + WorkTransaction transaction = userAdminWrapper.beginTransactionIfNeeded(); User user = (User) role; myGroup.addMember(user); if (UserAdminWrapper.COMMIT_ON_SAVE) diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java index a5fb6100e..f85649260 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UiAdminUtils.java @@ -1,6 +1,6 @@ package org.argeo.cms.e4.users; -import javax.transaction.UserTransaction; +import org.argeo.osgi.transaction.WorkTransaction; /** First effort to centralize back end methods used by the user admin UI */ public class UiAdminUtils { @@ -10,7 +10,7 @@ public class UiAdminUtils { */ /** Easily notify the ActiveWindow that the transaction had a state change */ public final static void notifyTransactionStateChange( - UserTransaction userTransaction) { + WorkTransaction userTransaction) { // try { // IWorkbenchWindow aww = PlatformUI.getWorkbench() // .getActiveWorkbenchWindow(); diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java index 60d232aba..d1bdd775a 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserAdminWrapper.java @@ -8,11 +8,9 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.transaction.Status; -import javax.transaction.UserTransaction; - import org.argeo.api.NodeConstants; 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.osgi.service.useradmin.UserAdmin; @@ -27,7 +25,7 @@ public class UserAdminWrapper { // private Set uris; private Map> userDirectories = Collections .synchronizedMap(new LinkedHashMap<>()); - private UserTransaction userTransaction; + private WorkTransaction userTransaction; // First effort to simplify UX while managing users and groups public final static boolean COMMIT_ON_SAVE = true; @@ -40,10 +38,10 @@ public class UserAdminWrapper { * {@link UserAdminWrapper#commitOrNotifyTransactionStateChange()} once the * security model changes have been performed. */ - public UserTransaction beginTransactionIfNeeded() { + public WorkTransaction beginTransactionIfNeeded() { try { // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) { + if (userTransaction.isNoTransactionStatus()) { userTransaction.begin(); // UiAdminUtils.notifyTransactionStateChange(userTransaction); } @@ -61,7 +59,7 @@ public class UserAdminWrapper { public void commitOrNotifyTransactionStateChange() { try { // UserTransaction userTransaction = getUserTransaction(); - if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) + if (userTransaction.isNoTransactionStatus()) return; if (UserAdminWrapper.COMMIT_ON_SAVE) @@ -126,7 +124,7 @@ public class UserAdminWrapper { return userAdmin; } - public UserTransaction getUserTransaction() { + public WorkTransaction getUserTransaction() { return userTransaction; } @@ -136,7 +134,7 @@ public class UserAdminWrapper { // this.uris = Collections.unmodifiableSortedSet(new TreeSet<>(properties.keySet())); } - public void setUserTransaction(UserTransaction userTransaction) { + public void setUserTransaction(WorkTransaction userTransaction) { this.userTransaction = userTransaction; } diff --git a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java index 7513102fe..4073a209b 100644 --- a/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java +++ b/org.argeo.cms.e4/src/org/argeo/cms/e4/users/UserBatchUpdateWizard.java @@ -5,9 +5,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.transaction.SystemException; -import javax.transaction.UserTransaction; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.api.NodeConstants; @@ -23,6 +20,7 @@ import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.parts.LdifUsersTable; import org.argeo.naming.LdapAttrs; import org.argeo.naming.LdapObjs; +import org.argeo.osgi.transaction.WorkTransaction; import org.eclipse.jface.dialogs.IPageChangeProvider; import org.eclipse.jface.dialogs.IPageChangedListener; import org.eclipse.jface.dialogs.MessageDialog; @@ -90,15 +88,10 @@ public class UserBatchUpdateWizard extends Wizard { public boolean performFinish() { if (!canFinish()) return false; - UserTransaction ut = userAdminWrapper.getUserTransaction(); - try { - if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION - && !MessageDialog.openConfirm(getShell(), "Existing Transaction", - "A user transaction is already existing, " + "are you sure you want to proceed ?")) - return false; - } catch (SystemException e) { - throw new CmsException("Cannot get user transaction state " + "before user batch update", e); - } + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus() && !MessageDialog.openConfirm(getShell(), "Existing Transaction", + "A user transaction is already existing, " + "are you sure you want to proceed ?")) + return false; // We cannot use jobs, user modifications are still meant to be done in // the UIThread @@ -151,14 +144,9 @@ public class UserBatchUpdateWizard extends Wizard { } catch (Exception e) { throw new CmsException("Cannot perform batch update on users", e); } finally { - UserTransaction ut = userAdminWrapper.getUserTransaction(); - try { - if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - ut.rollback(); - } catch (IllegalStateException | SecurityException | SystemException e) { - log.error("Unable to rollback session in 'finally', " + "the system might be in a dirty state"); - e.printStackTrace(); - } + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); } } } @@ -190,14 +178,9 @@ public class UserBatchUpdateWizard extends Wizard { } catch (Exception e) { throw new CmsException("Cannot perform batch update on users", e); } finally { - UserTransaction ut = userAdminWrapper.getUserTransaction(); - try { - if (ut.getStatus() != javax.transaction.Status.STATUS_NO_TRANSACTION) - ut.rollback(); - } catch (IllegalStateException | SecurityException | SystemException e) { - log.error("Unable to rollback session in finally block, the system might be in a dirty state"); - e.printStackTrace(); - } + WorkTransaction ut = userAdminWrapper.getUserTransaction(); + if (!ut.isNoTransactionStatus()) + ut.rollback(); } } } @@ -457,8 +440,7 @@ public class UserBatchUpdateWizard extends Wizard { } /** - * Displays a list of users with a check box to be able to choose some of - * them + * Displays a list of users with a check box to be able to choose some of them */ private class ChooseUsersWizardPage extends WizardPage implements IPageChangedListener { private static final long serialVersionUID = 7651807402211214274L; diff --git a/org.argeo.cms.jcr/ext/test/org/argeo/cms/tabular/JcrTabularTest.java b/org.argeo.cms.jcr/ext/test/org/argeo/cms/tabular/JcrTabularTest.java index 8da614800..18150c791 100644 --- a/org.argeo.cms.jcr/ext/test/org/argeo/cms/tabular/JcrTabularTest.java +++ b/org.argeo.cms.jcr/ext/test/org/argeo/cms/tabular/JcrTabularTest.java @@ -22,10 +22,10 @@ public class JcrTabularTest extends AbstractJackrabbitTestCase { public void testWriteReadCsv() throws Exception { // session().setNamespacePrefix("argeo", ArgeoNames.ARGEO_NAMESPACE); - InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/api/ldap.cnd")); + InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/cms/jcr/ldap.cnd")); CndImporter.registerNodeTypes(reader, session()); reader.close(); - reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/cms/argeo.cnd")); + reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/cms/jcr/argeo.cnd")); CndImporter.registerNodeTypes(reader, session()); reader.close(); // reader = new InputStreamReader(getClass().getResourceAsStream("/org/argeo/cms/cms.cnd")); diff --git a/org.argeo.cms.jcr/src/org/argeo/maintenance/AbstractMaintenanceService.java b/org.argeo.cms.jcr/src/org/argeo/maintenance/AbstractMaintenanceService.java index 6003d638d..ae09cd4bf 100644 --- a/org.argeo.cms.jcr/src/org/argeo/maintenance/AbstractMaintenanceService.java +++ b/org.argeo.cms.jcr/src/org/argeo/maintenance/AbstractMaintenanceService.java @@ -9,7 +9,6 @@ import javax.jcr.NoSuchWorkspaceException; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; -import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -17,6 +16,7 @@ import org.argeo.api.NodeUtils; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrUtils; import org.argeo.naming.Distinguished; +import org.argeo.osgi.transaction.WorkTransaction; import org.osgi.service.useradmin.Group; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.UserAdmin; @@ -28,7 +28,7 @@ public abstract class AbstractMaintenanceService { private Repository repository; // private UserAdminService userAdminService; private UserAdmin userAdmin; - private UserTransaction userTransaction; + private WorkTransaction userTransaction; public void init() { makeSureRolesExists(getRequiredRoles()); @@ -202,7 +202,7 @@ public abstract class AbstractMaintenanceService { // this.userAdminService = userAdminService; // } - protected UserTransaction getUserTransaction() { + protected WorkTransaction getUserTransaction() { return userTransaction; } @@ -214,7 +214,7 @@ public abstract class AbstractMaintenanceService { this.userAdmin = userAdmin; } - public void setUserTransaction(UserTransaction userTransaction) { + public void setUserTransaction(WorkTransaction userTransaction) { this.userTransaction = userTransaction; } diff --git a/org.argeo.cms.jcr/src/org/argeo/maintenance/SimpleRoleRegistration.java b/org.argeo.cms.jcr/src/org/argeo/maintenance/SimpleRoleRegistration.java index a30fe9796..07dc39985 100644 --- a/org.argeo.cms.jcr/src/org/argeo/maintenance/SimpleRoleRegistration.java +++ b/org.argeo.cms.jcr/src/org/argeo/maintenance/SimpleRoleRegistration.java @@ -6,10 +6,10 @@ import java.util.Map; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; -import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.argeo.osgi.transaction.WorkTransaction; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.UserAdmin; @@ -23,7 +23,7 @@ public class SimpleRoleRegistration implements Runnable { private String role; private List roles = new ArrayList(); private UserAdmin userAdmin; - private UserTransaction userTransaction; + private WorkTransaction userTransaction; @Override public void run() { @@ -80,7 +80,7 @@ public class SimpleRoleRegistration implements Runnable { this.userAdmin = userAdminService; } - public void setUserTransaction(UserTransaction userTransaction) { + public void setUserTransaction(WorkTransaction userTransaction) { this.userTransaction = userTransaction; } diff --git a/org.argeo.cms/OSGI-INF/cmsUserManager.xml b/org.argeo.cms/OSGI-INF/cmsUserManager.xml index 045c9609a..524c054ec 100644 --- a/org.argeo.cms/OSGI-INF/cmsUserManager.xml +++ b/org.argeo.cms/OSGI-INF/cmsUserManager.xml @@ -5,6 +5,6 @@ - + - \ No newline at end of file + diff --git a/org.argeo.cms/src/org/argeo/cms/CmsUserManager.java b/org.argeo.cms/src/org/argeo/cms/CmsUserManager.java index d970855ff..cd76d65ef 100644 --- a/org.argeo.cms/src/org/argeo/cms/CmsUserManager.java +++ b/org.argeo.cms/src/org/argeo/cms/CmsUserManager.java @@ -5,12 +5,10 @@ import java.util.List; import java.util.Set; import javax.security.auth.Subject; -import javax.transaction.UserTransaction; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.useradmin.Role; import org.osgi.service.useradmin.User; -import org.osgi.service.useradmin.UserAdmin; /** * Provide method interfaces to manage user concepts without accessing directly @@ -81,9 +79,9 @@ public interface CmsUserManager { // User createUserFromPerson(Node person); - @Deprecated - public UserAdmin getUserAdmin(); - - @Deprecated - public UserTransaction getUserTransaction(); +// @Deprecated +// public UserAdmin getUserAdmin(); +// +// @Deprecated +// public UserTransaction getUserTransaction(); } \ No newline at end of file diff --git a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java index d21e8616c..5753decf9 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/auth/CmsUserManagerImpl.java @@ -22,8 +22,6 @@ import java.util.UUID; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.security.auth.Subject; -import javax.transaction.Status; -import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,6 +32,7 @@ import org.argeo.cms.auth.UserAdminUtils; import org.argeo.naming.LdapAttrs; import org.argeo.naming.NamingUtils; import org.argeo.naming.SharedSecret; +import org.argeo.osgi.transaction.WorkTransaction; import org.argeo.osgi.useradmin.TokenUtils; import org.argeo.osgi.useradmin.UserAdminConf; import org.argeo.osgi.useradmin.UserDirectory; @@ -61,7 +60,7 @@ public class CmsUserManagerImpl implements CmsUserManager { private UserAdmin userAdmin; // private Map serviceProperties; - private UserTransaction userTransaction; + private WorkTransaction userTransaction; private Map> userDirectories = Collections .synchronizedMap(new LinkedHashMap<>()); @@ -351,7 +350,7 @@ public class CmsUserManagerImpl implements CmsUserManager { return tokenStr; } catch (Exception e1) { try { - if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) + if (!userTransaction.isNoTransactionStatus()) userTransaction.rollback(); } catch (Exception e2) { if (log.isTraceEnabled()) @@ -374,7 +373,7 @@ public class CmsUserManagerImpl implements CmsUserManager { log.debug("Token " + token + " expired."); } catch (Exception e1) { try { - if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) + if (!userTransaction.isNoTransactionStatus()) userTransaction.rollback(); } catch (Exception e2) { if (log.isTraceEnabled()) @@ -423,7 +422,7 @@ public class CmsUserManagerImpl implements CmsUserManager { userTransaction.commit(); } catch (Exception e1) { try { - if (userTransaction.getStatus() != Status.STATUS_NO_TRANSACTION) + if (!userTransaction.isNoTransactionStatus()) userTransaction.rollback(); } catch (Exception e2) { if (log.isTraceEnabled()) @@ -468,9 +467,9 @@ public class CmsUserManagerImpl implements CmsUserManager { return userAdmin; } - public UserTransaction getUserTransaction() { - return userTransaction; - } +// public UserTransaction getUserTransaction() { +// return userTransaction; +// } /* DEPENDENCY INJECTION */ public void setUserAdmin(UserAdmin userAdmin) { @@ -478,10 +477,10 @@ public class CmsUserManagerImpl implements CmsUserManager { // this.serviceProperties = serviceProperties; } - public void setUserTransaction(UserTransaction userTransaction) { + public void setUserTransaction(WorkTransaction userTransaction) { this.userTransaction = userTransaction; } - + public void addUserDirectory(UserDirectory userDirectory, Map properties) { userDirectories.put(userDirectory, new Hashtable<>(properties)); } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index cb4d55273..b24fb0a12 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -5,13 +5,12 @@ import java.lang.management.ManagementFactory; import java.net.URL; import java.util.Dictionary; -import javax.transaction.UserTransaction; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.api.NodeConstants; import org.argeo.api.NodeDeployment; import org.argeo.api.NodeState; +import org.argeo.osgi.transaction.WorkTransaction; import org.argeo.osgi.useradmin.UserAdminConf; import org.eclipse.equinox.http.jetty.JettyConfigurator; import org.osgi.framework.BundleContext; @@ -34,7 +33,6 @@ public class CmsDeployment implements NodeDeployment { private Long availableSince; - // Readiness private boolean nodeAvailable = false; private boolean userAdminAvailable = false; @@ -69,7 +67,6 @@ public class CmsDeployment implements NodeDeployment { // httpSt.open(); KernelUtils.asyncOpen(httpSt); - ServiceTracker userAdminSt = new ServiceTracker(bc, UserAdmin.class, null) { @Override public UserAdmin addingService(ServiceReference reference) { @@ -96,7 +93,7 @@ public class CmsDeployment implements NodeDeployment { } catch (Exception e) { throw new IllegalStateException("Cannot analyse clean state", e); } - deployConfig = new DeployConfig(configurationAdmin, isClean); + deployConfig = new DeployConfig(configurationAdmin, isClean); Activator.registerService(NodeDeployment.class, CmsDeployment.this, null); // JcrInitUtils.addToDeployment(CmsDeployment.this); httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null; @@ -145,7 +142,7 @@ public class CmsDeployment implements NodeDeployment { private void addStandardSystemRoles(UserAdmin userAdmin) { // we assume UserTransaction is already available (TODO make it more robust) - UserTransaction userTransaction = bc.getService(bc.getServiceReference(UserTransaction.class)); + WorkTransaction userTransaction = bc.getService(bc.getServiceReference(WorkTransaction.class)); try { userTransaction.begin(); Role adminRole = userAdmin.getRole(NodeConstants.ROLE_ADMIN); @@ -180,7 +177,6 @@ public class CmsDeployment implements NodeDeployment { // if (nodeHttp != null) // nodeHttp.destroy(); - try { JettyConfigurator.stopServer(KernelConstants.DEFAULT_JETTY_SERVER); } catch (Exception e) { @@ -236,7 +232,6 @@ public class CmsDeployment implements NodeDeployment { } } - @Override public synchronized Long getAvailableSince() { return availableSince; @@ -246,5 +241,4 @@ public class CmsDeployment implements NodeDeployment { return availableSince != null; } - } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java index fc9ea7dd7..219f2d53c 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java @@ -8,15 +8,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import javax.transaction.TransactionManager; -import javax.transaction.UserTransaction; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.api.NodeConstants; import org.argeo.api.NodeState; import org.argeo.cms.LocaleUtils; -import org.argeo.transaction.simple.SimpleTransactionManager; +import org.argeo.osgi.transaction.SimpleTransactionManager; +import org.argeo.osgi.transaction.WorkControl; +import org.argeo.osgi.transaction.WorkTransaction; import org.argeo.util.LangUtils; import org.osgi.framework.Constants; import org.osgi.service.cm.ManagedServiceFactory; @@ -117,8 +116,10 @@ public class CmsState implements NodeState { private void initSimpleTransactionManager() { SimpleTransactionManager transactionManager = new SimpleTransactionManager(); - Activator.registerService(TransactionManager.class, transactionManager, null); - Activator.registerService(UserTransaction.class, transactionManager, null); + Activator.registerService(WorkControl.class, transactionManager, null); + Activator.registerService(WorkTransaction.class, transactionManager, null); +// Activator.registerService(TransactionManager.class, transactionManager, null); +// Activator.registerService(UserTransaction.class, transactionManager, null); // TODO TransactionSynchronizationRegistry } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java index 1a9817450..86c6c9c31 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeUserAdmin.java @@ -25,7 +25,6 @@ import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import javax.transaction.TransactionManager; import org.apache.commons.httpclient.auth.AuthPolicy; import org.apache.commons.httpclient.auth.CredentialsProvider; @@ -38,6 +37,7 @@ import org.argeo.api.NodeConstants; import org.argeo.cms.internal.http.client.HttpCredentialProvider; import org.argeo.cms.internal.http.client.SpnegoAuthScheme; import org.argeo.naming.DnsBrowser; +import org.argeo.osgi.transaction.WorkControl; import org.argeo.osgi.useradmin.AbstractUserDirectory; import org.argeo.osgi.useradmin.AggregatingUserAdmin; import org.argeo.osgi.useradmin.LdapUserAdmin; @@ -72,7 +72,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor // private ServiceRegistration userAdminReg; // JTA - private final ServiceTracker tmTracker; + private final ServiceTracker tmTracker; // private final String cacheName = UserDirectory.class.getName(); // GSS API @@ -86,7 +86,7 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor super(systemRolesBaseDn, tokensBaseDn); BundleContext bc = Activator.getBundleContext(); if (bc != null) { - tmTracker = new ServiceTracker<>(bc, TransactionManager.class, null); + tmTracker = new ServiceTracker<>(bc, WorkControl.class, null); tmTracker.open(); } else { tmTracker = null; @@ -194,10 +194,10 @@ class NodeUserAdmin extends AggregatingUserAdmin implements ManagedServiceFactor protected void postAdd(AbstractUserDirectory userDirectory) { // JTA - TransactionManager tm = tmTracker != null ? tmTracker.getService() : null; + WorkControl tm = tmTracker != null ? tmTracker.getService() : null; if (tm == null) throw new IllegalStateException("A JTA transaction manager must be available."); - userDirectory.setTransactionManager(tm); + userDirectory.setTransactionControl(tm); // if (tmTracker.getService() instanceof BitronixTransactionManager) // EhCacheXAResourceProducer.registerXAResource(cacheName, userDirectory.getXaResource()); diff --git a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java index 126125b85..8432ce999 100644 --- a/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java +++ b/org.argeo.enterprise/ext/test/org/argeo/osgi/useradmin/LdifUserAdminTest.java @@ -16,10 +16,8 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import javax.transaction.TransactionManager; - import org.argeo.naming.LdapAttrs; -import org.argeo.transaction.simple.SimpleTransactionManager; +import org.argeo.osgi.transaction.SimpleTransactionManager; import org.osgi.service.useradmin.Authorization; import org.osgi.service.useradmin.Group; import org.osgi.service.useradmin.Role; @@ -36,7 +34,7 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants { final static int TM_BITRONIX = 1; private int tmType = TM_SIMPLE; - private TransactionManager tm; + private SimpleTransactionManager tm; private URI uri; private AbstractUserDirectory userAdmin; private Path tempDir; @@ -171,7 +169,7 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants { } } - private AbstractUserDirectory initUserAdmin(URI uri, TransactionManager tm) { + private AbstractUserDirectory initUserAdmin(URI uri, SimpleTransactionManager tm) { Dictionary props = new Hashtable<>(); props.put(UserAdminConf.uri.name(), uri.toString()); props.put(UserAdminConf.baseDn.name(), BASE_DN); @@ -186,7 +184,7 @@ public class LdifUserAdminTest extends TestCase implements BasicTestConstants { // JTA // if (TM_BITRONIX == tmType) // EhCacheXAResourceProducer.registerXAResource(UserDirectory.class.getName(), userAdmin.getXaResource()); - userAdmin.setTransactionManager(tm); + userAdmin.setTransactionControl(tm); return userAdmin; } diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/JtaStatusAdapter.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/JtaStatusAdapter.java new file mode 100644 index 000000000..2dd94c654 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/JtaStatusAdapter.java @@ -0,0 +1,61 @@ +package org.argeo.osgi.transaction; + +/** JTA transaction status. */ +public class JtaStatusAdapter implements TransactionStatusAdapter { + 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; + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleRollbackException.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleRollbackException.java new file mode 100644 index 000000000..cf8a80b83 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleRollbackException.java @@ -0,0 +1,15 @@ +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); + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransaction.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransaction.java new file mode 100644 index 000000000..e668b317e --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransaction.java @@ -0,0 +1,160 @@ +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 +//implements Transaction, Status +{ + private final Xid xid; + private T status; + private final List xaResources = new ArrayList(); + + private final SimpleTransactionManager transactionManager; + private TransactionStatusAdapter tsa; + + public SimpleTransaction(SimpleTransactionManager transactionManager, TransactionStatusAdapter 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; + } + +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransactionManager.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransactionManager.java new file mode 100644 index 000000000..3d4edfd17 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/SimpleTransactionManager.java @@ -0,0 +1,214 @@ +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> current = new ThreadLocal>(); + + private Map> knownTransactions = Collections + .synchronizedMap(new HashMap>()); + private TransactionStatusAdapter tsa = new JtaStatusAdapter(); +// private SyncRegistry syncRegistry = new SyncRegistry(); + + /* + * WORK IMPLEMENTATION + */ + @Override + public T required(Callable 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 transaction = new SimpleTransaction(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 getTransaction() +// throws SystemException + { + return getCurrent(); + } + + protected SimpleTransaction getCurrent() +// throws SystemException + { + SimpleTransaction 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 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 suspend() +// throws SystemException + { + SimpleTransaction 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); +// } +// } +// +// } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/TransactionStatusAdapter.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/TransactionStatusAdapter.java new file mode 100644 index 000000000..87abceba4 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/TransactionStatusAdapter.java @@ -0,0 +1,22 @@ +package org.argeo.osgi.transaction; + +/** Abstract the various approaches to represent transaction status. */ +public interface TransactionStatusAdapter { + T getActiveStatus(); + + T getPreparingStatus(); + + T getMarkedRollbackStatus(); + + T getPreparedStatus(); + + T getCommittingStatus(); + + T getCommittedStatus(); + + T getRollingBackStatus(); + + T getRolledBackStatus(); + + T getNoTransactionStatus(); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/UuidXid.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/UuidXid.java new file mode 100644 index 000000000..729aef8e5 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/UuidXid.java @@ -0,0 +1,132 @@ +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); + // } +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkContext.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkContext.java new file mode 100644 index 000000000..f50f20870 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkContext.java @@ -0,0 +1,11 @@ +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); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkControl.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkControl.java new file mode 100644 index 000000000..766809555 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkControl.java @@ -0,0 +1,15 @@ +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 required(Callable work); + + void setRollbackOnly(); + + WorkContext getWorkContext(); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkTransaction.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkTransaction.java new file mode 100644 index 000000000..653390912 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/WorkTransaction.java @@ -0,0 +1,15 @@ +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(); +} diff --git a/org.argeo.enterprise/src/org/argeo/osgi/transaction/package-info.java b/org.argeo.enterprise/src/org/argeo/osgi/transaction/package-info.java new file mode 100644 index 000000000..3d3756243 --- /dev/null +++ b/org.argeo.enterprise/src/org/argeo/osgi/transaction/package-info.java @@ -0,0 +1,2 @@ +/** Minimalistic and partial XA transaction manager implementation. */ +package org.argeo.osgi.transaction; \ No newline at end of file diff --git a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java index f2d7c88fc..7279877e0 100644 --- a/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java +++ b/org.argeo.enterprise/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java @@ -27,11 +27,9 @@ import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import javax.transaction.SystemException; -import javax.transaction.Transaction; -import javax.transaction.TransactionManager; import org.argeo.naming.LdapAttrs; +import org.argeo.osgi.transaction.WorkControl; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; @@ -64,8 +62,9 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory private List credentialAttributeIds = Arrays .asList(new String[] { LdapAttrs.userPassword.name(), LdapAttrs.authPassword.name() }); - // JTA - private TransactionManager transactionManager; + // Transaction +// private TransactionManager transactionManager; + private WorkControl transactionControl; private WcXaResource xaResource = new WcXaResource(this); AbstractUserDirectory(URI uriArg, Dictionary props, boolean scoped) { @@ -142,17 +141,18 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory } protected void checkEdit() { - Transaction transaction; - try { - transaction = transactionManager.getTransaction(); - } catch (SystemException e) { - throw new UserDirectoryException("Cannot get transaction", e); - } - if (transaction == null) - throw new UserDirectoryException("A transaction needs to be active in order to edit"); +// Transaction transaction; +// try { +// transaction = transactionManager.getTransaction(); +// } catch (SystemException e) { +// throw new UserDirectoryException("Cannot get transaction", e); +// } +// if (transaction == null) +// throw new UserDirectoryException("A transaction needs to be active in order to edit"); if (xaResource.wc() == null) { try { - transaction.enlistResource(xaResource); +// transaction.enlistResource(xaResource); + transactionControl.getWorkContext().registerXAResource(xaResource, null); } catch (Exception e) { throw new UserDirectoryException("Cannot enlist " + xaResource, e); } @@ -490,8 +490,12 @@ public abstract class AbstractUserDirectory implements UserAdmin, UserDirectory this.externalRoles = externalRoles; } - public void setTransactionManager(TransactionManager transactionManager) { - this.transactionManager = transactionManager; +// public void setTransactionManager(TransactionManager transactionManager) { +// this.transactionManager = transactionManager; +// } + + public void setTransactionControl(WorkControl transactionControl) { + this.transactionControl = transactionControl; } public WcXaResource getXaResource() { diff --git a/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransaction.java b/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransaction.java deleted file mode 100644 index 43d4cd91c..000000000 --- a/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransaction.java +++ /dev/null @@ -1,159 +0,0 @@ -package org.argeo.transaction.simple; - -import java.util.ArrayList; -import java.util.List; - -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.RollbackException; -import javax.transaction.Status; -import javax.transaction.Synchronization; -import javax.transaction.SystemException; -import javax.transaction.Transaction; -import javax.transaction.xa.XAException; -import javax.transaction.xa.XAResource; -import javax.transaction.xa.Xid; - -/** Simple implementation of an XA {@link Transaction}. */ -class SimpleTransaction implements Transaction, Status { - private final Xid xid; - private int status = Status.STATUS_ACTIVE; - private final List xaResources = new ArrayList(); - - private final SimpleTransactionManager transactionManager; - - public SimpleTransaction(SimpleTransactionManager transactionManager) { - this.xid = new UuidXid(); - this.transactionManager = transactionManager; - } - - @Override - public synchronized void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, - SecurityException, IllegalStateException, SystemException { - status = STATUS_PREPARING; - for (XAResource xaRes : xaResources) { - if (status == STATUS_MARKED_ROLLBACK) - break; - try { - xaRes.prepare(xid); - } catch (XAException e) { - status = STATUS_MARKED_ROLLBACK; - error("Cannot prepare " + xaRes + " for " + xid, e); - } - } - if (status == STATUS_MARKED_ROLLBACK) { - rollback(); - throw new RollbackException(); - } - status = STATUS_PREPARED; - - status = STATUS_COMMITTING; - for (XAResource xaRes : xaResources) { - if (status == STATUS_MARKED_ROLLBACK) - break; - try { - xaRes.commit(xid, false); - } catch (XAException e) { - status = STATUS_MARKED_ROLLBACK; - error("Cannot prepare " + xaRes + " for " + xid, e); - } - } - if (status == STATUS_MARKED_ROLLBACK) { - rollback(); - throw new RollbackException(); - } - - // complete - status = STATUS_COMMITTED; - clearResources(XAResource.TMSUCCESS); - transactionManager.unregister(xid); - } - - @Override - public synchronized void rollback() throws IllegalStateException, SystemException { - status = STATUS_ROLLING_BACK; - for (XAResource xaRes : xaResources) { - try { - xaRes.rollback(xid); - } catch (XAException e) { - error("Cannot rollback " + xaRes + " for " + xid, e); - } - } - - // complete - status = STATUS_ROLLEDBACK; - clearResources(XAResource.TMFAIL); - transactionManager.unregister(xid); - } - - @Override - 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; - } - - @Override - 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(); - } - - @Override - public synchronized int getStatus() throws SystemException { - return status; - } - - @Override - public void registerSynchronization(Synchronization sync) - throws RollbackException, IllegalStateException, SystemException { - throw new UnsupportedOperationException(); - } - - @Override - public void setRollbackOnly() throws IllegalStateException, SystemException { - status = STATUS_MARKED_ROLLBACK; - } - - @Override - public int hashCode() { - return xid.hashCode(); - } - - Xid getXid() { - return xid; - } - -} diff --git a/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransactionManager.java b/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransactionManager.java deleted file mode 100644 index 7dcf7d99a..000000000 --- a/org.argeo.enterprise/src/org/argeo/transaction/simple/SimpleTransactionManager.java +++ /dev/null @@ -1,170 +0,0 @@ -package org.argeo.transaction.simple; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.transaction.HeuristicMixedException; -import javax.transaction.HeuristicRollbackException; -import javax.transaction.InvalidTransactionException; -import javax.transaction.NotSupportedException; -import javax.transaction.RollbackException; -import javax.transaction.Status; -import javax.transaction.Synchronization; -import javax.transaction.SystemException; -import javax.transaction.Transaction; -import javax.transaction.TransactionManager; -import javax.transaction.TransactionSynchronizationRegistry; -import javax.transaction.UserTransaction; -import javax.transaction.xa.Xid; - -/** - * Simple implementation of an XA {@link TransactionManager} and - * {@link UserTransaction}. - */ -public class SimpleTransactionManager implements TransactionManager, UserTransaction { - private ThreadLocal current = new ThreadLocal(); - - private Map knownTransactions = Collections - .synchronizedMap(new HashMap()); - private SyncRegistry syncRegistry = new SyncRegistry(); - - @Override - public void begin() throws NotSupportedException, SystemException { - if (getCurrent() != null) - throw new NotSupportedException("Nested transactions are not supported"); - SimpleTransaction transaction = new SimpleTransaction(this); - knownTransactions.put(transaction.getXid(), transaction); - current.set(transaction); - } - - @Override - 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(); - } - - @Override - public int getStatus() throws SystemException { - if (getCurrent() == null) - return Status.STATUS_NO_TRANSACTION; - return getTransaction().getStatus(); - } - - @Override - public Transaction getTransaction() throws SystemException { - return getCurrent(); - } - - protected SimpleTransaction getCurrent() throws SystemException { - SimpleTransaction transaction = current.get(); - if (transaction == null) - return null; - int status = transaction.getStatus(); - if (Status.STATUS_COMMITTED == status || Status.STATUS_ROLLEDBACK == status) { - current.remove(); - return null; - } - return transaction; - } - - void unregister(Xid xid) { - knownTransactions.remove(xid); - } - - @Override - public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { - if (getCurrent() != null) - throw new IllegalStateException("Transaction " + current.get() + " already registered"); - current.set((SimpleTransaction) tobj); - } - - @Override - public void rollback() throws IllegalStateException, SecurityException, SystemException { - if (getCurrent() == null) - throw new IllegalStateException("No transaction registered with the current thread."); - getCurrent().rollback(); - } - - @Override - public void setRollbackOnly() throws IllegalStateException, SystemException { - if (getCurrent() == null) - throw new IllegalStateException("No transaction registered with the current thread."); - getCurrent().setRollbackOnly(); - } - - @Override - public void setTransactionTimeout(int seconds) throws SystemException { - throw new UnsupportedOperationException(); - } - - @Override - public Transaction suspend() throws SystemException { - Transaction 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); - } - } - - } -} diff --git a/org.argeo.enterprise/src/org/argeo/transaction/simple/UuidXid.java b/org.argeo.enterprise/src/org/argeo/transaction/simple/UuidXid.java deleted file mode 100644 index 1009c8200..000000000 --- a/org.argeo.enterprise/src/org/argeo/transaction/simple/UuidXid.java +++ /dev/null @@ -1,132 +0,0 @@ -package org.argeo.transaction.simple; - -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); - // } -} diff --git a/org.argeo.enterprise/src/org/argeo/transaction/simple/package-info.java b/org.argeo.enterprise/src/org/argeo/transaction/simple/package-info.java deleted file mode 100644 index 19adecc49..000000000 --- a/org.argeo.enterprise/src/org/argeo/transaction/simple/package-info.java +++ /dev/null @@ -1,2 +0,0 @@ -/** Minimalistic and partial XA transaction manager implementation. */ -package org.argeo.transaction.simple; \ No newline at end of file diff --git a/pom.xml b/pom.xml index c4d70f610..88ab72780 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 org.argeo.maven @@ -19,7 +21,7 @@ org.argeo.enterprise - + org.argeo.osgi.boot org.argeo.core @@ -27,7 +29,7 @@ org.argeo.eclipse.ui.rap org.argeo.api - + org.argeo.cms org.argeo.cms.jcr org.argeo.cms.ui.theme @@ -96,6 +98,11 @@ org.argeo.tp.misc slf4j.osgi + + + org.argeo.tp.javax + javax.transaction-api +