From 065fd29c52af058c977b2a7bc7b2b0c15a652a06 Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Tue, 13 Oct 2009 14:43:23 +0000 Subject: [PATCH] Merged V3.2 to HEAD 15888: ETHREEOH-2617: CIFS Authenticators should not try to initialize when disabled - removed init-method declaration from cifsAuthenticatorBase 15731: ENH-524: Use JobLockService to ensure that it is only possible for LDAP sync to run on one node at a time in a cluster - Ensures that if schedule is identical on all nodes, the LDAP sync will only be run on one 15694: Fix TransactionServiceImplTest broken by 15685 15685: ETHREEOH-983: Move RepoServerMgmt JMX editable capabilities into a sysAdmin subsystem for more consistent control and cluster support - New SysAdminParams interface exported by sysAdmin subsystem through which AuthenticationService and TransactionService get at the configured parameters - The repository read only flag does not apply to the system user so that we can still persist changes to that flag through JMX! - Removed sysAdminCache and supporting configuration. 15684: Improvements to cluster support for subsystems - When a subsystem is stopped on a node for editing it is completely destroyed and deregistered from JMX on other nodes - Should the subsystem be reactivated on those other nodes (e.g. called into by code) it will be reinitialized from persisted properties and thus stay in sync with the node being edited! 15683: Fixed potential concurrency issues in HeartBeat and LicenseComponent - Discovered during cluster testing - Because these components schedule triggers in a retrying transaction, they need to unschedule the triggers beforehand, just in case a retry has happened 15617: MOB-646: JMX edits now synchronized across cluster via JGroups - When you stop a component or subsystem, it is stopped across the entire cluster - When you restart it after editing properties, the component is reinitialized from the persisted properties across the cluster git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@16873 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco-global.properties.sample | 3 +- .../authentication-services-context.xml | 4 +- config/alfresco/bootstrap-context.xml | 3 + config/alfresco/cache-context.xml | 33 -- config/alfresco/core-services-context.xml | 23 +- config/alfresco/ehcache-default.xml | 6 - .../linkvalidation-service-context.xml | 3 - .../alfresco-authentication-context.xml | 4 +- .../alfresco-authentication-context.xml | 4 +- .../Authentication/common-ldap-context.xml | 4 +- .../kerberos-authentication-context.xml | 4 +- .../passthru-authentication-context.xml | 4 +- .../default-synchronization-context.xml | 28 +- .../default/sysadmin-parameter-context.xml | 18 + .../default/sysadmin-parameter.properties | 3 + .../linkvalidation/LinkValidationService.java | 2 - .../alfresco/repo/admin/RepoServerMgmt.java | 194 +------ .../repo/admin/RepoServerMgmtMBean.java | 51 +- .../alfresco/repo/admin/SysAdminParams.java | 57 ++ .../repo/admin/SysAdminParamsImpl.java | 155 ++++++ .../descriptor/DescriptorServiceImpl.java | 86 +-- .../AbstractPropertyBackedBean.java | 287 ++++++++-- .../ChildApplicationContextFactory.java | 524 ++++++++++-------- .../subsystems/CompositeDataBean.java | 190 ++++--- ...DefaultChildApplicationContextManager.java | 419 ++++++++------ .../DefaultPropertyBackedBeanRegistry.java | 22 + .../subsystems/PropertyBackedBean.java | 70 +-- .../subsystems/PropertyBackedBeanEvent.java | 16 +- .../PropertyBackedBeanRegisteredEvent.java | 5 +- .../PropertyBackedBeanRegistry.java | 17 + .../PropertyBackedBeanStartedEvent.java | 49 ++ .../subsystems/PropertyBackedBeanState.java | 77 +++ .../PropertyBackedBeanStoppedEvent.java | 49 ++ .../PropertyBackedBeanUnregisteredEvent.java | 4 +- .../SwitchableApplicationContextFactory.java | 214 ++++--- .../AbstractAuthenticationService.java | 92 +-- .../ChainingUserRegistrySynchronizer.java | 79 ++- .../transaction/TransactionServiceImpl.java | 95 ++-- .../TransactionServiceImplTest.java | 18 +- source/test-resources/sync-test-context.xml | 5 +- 40 files changed, 1765 insertions(+), 1156 deletions(-) create mode 100644 config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml create mode 100644 config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties create mode 100644 source/java/org/alfresco/repo/admin/SysAdminParams.java create mode 100644 source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java create mode 100644 source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStartedEvent.java create mode 100644 source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java create mode 100644 source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStoppedEvent.java diff --git a/config/alfresco-global.properties.sample b/config/alfresco-global.properties.sample index d35d089227..dbabf5079c 100644 --- a/config/alfresco-global.properties.sample +++ b/config/alfresco-global.properties.sample @@ -31,7 +31,7 @@ # alfresco.rmi.services.port=50500 # # RMI service ports for the individual services. -# These six services are available remotely. +# These seven services are available remotely. # # Assign individual ports for each service for best performance # or run several services on the same port. You can even run everything on 50500 if needed. @@ -44,3 +44,4 @@ #authentication.rmi.service.port=50504 #repo.rmi.service.port=50505 #action.rmi.service.port=50506 +#wcm-deployment-receiver.rmi.service.port=50507 \ No newline at end of file diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 44deadc447..3d320cd7ca 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -142,8 +142,8 @@ - - + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 261a80f8dc..e90acf725e 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -387,6 +387,9 @@ + + + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index f243c7b94c..2f818357dd 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -498,39 +498,6 @@ - - - - - - - - - - - - org.alfresco.cache.sysAdminCache - - - - - - - - - - - - - - org.alfresco.sysAdminTransactionalCache - - - 10 - - - - diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 5253fb490c..323ce9b7c9 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -214,8 +214,6 @@ - ${server.maxusers} - ${server.singleuseronly.name} @@ -316,8 +314,11 @@ - - + + + + + ${server.transaction.allow-writes} @@ -1396,5 +1397,17 @@ 10 20 - + + + + + + + + + org.alfresco.repo.admin.SysAdminParams + + + + diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index 88a1086708..57b4a3872f 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -317,12 +317,6 @@ eternal="true" overflowToDisk="true" /> - - - - diff --git a/config/alfresco/subsystems/Authentication/alfresco/alfresco-authentication-context.xml b/config/alfresco/subsystems/Authentication/alfresco/alfresco-authentication-context.xml index 4059525b1e..fffc263a3a 100644 --- a/config/alfresco/subsystems/Authentication/alfresco/alfresco-authentication-context.xml +++ b/config/alfresco/subsystems/Authentication/alfresco/alfresco-authentication-context.xml @@ -114,8 +114,8 @@ - - + + diff --git a/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml b/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml index 65c1708705..7e37f55708 100644 --- a/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml +++ b/config/alfresco/subsystems/Authentication/alfrescoNtlm/alfresco-authentication-context.xml @@ -82,8 +82,8 @@ - - + + diff --git a/config/alfresco/subsystems/Authentication/common-ldap-context.xml b/config/alfresco/subsystems/Authentication/common-ldap-context.xml index 0075fd01b0..a38df65dfa 100644 --- a/config/alfresco/subsystems/Authentication/common-ldap-context.xml +++ b/config/alfresco/subsystems/Authentication/common-ldap-context.xml @@ -98,8 +98,8 @@ - - + + diff --git a/config/alfresco/subsystems/Authentication/kerberos/kerberos-authentication-context.xml b/config/alfresco/subsystems/Authentication/kerberos/kerberos-authentication-context.xml index 66bbc2b2fe..77ccf8bd9d 100644 --- a/config/alfresco/subsystems/Authentication/kerberos/kerberos-authentication-context.xml +++ b/config/alfresco/subsystems/Authentication/kerberos/kerberos-authentication-context.xml @@ -68,8 +68,8 @@ - - + + diff --git a/config/alfresco/subsystems/Authentication/passthru/passthru-authentication-context.xml b/config/alfresco/subsystems/Authentication/passthru/passthru-authentication-context.xml index 12267243ca..b0417da58e 100644 --- a/config/alfresco/subsystems/Authentication/passthru/passthru-authentication-context.xml +++ b/config/alfresco/subsystems/Authentication/passthru/passthru-authentication-context.xml @@ -103,8 +103,8 @@ - - + + diff --git a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml index f67dffe3d3..22bdaba0a0 100644 --- a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml +++ b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml @@ -1,9 +1,12 @@ - - - + + + @@ -13,7 +16,7 @@ - + ${synchronization.synchronizeChangesOnly} @@ -23,22 +26,22 @@ - ${synchronization.import.cron} - + ${synchronization.import.cron} + - + - + ${synchronization.syncWhenMissingPeopleLogIn} - + ${synchronization.syncOnStartup} - + ${synchronization.autoCreatePeopleOnLogin} @@ -54,7 +57,10 @@ - + + + + userRegistry diff --git a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml new file mode 100644 index 0000000000..1aef144a3d --- /dev/null +++ b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml @@ -0,0 +1,18 @@ + + + + + + + + ${server.maxusers} + + + ${server.allowedusers} + + + ${server.transaction.allow-writes} + + + + diff --git a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties new file mode 100644 index 0000000000..c1a3b01789 --- /dev/null +++ b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties @@ -0,0 +1,3 @@ +server.maxusers=-1 +server.allowedusers= +server.transaction.allow-writes=true \ No newline at end of file diff --git a/source/java/org/alfresco/linkvalidation/LinkValidationService.java b/source/java/org/alfresco/linkvalidation/LinkValidationService.java index 44bb79393a..bafc440d78 100644 --- a/source/java/org/alfresco/linkvalidation/LinkValidationService.java +++ b/source/java/org/alfresco/linkvalidation/LinkValidationService.java @@ -190,7 +190,5 @@ public interface LinkValidationService //------------------------------------------------------------------------- public List getHrefsDependentUponFile(String path); - public void setLinkValidationDisabled(boolean disabled); - public boolean isLinkValidationDisabled(); } diff --git a/source/java/org/alfresco/repo/admin/RepoServerMgmt.java b/source/java/org/alfresco/repo/admin/RepoServerMgmt.java index 3e8aee2a94..28b4b0689b 100644 --- a/source/java/org/alfresco/repo/admin/RepoServerMgmt.java +++ b/source/java/org/alfresco/repo/admin/RepoServerMgmt.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,17 +15,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.admin; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -34,29 +32,19 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.linkvalidation.LinkValidationService; import org.alfresco.repo.security.authentication.AbstractAuthenticationService; import org.alfresco.repo.transaction.TransactionServiceImpl; -import org.alfresco.service.license.LicenseService; -import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAware, InitializingBean +public class RepoServerMgmt implements RepoServerMgmtMBean { private static final Log log = LogFactory.getLog(RepoServerMgmt.class); - private ApplicationContext ctx; // to get license component, if installed - private TransactionServiceImpl transactionService; private AbstractAuthenticationService authenticationService; private LinkValidationService linkValidationService; - private boolean initialised = false; - public void setTransactionService(TransactionServiceImpl transactionService) { this.transactionService = transactionService; @@ -73,50 +61,6 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw this.linkValidationService = linkValidationService; } - public void setApplicationContext(ApplicationContext ctx) - { - this.ctx = ctx; - } - - public void setReadOnly(boolean readOnly) - { - if (readOnly && isReadOnly()) - { - log.warn("Alfresco is already read-only"); - return; - } - - if (!readOnly && !isReadOnly()) - { - log.warn("Alfresco is already read-write"); - return; - } - - if (!readOnly) - { - LicenseService licenseService = null; - try - { - licenseService = (LicenseService) ctx.getBean("org.alfresco.enterprise.license.LicenseComponent"); - readOnly = !licenseService.isLicenseValid(); - } - catch (NoSuchBeanDefinitionException e) - { - // ignore - } - } - transactionService.setAllowWrite(!readOnly); - - if (readOnly) - { - log.warn("Alfresco set to be read-only"); - } - else - { - log.info("Alfresco set to be read-write"); - } - } - public boolean isReadOnly() { return transactionService.isReadOnly(); @@ -182,130 +126,11 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw log.info("User invalidated: " + username); } - public void setSingleUserOnly(String allowedUsername) - { - - List allowedUsers = null; - if (PropertyCheck.isValidPropertyString(allowedUsername)) - { - allowedUsers = new ArrayList(0); - allowedUsers.add(allowedUsername); - - if (initialised) - { - int maxUsers = getMaxUsers(); - invalidateTicketsAll(); - - if (maxUsers != 0) - { - log.warn("Alfresco set to allow single-user (" + allowedUsername + ") logins only"); - } - else - { - log.warn("Alfresco set to allow single-user (" + allowedUsername + ") logins - although further logins are currently prevented (limit = 0)"); - } - } - } - else - { - if (initialised) - { - int maxUsers = getMaxUsers(); - if (maxUsers == -1) - { - log.info("Alfresco set to allow logins (no limit set)"); - } - else if (maxUsers == 0) - { - log.warn("Alfresco set to allow logins - although further logins are currently prevented (limit = 0)"); - } - else if (maxUsers != 0) - { - log.info("Alfresco set to allow logins (limit = " + maxUsers + ")"); - } - } - } - - authenticationService.setAllowedUsers(allowedUsers); - } - - public String getSingleUserOnly() - { - List allowedUsers = authenticationService.getAllowedUsers(); - if (allowedUsers != null) - { - if (allowedUsers.size() > 1) - { - throw new AlfrescoRuntimeException("Unexpected: more than one user allowed"); - } - if (allowedUsers.size() == 1) - { - return allowedUsers.get(0); - } - } - return null; - } - - public void setMaxUsers(int maxUsers) - { - authenticationService.setMaxUsers(maxUsers); - - if (initialised) - { - String singleUserOnlyName = getSingleUserOnly(); - if (maxUsers == -1) - { - if ((singleUserOnlyName != null) && (!singleUserOnlyName.equals(""))) - { - log.info("Alfresco set to allow logins (no limit set) - although currently restricted to single-user (" + singleUserOnlyName + ")"); - } - else - { - log.info("Alfresco set to allow logins (no limit set)"); - } - } - else if (maxUsers == 0) - { - log.warn("Alfresco set to prevent further logins (limit = 0)"); - } - else - { - if ((singleUserOnlyName != null) && (!singleUserOnlyName.equals(""))) - { - log.info("Alfresco set to allow logins (limit = " + maxUsers + ") - although currently restricted to single-user (" + singleUserOnlyName + ")"); - } - else - { - log.info("Alfresco set to allow logins (limit = " + maxUsers + ")"); - } - } - } - } - public int getMaxUsers() { return authenticationService.getMaxUsers(); } - public void setLinkValidationDisabled(boolean disable) - { - if (linkValidationService == null) - { - log.error("LinkValidationService not registered"); - throw new AlfrescoRuntimeException("LinkValidationService not registered"); - } - - linkValidationService.setLinkValidationDisabled(disable); - if (disable) - { - log.warn("Link validation disabled"); - } - else - { - log.info("Link validation enabled"); - } - } - public boolean isLinkValidationDisabled() { if (linkValidationService == null) @@ -316,9 +141,4 @@ public class RepoServerMgmt implements RepoServerMgmtMBean, ApplicationContextAw return linkValidationService.isLinkValidationDisabled(); } - - public void afterPropertiesSet() throws Exception - { - initialised = true; - } } diff --git a/source/java/org/alfresco/repo/admin/RepoServerMgmtMBean.java b/source/java/org/alfresco/repo/admin/RepoServerMgmtMBean.java index 7bb3dbb434..c8d378af9d 100644 --- a/source/java/org/alfresco/repo/admin/RepoServerMgmtMBean.java +++ b/source/java/org/alfresco/repo/admin/RepoServerMgmtMBean.java @@ -32,13 +32,6 @@ package org.alfresco.repo.admin; */ public interface RepoServerMgmtMBean { - /** - * Set whether Repository allows writes or not - * - * @param readOnly true is READONLY, false is WRITEABLE - */ - public void setReadOnly(boolean readOnly); - /** * Does the Repository allows writes or not ? * @@ -117,42 +110,6 @@ public interface RepoServerMgmtMBean */ public void invalidateUser(String username); - /** - * Set whether Repository allows single user mode or not - * - * If single user mode is set then all tickets will be invalidated first before allowing the - * named user to login (with one or more sessions) assuming maxUsers is not set to 0 - * - * Note: This can also be configured at startup. Refer to repository property (server.singleuseronly.name). - * - * @param String allowed username (eg. 'admin') or null to unset (ie. allow all users) - */ - public void setSingleUserOnly(String allowedUsername); - - /** - * If Repository is in single user mode then return the name of the allowed user else return null - * - * @param String allowed username (eg. 'admin') or null (ie. allow all users) - */ - public String getSingleUserOnly(); - - /** - * Set limit for max users and/or prevent further logins - * - * If number of non-expired logins is greater or equal to the limit then further logins will be prevented - * otherwise valid login attempt will be permitted, unless the system is in single-user mode. - * - * Note: - * - * Max users = 0 prevents further logins (will also prevent single-user mode login) - * Max users = -1 allow logins (without a max limit) - * - * Note: This can also be configured at startup. Refer to repository property (server.maxusers). - * - * @param maxUsers - */ - public void setMaxUsers(int maxUsers); - /** * Get limit for max users * @@ -166,13 +123,7 @@ public interface RepoServerMgmtMBean */ public int getMaxUsers(); - /** - * Disable or re-enable link validation - * - * @param disable true = disable, false = re-enable - */ - public void setLinkValidationDisabled(boolean disable); - + /** * Is link validation disabled ? * diff --git a/source/java/org/alfresco/repo/admin/SysAdminParams.java b/source/java/org/alfresco/repo/admin/SysAdminParams.java new file mode 100644 index 0000000000..722556d684 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/SysAdminParams.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.admin; + +import java.util.List; + +/** + * An interface for retrieving configurable system parameters. + * + * @author dward + */ +public interface SysAdminParams +{ + + /** + * Do we allow write operations by non-system users on the repository?. + * + * @return true if we allow write operations by non-system users on the repository + */ + public boolean getAllowWrite(); + + /** + * Gets the list of users who are allowed to log in. + * + * @return the allowed user list or null if all users are allowed to log in + */ + public List getAllowedUserList(); + + /** + * Gets the maximum number of users who are allowed to log in. + * + * @return the the maximum number of users who are allowed to log in + */ + public int getMaxUsers(); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java b/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java new file mode 100644 index 0000000000..77d4d8a5ff --- /dev/null +++ b/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.admin; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.alfresco.service.license.LicenseService; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +/** + * Configurable system parameters. + */ +public class SysAdminParamsImpl implements SysAdminParams, ApplicationContextAware, InitializingBean +{ + /** The application context, to get license component, if installed. */ + private ApplicationContext ctx; + + /** The max users. */ + private Integer maxUsers; + + /** The allowed users. */ + private List allowedUsers; + + /** The allow write. */ + private boolean allowWrite = true; + + /* + * (non-Javadoc) + * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context. + * ApplicationContext) + */ + public void setApplicationContext(ApplicationContext ctx) + { + this.ctx = ctx; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + public void afterPropertiesSet() throws Exception + { + if (this.allowWrite) + { + LicenseService licenseService = null; + try + { + licenseService = (LicenseService) this.ctx.getBean("licenseService"); + this.allowWrite = licenseService.isLicenseValid(); + } + catch (NoSuchBeanDefinitionException e) + { + // ignore + } + } + } + + /** + * Sets the list of users who are allowed to log in. + * + * @param allowedUsers + * a comma-separated list of users who are allowed to log in or null if all users are + * allowed to log in + */ + public void setAllowedUsers(String allowedUsers) + { + StringTokenizer tkn = new StringTokenizer(allowedUsers, ","); + int length = tkn.countTokens(); + if (length > 0) + { + this.allowedUsers = new ArrayList(length); + while (tkn.hasMoreTokens()) + { + this.allowedUsers.add(tkn.nextToken().trim()); + } + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.admin.SysAdminParams#getAllowedUserList() + */ + public List getAllowedUserList() + { + return this.allowedUsers; + } + + /** + * Sets the maximum number of users who are allowed to log in. + * + * @param maxUsers + * the maximum number of users who are allowed to log in + */ + public void setMaxUsers(int maxUsers) + { + this.maxUsers = new Integer(maxUsers); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.admin.SysAdminParams#getMaxUsers() + */ + public int getMaxUsers() + { + return this.maxUsers; + } + + /** + * Controls where we allow write operations by non-system users on the repository. + * + * @param allowWrite + * true if we allow write operations by non-system users on the repository + */ + public void setAllowWrite(boolean allowWrite) + { + this.allowWrite = allowWrite; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.admin.SysAdminParams#getAllowWrite() + */ + public boolean getAllowWrite() + { + return this.allowWrite; + } + +} diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java index 62be916e2e..448a629d3d 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java @@ -165,21 +165,31 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean implements Desc @Override protected void onBootstrap(ApplicationEvent event) { - // Initialize the repository descriptor - // note: this requires that the repository schema has already been initialized - final RetryingTransactionCallback createDescriptorWork = new RetryingTransactionCallback() + AuthenticationUtil.runAs(new RunAsWork() { - public Descriptor execute() throws ClassNotFoundException + public Object doWork() throws Exception { - boolean initialiseHeartBeat = false; + final boolean initialiseHeartBeat; // Initialize license service (if installed) - DescriptorServiceImpl.this.licenseService = (LicenseService) constructSpecialService("org.alfresco.enterprise.license.LicenseComponent"); + DescriptorServiceImpl.this.licenseService = DescriptorServiceImpl.this.transactionService + .getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionCallback() + { + public LicenseService execute() + { + return (LicenseService) constructSpecialService("org.alfresco.enterprise.license.LicenseComponent"); + } + }, DescriptorServiceImpl.this.transactionService.isReadOnly(), false); if (DescriptorServiceImpl.this.licenseService == null) { DescriptorServiceImpl.this.licenseService = new NOOPLicenseService(); initialiseHeartBeat = true; } + else + { + initialiseHeartBeat = false; + } // Make the license service available through the application context as a singleton for other beans // that need it (e.g. the HeartBeat). @@ -190,39 +200,41 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean implements Desc "licenseService", DescriptorServiceImpl.this.licenseService); } - // verify license, but only if license component is installed - try - { - DescriptorServiceImpl.this.licenseService.verifyLicense(); - LicenseDescriptor l = DescriptorServiceImpl.this.licenseService.getLicense(); - // Initialize the heartbeat unless it is disabled by the license - if (initialiseHeartBeat || l == null || !l.isHeartBeatDisabled()) - { - DescriptorServiceImpl.this.heartBeat = constructSpecialService("org.alfresco.enterprise.heartbeat.HeartBeat"); - } - } - catch (LicenseException e) - { - // Initialize heart beat anyway - DescriptorServiceImpl.this.heartBeat = constructSpecialService("org.alfresco.enterprise.heartbeat.HeartBeat"); - throw e; - } + DescriptorServiceImpl.this.installedRepoDescriptor = DescriptorServiceImpl.this.transactionService + .getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Descriptor execute() throws ClassNotFoundException + { - // persist the server descriptor values - DescriptorServiceImpl.this.currentRepoDescriptor = DescriptorServiceImpl.this.currentRepoDescriptorDAO - .updateDescriptor(DescriptorServiceImpl.this.serverDescriptor); + // verify license, but only if license component is installed + try + { + DescriptorServiceImpl.this.licenseService.verifyLicense(); + LicenseDescriptor l = DescriptorServiceImpl.this.licenseService.getLicense(); + // Initialize the heartbeat unless it is disabled by the license + if (initialiseHeartBeat || l == null || !l.isHeartBeatDisabled()) + { + DescriptorServiceImpl.this.heartBeat = constructSpecialService("org.alfresco.enterprise.heartbeat.HeartBeat"); + } + } + catch (LicenseException e) + { + // Initialize heart beat anyway + DescriptorServiceImpl.this.heartBeat = constructSpecialService("org.alfresco.enterprise.heartbeat.HeartBeat"); + throw e; + } - // create the installed descriptor - Descriptor installed = DescriptorServiceImpl.this.installedRepoDescriptorDAO.getDescriptor(); - return installed == null ? new UnknownDescriptor() : installed; - } - }; - this.installedRepoDescriptor = AuthenticationUtil.runAs(new RunAsWork() - { - public Descriptor doWork() throws Exception - { - return DescriptorServiceImpl.this.transactionService.getRetryingTransactionHelper().doInTransaction( - createDescriptorWork, DescriptorServiceImpl.this.transactionService.isReadOnly(), false); + // persist the server descriptor values + DescriptorServiceImpl.this.currentRepoDescriptor = DescriptorServiceImpl.this.currentRepoDescriptorDAO + .updateDescriptor(DescriptorServiceImpl.this.serverDescriptor); + + // create the installed descriptor + Descriptor installed = DescriptorServiceImpl.this.installedRepoDescriptorDAO + .getDescriptor(); + return installed == null ? new UnknownDescriptor() : installed; + } + }, DescriptorServiceImpl.this.transactionService.isReadOnly(), false); + return null; } }, AuthenticationUtil.getSystemUserName()); diff --git a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java index 1e968a61e3..da8cee3dc0 100644 --- a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java @@ -24,10 +24,13 @@ */ package org.alfresco.repo.management.subsystems; +import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Properties; +import java.util.Set; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanNameAware; @@ -43,7 +46,10 @@ import org.springframework.context.event.ContextRefreshedEvent; /** * A base class for {@link PropertyBackedBean}s. Gets its category from its Spring bean name and automatically * propagates and resolves property defaults on initialization. Automatically destroys itself on server shutdown. - * Communicates its creation and destruction to a {@link PropertyBackedBeanRegistry}. + * Communicates its creation and destruction and start and stop events to a {@link PropertyBackedBeanRegistry}. Listens + * for start and stop events from remote nodes in order to keep the bean in sync with edits made on a remote node. On + * receiving a start event from a remote node, the bean is completely reinitialized, allowing it to be resynchronized + * with any persisted changes. * * @author dward */ @@ -51,12 +57,8 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, ApplicationListener, InitializingBean, DisposableBean, BeanNameAware { - /** The root component of the default ID. */ - protected static final String DEFAULT_ID_ROOT = "default"; - - /** The default ID (when we do not expect there to be more than one instance within a category). */ - protected static final List DEFAULT_ID = Collections - .singletonList(AbstractPropertyBackedBean.DEFAULT_ID_ROOT); + /** The default final part of an ID. */ + protected static final String DEFAULT_INSTANCE_NAME = "default"; /** The parent application context. */ private ApplicationContext parent; @@ -64,12 +66,15 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, /** The registry of all property backed beans. */ private PropertyBackedBeanRegistry registry; - /** The hierarchical id. Must be unique within the category. */ - private List id = AbstractPropertyBackedBean.DEFAULT_ID; - - /** The category. */ + /** The category (first part of the ID). */ private String category; + /** The hierarchical instance path within the category (second part of the ID). */ + private List instancePath = Collections.singletonList(AbstractPropertyBackedBean.DEFAULT_INSTANCE_NAME); + + /** The combined unique id. */ + private List id; + /** Should the application context be started on startup of the parent application?. */ private boolean autoStart; @@ -79,6 +84,12 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, /** Resolves placeholders in the property defaults. */ private DefaultResolver defaultResolver = new DefaultResolver(); + /** Has the state been started yet?. */ + private boolean isStarted; + + /** The state. */ + private PropertyBackedBeanState state; + /* * (non-Javadoc) * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context. @@ -120,14 +131,14 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, } /** - * Sets the id. + * Sets the hierarchical instance path within the category (second part of the ID).. * - * @param id - * the id to set + * @param instancePath + * the instance path */ - public void setId(List id) + public void setInstancePath(List instancePath) { - this.id = id; + this.instancePath = instancePath; } /** @@ -189,44 +200,126 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, return this.parent; } + /** + * Gets the state. + * + * @param start + * are we making use of the state? I.e. should we start it if it has not been already? + * @return the state + */ + protected synchronized PropertyBackedBeanState getState(boolean start) + { + if (start) + { + start(); + } + return this.state; + } + /* * (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() throws Exception { - // Override default settings using corresponding global defaults (this allows installer settings - // to propagate through) - for (String property : getPropertyNames()) - { - String value = resolveDefault(property); - if (value != null) - { - setProperty(property, value); - } - } + // Derive the unique ID from the category and instance path + List path = getInstancePath(); + this.id = new ArrayList(path.size() + 1); + this.id.add(getCategory()); + this.id.addAll(getInstancePath()); - this.registry.register(this); + init(); + } + + /** + * Initializes or resets the bean and its state. + */ + public void init() + { + if (this.state == null) + { + try + { + this.state = createInitialState(); + applyDefaultOverrides(this.state); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + this.registry.register(this); + } } /* * (non-Javadoc) - * @see org.alfresco.repo.management.SelfDescribingBean#getId() + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#revert() + */ + public synchronized void revert() + { + stop(); + destroy(true); + init(); + } + + /** + * Creates the initial state. + * + * @return the property backed bean state + * @throws IOException + * Signals that an I/O exception has occurred. + */ + protected abstract PropertyBackedBeanState createInitialState() throws IOException; + + /** + * Applies default overrides to the initial state. + * + * @param state + * the state + * @throws IOException + * Signals that an I/O exception has occurred. + */ + protected void applyDefaultOverrides(PropertyBackedBeanState state) throws IOException + { + for (String name : state.getPropertyNames()) + { + String override = resolveDefault(name); + if (override != null) + { + state.setProperty(name, override); + } + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getId() */ public List getId() { return this.id; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getCategory() + /** + * Gets the category. + * + * @return the category */ - public String getCategory() + protected String getCategory() { return this.category; } + /** + * Gets the hierarchical instance path within the category (second part of the ID). + * + * @return the instance path + */ + protected List getInstancePath() + { + return this.instancePath; + } + /* * (non-Javadoc) * @see org.springframework.beans.factory.DisposableBean#destroy() @@ -236,14 +329,22 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, destroy(false); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#destroy(boolean) + /** + * Releases any resources held by this component. + * + * @param isPermanent + * is the component being destroyed forever, i.e. should persisted values be removed? On server shutdown, + * this value would be false, whereas on the removal of a dynamically created instance, this + * value would be true. */ - public void destroy(boolean isPermanent) + protected synchronized void destroy(boolean isPermanent) { - stop(); - this.registry.deregister(this, isPermanent); + if (this.state != null) + { + stop(false); + this.registry.deregister(this, isPermanent); + this.state = null; + } } /* @@ -273,7 +374,113 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, { if (this.autoStart && event instanceof ContextRefreshedEvent && event.getSource() == this.parent) { - start(); + start(false); + } + else if (event instanceof PropertyBackedBeanStartedEvent) + { + synchronized (this) + { + if (!this.isStarted) + { + // Reinitialize so that we pick up state changes from the database + destroy(false); + start(false); + } + } + } + else if (event instanceof PropertyBackedBeanStoppedEvent) + { + // Completely destroy the state so that it will have to be reinitialized should the bean be put back in to + // use by this node + destroy(false); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#getProperty(java.lang.String) + */ + public synchronized String getProperty(String name) + { + init(); + return this.state.getProperty(name); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#getPropertyNames() + */ + public synchronized Set getPropertyNames() + { + init(); + return this.state.getPropertyNames(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#setProperty(java.lang.String, + * java.lang.String) + */ + public synchronized void setProperty(String name, String value) + { + init(); + this.state.setProperty(name, value); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#start() + */ + public synchronized void start() + { + start(true); + } + + /** + * Starts the bean, optionally broadcasting the event to remote nodes. + * + * @param broadcast + * Should the event be broadcast? + */ + protected synchronized void start(boolean broadcast) + { + if (!this.isStarted) + { + init(); + if (broadcast) + { + this.registry.broadcastStart(this); + } + this.state.start(); + this.isStarted = true; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#stop() + */ + public void stop() + { + stop(true); + } + + /** + * Stops the bean, optionally broadcasting the event to remote nodes. + * + * @param broadcast + * Should the event be broadcast? + */ + protected synchronized void stop(boolean broadcast) + { + if (this.isStarted) + { + if (broadcast) + { + this.registry.broadcastStop(this); + } + this.state.stop(); + this.isStarted = false; } } @@ -293,7 +500,7 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, } /** - * Expands the given value, resolving any ${} placeholders using the property defaults + * Expands the given value, resolving any ${} placeholders using the property defaults. * * @param val * the value to expand diff --git a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java index 371ea72874..33f0292b31 100644 --- a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java @@ -113,10 +113,10 @@ import org.springframework.core.io.support.ResourcePatternResolver; * *
  * <bean id="imap.server.mountPoints" class="org.springframework.beans.factory.config.ListFactoryBean">
- *    <property name="sourceList">
- *       <!-- Whatever you declare in here will get replaced by the property value list -->
- *       <!-- This property is not actually required at all -->
- *    </property>
+ * <property name="sourceList">
+ * <!-- Whatever you declare in here will get replaced by the property value list -->
+ * <!-- This property is not actually required at all -->
+ * </property>
  * </bean>
  * 
* @@ -148,21 +148,12 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i /** The logger. */ private static Log logger = LogFactory.getLog(ChildApplicationContextFactory.class); - /** The properties to be used in placeholder expansion. */ - private Properties properties; - - /** The child application context. */ - private ClassPathXmlApplicationContext applicationContext; - /** The type name. */ private String typeName; - /** The registered composite propertes and their types. */ + /** The registered composite properties and their types. */ private Map> compositePropertyTypes = Collections.emptyMap(); - /** The composite property values. */ - private Map> compositeProperties = new TreeMap>(); - /** * Default constructor for container construction. */ @@ -183,20 +174,21 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i * the category * @param typeName * the type name - * @param id - * the instance id + * @param instancePath + * the instance path within the category * @throws IOException * Signals that an I/O exception has occurred. */ public ChildApplicationContextFactory(ApplicationContext parent, PropertyBackedBeanRegistry registry, - Properties propertyDefaults, String category, String typeName, List id) throws IOException + Properties propertyDefaults, String category, String typeName, List instancePath) + throws IOException { setApplicationContext(parent); setRegistry(registry); setPropertyDefaults(propertyDefaults); setBeanName(category); setTypeName(typeName); - setId(id); + setInstancePath(instancePath); try { @@ -253,26 +245,42 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i @Override public void afterPropertiesSet() throws Exception { - List idList = getId(); + List idList = getInstancePath(); if (idList.isEmpty()) { - throw new IllegalStateException("Invalid ID"); + throw new IllegalStateException("Invalid instance path"); } if (getTypeName() == null) { setTypeName(idList.get(0)); } - // Load the property defaults - PropertiesFactoryBean factory = new PropertiesFactoryBean(); - factory.setLocations(getParent().getResources( - ChildApplicationContextFactory.CLASSPATH_PREFIX + getCategory() + '/' + getTypeName() - + ChildApplicationContextFactory.PROPERTIES_SUFFIX)); - factory.afterPropertiesSet(); - this.properties = (Properties) factory.getObject(); - - // Now let the superclass propagate default settings from the global properties and register us super.afterPropertiesSet(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#createInitialState() + */ + @Override + protected PropertyBackedBeanState createInitialState() throws IOException + { + return new ApplicationContextState(); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#applyDefaultOverrides(org.alfresco.repo.management + * .subsystems.PropertyBackedBeanState) + */ + @Override + protected void applyDefaultOverrides(PropertyBackedBeanState state) throws IOException + { + // Let the superclass propagate default settings from the global properties and register us + super.applyDefaultOverrides(state); + + List idList = getId(); // Apply any property overrides from the extension classpath and also allow system properties and JNDI to // override. We use the type name and last component of the ID in the path @@ -281,146 +289,9 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i overrideFactory.setLocations(getParent().getResources( ChildApplicationContextFactory.EXTENSION_CLASSPATH_PREFIX + getCategory() + '/' + getTypeName() + '/' + idList.get(idList.size() - 1) + '/' + ChildApplicationContextFactory.PROPERTIES_SUFFIX)); - overrideFactory.setProperties(this.properties); + overrideFactory.setProperties(((ApplicationContextState) state).properties); overrideFactory.afterPropertiesSet(); - this.properties = (Properties) overrideFactory.getObject(); - - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() - */ - @SuppressWarnings("unchecked") - public synchronized Set getPropertyNames() - { - Set result = new TreeSet(((Map) this.properties).keySet()); - result.add(ChildApplicationContextFactory.TYPE_NAME_PROPERTY); - result.addAll(this.compositePropertyTypes.keySet()); - return result; - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) - */ - public synchronized String getProperty(String name) - { - if (name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) - { - return getTypeName(); - } - else if (this.compositePropertyTypes.containsKey(name)) - { - Map beans = this.compositeProperties.get(name); - if (beans != null) - { - StringBuilder list = new StringBuilder(100); - for (String id : beans.keySet()) - { - if (list.length() > 0) - { - list.append(','); - } - list.append(id); - } - return list.toString(); - } - return ""; - } - else - { - return this.properties.getProperty(name); - } - - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, java.lang.String) - */ - public void setProperty(String name, String value) - { - if (name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) - { - throw new IllegalStateException("Illegal write to property \"" - + ChildApplicationContextFactory.TYPE_NAME_PROPERTY + "\""); - } - Class type = this.compositePropertyTypes.get(name); - if (type != null) - { - updateCompositeProperty(name, value, type); - } - else - { - this.properties.setProperty(name, value); - } - } - - /** - * Updates a composite property with a new list of instance names. Properties of those instances that existed - * previously will be preserved. Instances that no longer exist will be destroyed. New instances will be brought - * into life with default values, as described in the class description. - * - * @param name - * the composite property name - * @param value - * a list of bean instance IDs - * @param type - * the bean class - */ - private void updateCompositeProperty(String name, String value, Class type) - { - // Retrieve the map of existing values of this property - Map propertyValues = this.compositeProperties.get(name); - if (propertyValues == null) - { - propertyValues = Collections.emptyMap(); - } - - try - { - Map newPropertyValues = new LinkedHashMap(11); - StringTokenizer tkn = new StringTokenizer(value, ", \t\n\r\f"); - while (tkn.hasMoreTokens()) - { - String id = tkn.nextToken(); - - // Generate a unique ID within the category - List childId = new ArrayList(3); - childId.addAll(getId()); - childId.add(name); - childId.add(id); - - // Look out for new or updated children - CompositeDataBean child = propertyValues.get(id); - - if (child == null) - { - child = new CompositeDataBean(getParent(), this, getRegistry(), getPropertyDefaults(), - getCategory(), type, childId); - } - newPropertyValues.put(id, child); - } - - // Destroy any children that have been removed - Set idsToRemove = new TreeSet(propertyValues.keySet()); - idsToRemove.removeAll(newPropertyValues.keySet()); - for (String id : idsToRemove) - { - CompositeDataBean child = propertyValues.get(id); - child.destroy(true); - } - this.compositeProperties.put(name, newPropertyValues); - } - catch (RuntimeException e) - { - throw e; - } - catch (Exception e) - { - throw new RuntimeException(e); - } + ((ApplicationContextState) state).properties = (Properties) overrideFactory.getObject(); } /* @@ -446,63 +317,22 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i .getDescription(name); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() - */ - public synchronized void start() - { - // This is where we actually create and start a child application context based on the configured properties. - if (this.applicationContext == null) - { - ChildApplicationContextFactory.logger.info("Starting '" + getCategory() + "' subsystem, ID: " + getId()); - this.applicationContext = new ChildApplicationContext(); - this.applicationContext.refresh(); - ChildApplicationContextFactory.logger.info("Startup of '" + getCategory() + "' subsystem, ID: " + getId() - + " complete"); - } - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() - */ - public void stop() - { - if (this.applicationContext != null) - { - ChildApplicationContextFactory.logger.info("Stopping '" + getCategory() + "' subsystem, ID: " + getId()); - try - { - this.applicationContext.close(); - } - catch (Exception e) - { - ChildApplicationContextFactory.logger.error(e); - // Continue anyway. Perhaps it didn't start properly - } - this.applicationContext = null; - ChildApplicationContextFactory.logger.info("Stopped '" + getCategory() + "' subsystem, ID: " + getId()); - } - } - /* * (non-Javadoc) * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#destroy(boolean) */ @Override - public void destroy(boolean permanent) + protected synchronized void destroy(boolean permanent) { - super.destroy(permanent); + ApplicationContextState state = (ApplicationContextState) getState(false); // Cascade the destroy / shutdown - for (Map beans : this.compositeProperties.values()) + if (state != null) { - for (CompositeDataBean bean : beans.values()) - { - bean.destroy(permanent); - } + state.destroy(permanent); } + + super.destroy(permanent); } /* @@ -511,8 +341,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i */ public synchronized ApplicationContext getApplicationContext() { - start(); - return this.applicationContext; + return ((ApplicationContextState) getState(true)).getApplicationContext(); } /** @@ -524,13 +353,21 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i private class ChildApplicationContext extends ClassPathXmlApplicationContext { + /** The composite property values. */ + private Map> compositeProperties; + /** * The Constructor. * + * @param properties + * the properties + * @param compositeProperties + * the composite properties * @throws BeansException * the beans exception */ - private ChildApplicationContext() throws BeansException + private ChildApplicationContext(Properties properties, + Map> compositeProperties) throws BeansException { super(new String[] { @@ -540,9 +377,11 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i + getId().get(getId().size() - 1) + '/' + ChildApplicationContextFactory.CONTEXT_SUFFIX }, false, ChildApplicationContextFactory.this.getParent()); + this.compositeProperties = compositeProperties; + // Add a property placeholder configurer, with the subsystem-scoped default properties PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); - configurer.setProperties(ChildApplicationContextFactory.this.properties); + configurer.setProperties(properties); configurer.setIgnoreUnresolvablePlaceholders(true); addBeanFactoryPostProcessor(configurer); @@ -589,7 +428,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i if (bean instanceof ListFactoryBean && ChildApplicationContextFactory.this.compositePropertyTypes.containsKey(beanName)) { - Map beans = ChildApplicationContextFactory.this.compositeProperties + Map beans = ChildApplicationContext.this.compositeProperties .get(beanName); List beanList; if (beans != null) @@ -611,4 +450,249 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i }); } } + + /** + * The Class ApplicationContextState. + */ + protected class ApplicationContextState implements PropertyBackedBeanState + { + + /** The properties to be used in placeholder expansion. */ + private Properties properties; + + /** The composite property values. */ + private Map> compositeProperties = new TreeMap>(); + + /** The child application context. */ + private ClassPathXmlApplicationContext applicationContext; + + /** + * Instantiates a new application context state. + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + protected ApplicationContextState() throws IOException + { + // Load the property defaults + PropertiesFactoryBean factory = new PropertiesFactoryBean(); + factory.setLocations(getParent().getResources( + ChildApplicationContextFactory.CLASSPATH_PREFIX + getCategory() + '/' + getTypeName() + + ChildApplicationContextFactory.PROPERTIES_SUFFIX)); + factory.afterPropertiesSet(); + this.properties = (Properties) factory.getObject(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() + */ + @SuppressWarnings("unchecked") + public synchronized Set getPropertyNames() + { + Set result = new TreeSet(((Map) this.properties).keySet()); + result.add(ChildApplicationContextFactory.TYPE_NAME_PROPERTY); + result.addAll(ChildApplicationContextFactory.this.compositePropertyTypes.keySet()); + return result; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) + */ + public synchronized String getProperty(String name) + { + if (name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) + { + return getTypeName(); + } + else if (ChildApplicationContextFactory.this.compositePropertyTypes.containsKey(name)) + { + Map beans = this.compositeProperties.get(name); + if (beans != null) + { + StringBuilder list = new StringBuilder(100); + for (String id : beans.keySet()) + { + if (list.length() > 0) + { + list.append(','); + } + list.append(id); + } + return list.toString(); + } + return ""; + } + else + { + return this.properties.getProperty(name); + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, + * java.lang.String) + */ + public void setProperty(String name, String value) + { + if (name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) + { + throw new IllegalStateException("Illegal write to property \"" + + ChildApplicationContextFactory.TYPE_NAME_PROPERTY + "\""); + } + Class type = ChildApplicationContextFactory.this.compositePropertyTypes.get(name); + if (type != null) + { + updateCompositeProperty(name, value, type); + } + else + { + this.properties.setProperty(name, value); + } + } + + /** + * Updates a composite property with a new list of instance names. Properties of those instances that existed + * previously will be preserved. Instances that no longer exist will be destroyed. New instances will be brought + * into life with default values, as described in the class description. + * + * @param name + * the composite property name + * @param value + * a list of bean instance IDs + * @param type + * the bean class + */ + private void updateCompositeProperty(String name, String value, Class type) + { + // Retrieve the map of existing values of this property + Map propertyValues = this.compositeProperties.get(name); + if (propertyValues == null) + { + propertyValues = Collections.emptyMap(); + } + + try + { + Map newPropertyValues = new LinkedHashMap(11); + StringTokenizer tkn = new StringTokenizer(value, ", \t\n\r\f"); + while (tkn.hasMoreTokens()) + { + String id = tkn.nextToken(); + + // Generate a unique ID within the category + List childPath = new ArrayList(4); + childPath.addAll(getInstancePath()); + childPath.add(name); + childPath.add(id); + + // Look out for new or updated children + CompositeDataBean child = propertyValues.get(id); + + if (child == null) + { + child = new CompositeDataBean(getParent(), ChildApplicationContextFactory.this, getRegistry(), + getPropertyDefaults(), getCategory(), type, childPath); + } + newPropertyValues.put(id, child); + } + + // Destroy any children that have been removed + Set idsToRemove = new TreeSet(propertyValues.keySet()); + idsToRemove.removeAll(newPropertyValues.keySet()); + for (String id : idsToRemove) + { + CompositeDataBean child = propertyValues.get(id); + child.destroy(true); + } + this.compositeProperties.put(name, newPropertyValues); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() + */ + public synchronized void start() + { + // This is where we actually create and start a child application context based on the configured + // properties. + if (this.applicationContext == null) + { + ChildApplicationContextFactory.logger + .info("Starting '" + getCategory() + "' subsystem, ID: " + getId()); + this.applicationContext = ChildApplicationContextFactory.this.new ChildApplicationContext( + this.properties, this.compositeProperties); + this.applicationContext.refresh(); + ChildApplicationContextFactory.logger.info("Startup of '" + getCategory() + "' subsystem, ID: " + + getId() + " complete"); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() + */ + public void stop() + { + if (this.applicationContext != null) + { + ChildApplicationContextFactory.logger + .info("Stopping '" + getCategory() + "' subsystem, ID: " + getId()); + try + { + this.applicationContext.close(); + } + catch (Exception e) + { + ChildApplicationContextFactory.logger.error(e); + // Continue anyway. Perhaps it didn't start properly + } + this.applicationContext = null; + ChildApplicationContextFactory.logger.info("Stopped '" + getCategory() + "' subsystem, ID: " + getId()); + } + } + + /** + * Releases any resources held by this state. + * + * @param permanent + * is the state being destroyed forever, i.e. should persisted values be removed? On server shutdown, + * this value would be false, whereas on the removal of a dynamically created instance, + * this value would be true. + */ + public void destroy(boolean permanent) + { + // Cascade the destroy / shutdown + for (Map beans : this.compositeProperties.values()) + { + for (CompositeDataBean bean : beans.values()) + { + bean.destroy(permanent); + } + } + } + + /** + * Gets the application context. + * + * @return the application context + */ + public synchronized ApplicationContext getApplicationContext() + { + start(); + return this.applicationContext; + } + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java b/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java index 4d7d8ed2a7..b1f1c498b3 100644 --- a/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java @@ -32,6 +32,7 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.BeanNameAware; @@ -47,14 +48,12 @@ import org.springframework.context.ApplicationContext; */ public class CompositeDataBean extends AbstractPropertyBackedBean { - /** The owning bean */ + + /** The owning bean. */ private final PropertyBackedBean owner; - /** The Java bean instance. */ - private final Object bean; - - /** A Spring wrapper around the Java bean, allowing easy configuration of properties. */ - private final BeanWrapper wrappedBean; + /** The bean type. */ + private final Class type; /** The property names. */ private final Set propertyNames; @@ -62,10 +61,10 @@ public class CompositeDataBean extends AbstractPropertyBackedBean /** The writeable properties. */ private final Set writeableProperties; - /** The prefix used to look up default values for this bean's properties */ + /** The prefix used to look up default values for this bean's properties. */ private String defaultKeyPrefix; - /** The prefix used to look up instance-specific default values for this bean's properties */ + /** The prefix used to look up instance-specific default values for this bean's properties. */ private String instanceKeyPrefix; /** @@ -79,8 +78,8 @@ public class CompositeDataBean extends AbstractPropertyBackedBean * property defaults provided by the installer or System properties * @param category * the category - * @param id - * the instance id + * @param instancePath + * the instance path within the category * @param owner * the owning bean * @param type @@ -89,25 +88,19 @@ public class CompositeDataBean extends AbstractPropertyBackedBean * Signals that an I/O exception has occurred. */ public CompositeDataBean(ApplicationContext parent, PropertyBackedBean owner, PropertyBackedBeanRegistry registry, - Properties propertyDefaults, String category, Class type, List id) throws IOException + Properties propertyDefaults, String category, Class type, List instancePath) throws IOException { setApplicationContext(parent); setRegistry(registry); setPropertyDefaults(propertyDefaults); setBeanName(category); - setId(id); + setInstancePath(instancePath); this.owner = owner; + this.type = type; try { - this.bean = type.newInstance(); - // Tell the bean its name if it cares - if (this.bean instanceof BeanNameAware) - { - ((BeanNameAware) this.bean).setBeanName(id.get(id.size() - 1)); - } - this.wrappedBean = new BeanWrapperImpl(this.bean); - PropertyDescriptor[] descriptors = this.wrappedBean.getPropertyDescriptors(); + PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(type); this.propertyNames = new TreeSet(); this.writeableProperties = new TreeSet(); for (PropertyDescriptor descriptor : descriptors) @@ -149,7 +142,7 @@ public class CompositeDataBean extends AbstractPropertyBackedBean // Derive a default and instance key prefix of the form ".default." and ".value.." StringBuilder defaultKeyPrefixBuff = new StringBuilder(200); StringBuilder instanceKeyPrefixBuff = new StringBuilder(200); - List id = getId(); + List id = getInstancePath(); int size = id.size(); if (size > 1) { @@ -181,30 +174,12 @@ public class CompositeDataBean extends AbstractPropertyBackedBean /* * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#createInitialState() */ - public String getProperty(String name) + @Override + protected PropertyBackedBeanState createInitialState() throws IOException { - Object value = this.wrappedBean.getPropertyValue(name); - return value == null ? null : value.toString(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() - */ - public Set getPropertyNames() - { - return this.propertyNames; - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, java.lang.String) - */ - public void setProperty(String name, String value) - { - this.wrappedBean.setPropertyValue(name, value); + return new CompositeDataBeanState(); } /* @@ -217,24 +192,6 @@ public class CompositeDataBean extends AbstractPropertyBackedBean return this.writeableProperties.contains(name); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() - */ - public void start() - { - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() - */ - public void stop() - { - // Ensure any edits to child composites cause the parent to be shut down and subsequently re-initialized - this.owner.stop(); - } - /** * Gets the wrapped Java bean. * @@ -242,6 +199,115 @@ public class CompositeDataBean extends AbstractPropertyBackedBean */ protected Object getBean() { - return this.bean; + return ((CompositeDataBeanState) getState(true)).getBean(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#stop(boolean) + */ + @Override + protected synchronized void stop(boolean broadcast) + { + super.stop(broadcast); + + // Ensure any edits to child composites cause the parent to be shut down and subsequently re-initialized + if (broadcast) + { + this.owner.stop(); + } + } + + /** + * The Class CompositeDataBeanState. + */ + protected class CompositeDataBeanState implements PropertyBackedBeanState + { + + /** The Java bean instance. */ + private final Object bean; + + /** A Spring wrapper around the Java bean, allowing easy configuration of properties. */ + private final BeanWrapper wrappedBean; + + /** + * Instantiates a new composite data bean state. + */ + protected CompositeDataBeanState() + { + try + { + this.bean = CompositeDataBean.this.type.newInstance(); + // Tell the bean its name if it cares + if (this.bean instanceof BeanNameAware) + { + ((BeanNameAware) this.bean).setBeanName(getId().get(getId().size() - 1)); + } + this.wrappedBean = new BeanWrapperImpl(this.bean); + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) + */ + public String getProperty(String name) + { + Object value = this.wrappedBean.getPropertyValue(name); + return value == null ? null : value.toString(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() + */ + public Set getPropertyNames() + { + return CompositeDataBean.this.propertyNames; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, + * java.lang.String) + */ + public void setProperty(String name, String value) + { + this.wrappedBean.setPropertyValue(name, value); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() + */ + public void start() + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() + */ + public void stop() + { + } + + /** + * Gets the wrapped Java bean. + * + * @return the Java bean + */ + protected Object getBean() + { + return this.bean; + } } } diff --git a/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java b/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java index b6cc366a46..8cb0fee312 100644 --- a/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java +++ b/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.management.subsystems; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -66,18 +67,12 @@ public class DefaultChildApplicationContextManager extends AbstractPropertyBacke /** The default chain. */ private String defaultChain; - /** The instance ids. */ - private List instanceIds = new ArrayList(10); - - /** The child application contexts. */ - private Map childApplicationContexts = new TreeMap(); - /** * Instantiates a new default child application context manager. */ public DefaultChildApplicationContextManager() { - setId(Collections.singletonList("manager")); + setInstancePath(Collections.singletonList("manager")); } /** @@ -106,94 +101,6 @@ public class DefaultChildApplicationContextManager extends AbstractPropertyBacke this.defaultChain = defaultChain; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#afterPropertiesSet() - */ - @Override - public void afterPropertiesSet() throws Exception - { - if (this.defaultChain != null && this.defaultChain.length() > 0) - { - // Use the first type as the default, unless one is specified explicitly - if (this.defaultTypeName == null) - { - updateOrder(this.defaultChain, AbstractPropertyBackedBean.DEFAULT_ID_ROOT); - this.defaultTypeName = this.childApplicationContexts.get(this.instanceIds.get(0)).getTypeName(); - } - else - { - updateOrder(this.defaultChain, this.defaultTypeName); - } - } - else if (this.defaultTypeName == null) - { - setDefaultTypeName(AbstractPropertyBackedBean.DEFAULT_ID_ROOT); - } - - super.afterPropertiesSet(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() - */ - public void start() - { - for (String instance : getInstanceIds()) - { - getApplicationContext(instance); - } - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() - */ - public void stop() - { - // Nothing to do - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#destroy(boolean) - */ - @Override - public void destroy(boolean permanent) - { - super.destroy(permanent); - - // Cascade the destroy / shutdown - for (String id : this.instanceIds) - { - ChildApplicationContextFactory factory = this.childApplicationContexts.get(id); - factory.destroy(permanent); - } - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) - */ - public synchronized String getProperty(String name) - { - if (!name.equals(DefaultChildApplicationContextManager.ORDER_PROPERTY)) - { - return null; - } - return getOrderString(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() - */ - public Set getPropertyNames() - { - return Collections.singleton(DefaultChildApplicationContextManager.ORDER_PROPERTY); - } - /* * (non-Javadoc) * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#getDescription(java.lang.String) @@ -206,115 +113,281 @@ public class DefaultChildApplicationContextManager extends AbstractPropertyBacke /* * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, java.lang.String) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#destroy(boolean) */ - public synchronized void setProperty(String name, String value) + @Override + public void destroy(boolean permanent) { - if (!name.equals(DefaultChildApplicationContextManager.ORDER_PROPERTY)) + ApplicationContextManagerState state = (ApplicationContextManagerState) getState(false); + + if (state != null) { - throw new IllegalStateException("Illegal attempt to write to property \"" + name + "\""); + // Cascade the destroy / shutdown + for (String id : state.getInstanceIds()) + { + ChildApplicationContextFactory factory = state.getApplicationContextFactory(id); + factory.destroy(permanent); + } } - updateOrder(value, this.defaultTypeName); + + super.destroy(permanent); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#createInitialState() + */ + @Override + protected PropertyBackedBeanState createInitialState() throws IOException + { + return new ApplicationContextManagerState(this.defaultChain, this.defaultTypeName); } /* * (non-Javadoc) * @see org.alfresco.repo.management.ChildApplicationContextManager#getInstanceIds() */ - public synchronized Collection getInstanceIds() + public Collection getInstanceIds() { - return Collections.unmodifiableList(this.instanceIds); + return ((ApplicationContextManagerState) getState(true)).getInstanceIds(); } /* * (non-Javadoc) * @see org.alfresco.repo.management.ChildApplicationContextManager#getApplicationContext(java.lang.String) */ - public synchronized ApplicationContext getApplicationContext(String id) + public ApplicationContext getApplicationContext(String id) { - ChildApplicationContextFactory child = this.childApplicationContexts.get(id); - return child == null ? null : child.getApplicationContext(); + return ((ApplicationContextManagerState) getState(true)).getApplicationContext(id); } /** - * Gets the order string. - * - * @return the order string + * The Class ApplicationContextManagerState. */ - private String getOrderString() + protected class ApplicationContextManagerState implements PropertyBackedBeanState { - StringBuilder orderString = new StringBuilder(100); - for (String id : this.instanceIds) + + /** The instance ids. */ + private List instanceIds = new ArrayList(10); + + /** The child application contexts. */ + private Map childApplicationContexts = new TreeMap(); + + /** The default type name. */ + private String defaultTypeName; + + /** + * Instantiates a new application context manager state. + * + * @param defaultChain + * the default chain + * @param defaultTypeName + * the default type name + */ + protected ApplicationContextManagerState(String defaultChain, String defaultTypeName) { - if (orderString.length() > 0) + // Work out what the default type name should be; either specified explicitly or implied by the first member + // of the default chain + if (defaultChain != null && defaultChain.length() > 0) { - orderString.append(","); - } - orderString.append(id).append(':').append(this.childApplicationContexts.get(id).getTypeName()); - } - return orderString.toString(); - } - - /** - * Updates the order from a comma or whitespace separated string. - * - * @param orderString - * the order as a comma or whitespace separated string - * @param defaultTypeName - * the default type name - */ - private void updateOrder(String orderString, String defaultTypeName) - { - try - { - StringTokenizer tkn = new StringTokenizer(orderString, ", \t\n\r\f"); - List newInstanceIds = new ArrayList(tkn.countTokens()); - while (tkn.hasMoreTokens()) - { - String instance = tkn.nextToken(); - int sepIndex = instance.indexOf(':'); - String id = sepIndex == -1 ? instance : instance.substring(0, sepIndex); - String typeName = sepIndex == -1 || sepIndex + 1 >= instance.length() ? defaultTypeName : instance - .substring(sepIndex + 1); - newInstanceIds.add(id); - - // Look out for new or updated children - ChildApplicationContextFactory factory = this.childApplicationContexts.get(id); - - // If we have the same instance ID but a different type, treat that as a destroy and remove - if (factory != null && !factory.getTypeName().equals(typeName)) + // Use the first type as the default, unless one is specified explicitly + if (defaultTypeName == null) { + updateOrder(defaultChain, AbstractPropertyBackedBean.DEFAULT_INSTANCE_NAME); + this.defaultTypeName = this.childApplicationContexts.get(this.instanceIds.get(0)).getTypeName(); + } + else + { + this.defaultTypeName = defaultTypeName; + updateOrder(defaultChain, defaultTypeName); + } + } + else if (defaultTypeName == null) + { + this.defaultTypeName = AbstractPropertyBackedBean.DEFAULT_INSTANCE_NAME; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) + */ + public synchronized String getProperty(String name) + { + if (!name.equals(DefaultChildApplicationContextManager.ORDER_PROPERTY)) + { + return null; + } + return getOrderString(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() + */ + public Set getPropertyNames() + { + return Collections.singleton(DefaultChildApplicationContextManager.ORDER_PROPERTY); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, + * java.lang.String) + */ + public synchronized void setProperty(String name, String value) + { + if (!name.equals(DefaultChildApplicationContextManager.ORDER_PROPERTY)) + { + throw new IllegalStateException("Illegal attempt to write to property \"" + name + "\""); + } + updateOrder(value, this.defaultTypeName); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() + */ + public void start() + { + for (String instance : getInstanceIds()) + { + getApplicationContext(instance); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#stop() + */ + public void stop() + { + // Nothing to do + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.ChildApplicationContextManager#getInstanceIds() + */ + /** + * Gets the instance ids. + * + * @return the instance ids + */ + public synchronized Collection getInstanceIds() + { + return Collections.unmodifiableList(this.instanceIds); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.ChildApplicationContextManager#getApplicationContext(java.lang.String) + */ + /** + * Gets the application context. + * + * @param id + * the id + * @return the application context + */ + public synchronized ApplicationContext getApplicationContext(String id) + { + ChildApplicationContextFactory child = this.childApplicationContexts.get(id); + return child == null ? null : child.getApplicationContext(); + } + + /** + * Gets the application context factory. + * + * @param id + * the id + * @return the application context factory + */ + protected ChildApplicationContextFactory getApplicationContextFactory(String id) + { + return this.childApplicationContexts.get(id); + } + + /** + * Gets the order string. + * + * @return the order string + */ + private String getOrderString() + { + StringBuilder orderString = new StringBuilder(100); + for (String id : this.instanceIds) + { + if (orderString.length() > 0) + { + orderString.append(","); + } + orderString.append(id).append(':').append(this.childApplicationContexts.get(id).getTypeName()); + } + return orderString.toString(); + } + + /** + * Updates the order from a comma or whitespace separated string. + * + * @param orderString + * the order as a comma or whitespace separated string + * @param defaultTypeName + * the default type name + */ + private void updateOrder(String orderString, String defaultTypeName) + { + try + { + StringTokenizer tkn = new StringTokenizer(orderString, ", \t\n\r\f"); + List newInstanceIds = new ArrayList(tkn.countTokens()); + while (tkn.hasMoreTokens()) + { + String instance = tkn.nextToken(); + int sepIndex = instance.indexOf(':'); + String id = sepIndex == -1 ? instance : instance.substring(0, sepIndex); + String typeName = sepIndex == -1 || sepIndex + 1 >= instance.length() ? defaultTypeName : instance + .substring(sepIndex + 1); + newInstanceIds.add(id); + + // Look out for new or updated children + ChildApplicationContextFactory factory = this.childApplicationContexts.get(id); + + // If we have the same instance ID but a different type, treat that as a destroy and remove + if (factory != null && !factory.getTypeName().equals(typeName)) + { + factory.destroy(true); + factory = null; + } + if (factory == null) + { + // Generate a unique ID within the category + List childId = new ArrayList(2); + childId.add("managed"); + childId.add(id); + this.childApplicationContexts.put(id, new ChildApplicationContextFactory(getParent(), + getRegistry(), getPropertyDefaults(), getCategory(), typeName, childId)); + } + } + + // Destroy any children that have been removed + Set idsToRemove = new TreeSet(this.childApplicationContexts.keySet()); + idsToRemove.removeAll(newInstanceIds); + for (String id : idsToRemove) + { + ChildApplicationContextFactory factory = this.childApplicationContexts.remove(id); factory.destroy(true); - factory = null; - } - if (factory == null) - { - // Generate a unique ID within the category - List childId = new ArrayList(2); - childId.add("managed"); - childId.add(id); - this.childApplicationContexts.put(id, new ChildApplicationContextFactory(getParent(), - getRegistry(), getPropertyDefaults(), getCategory(), typeName, childId)); } + this.instanceIds = newInstanceIds; } - - // Destroy any children that have been removed - Set idsToRemove = new TreeSet(this.childApplicationContexts.keySet()); - idsToRemove.removeAll(newInstanceIds); - for (String id : idsToRemove) + catch (RuntimeException e) { - ChildApplicationContextFactory factory = this.childApplicationContexts.remove(id); - factory.destroy(true); + throw e; + } + catch (Exception e) + { + throw new RuntimeException(e); } - this.instanceIds = newInstanceIds; - } - catch (RuntimeException e) - { - throw e; - } - catch (Exception e) - { - throw new RuntimeException(e); } + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java b/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java index 008f512281..981ed691c6 100644 --- a/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java +++ b/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java @@ -82,6 +82,28 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi broadcastEvent(new PropertyBackedBeanUnregisteredEvent(bean, isPermanent)); } + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.management.subsystems.PropertyBackedBeanRegistry#broadcastStart(org.alfresco.repo.management + * .subsystems.PropertyBackedBean) + */ + public void broadcastStart(PropertyBackedBean bean) + { + broadcastEvent(new PropertyBackedBeanStartedEvent(bean)); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.management.subsystems.PropertyBackedBeanRegistry#broadcastStop(org.alfresco.repo.management + * .subsystems.PropertyBackedBean) + */ + public void broadcastStop(PropertyBackedBean bean) + { + broadcastEvent(new PropertyBackedBeanStoppedEvent(bean)); + } + /** * Broadcast event. * diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java index f384bad950..c2f1730a82 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java @@ -25,64 +25,25 @@ package org.alfresco.repo.management.subsystems; import java.util.List; -import java.util.Set; /** * A PropertyBackedBean is a reconfigurable sub-component or subsystem in the Alfresco server. It exposes - * configurable properties, along with {@link #stop()}, {@link #start()} and {@link #destroy(boolean)} methods. To - * reconfigure a bean, first ensure it is stopped by calling {@link #stop()}. Then set one or more properties. Then test - * out the changes with {@link #start()}. To bring the bean instance out of play (e.g. on server shutdown) call - * {@link #destroy(boolean)}. In the Alfresco enterprise edition PropertyBackedBeans are exposed as - * persistent MBeans and can be reconfigured at runtime via JMX. + * its state through the {@link PropertyBackedBeanState} interface, along with fixed attributes and a method for + * reverting the bean to its default initial state. In the Alfresco enterprise edition PropertyBackedBeans + * are exposed as persistent MBeans and can be reconfigured across a cluster at runtime via JMX. * * @author dward */ -public interface PropertyBackedBean +public interface PropertyBackedBean extends PropertyBackedBeanState { - /** - * Gets a human readable categorization of this bean, explaining its purpose. This category may be used e.g. in - * administration UIs and JMX object names. - * - * @return the category - */ - public String getCategory(); - - /** - * Gets an identifier for the bean. Must be unique within the category. The ID is a List to encourage hierarchical - * structuring of IDs, e.g. to aid construction of JMX Object names and presentation in JConsole. + * Gets a unique identifier for the bean. The ID is a List to encourage hierarchical structuring of IDs, e.g. to aid + * construction of JMX Object names and presentation in JConsole. * * @return the id */ public List getId(); - /** - * Gets the names of all properties. - * - * @return the property names - */ - public Set getPropertyNames(); - - /** - * Gets a property value. - * - * @param name - * the name - * @return the property value - */ - public String getProperty(String name); - - /** - * Sets the value of a property. This may only be called after {@link #stop()} and should only be called for - * property names for which the {@link #isUpdateable(String)} method returns true. - * - * @param name - * the property name - * @param value - * the property value - */ - public void setProperty(String name, String value); - /** * Checks if a property is updateable. * @@ -102,22 +63,7 @@ public interface PropertyBackedBean public String getDescription(String name); /** - * Starts up the component, using its new property values. + * Reverts this component to its original default start state, removing any previously persisted state changes. */ - public void start(); - - /** - * Stops the component, so that its property values can be changed. - */ - public void stop(); - - /** - * Releases any resources held by this component. - * - * @param isPermanent - * is the component being destroyed forever, i.e. should persisted values be removed? On server shutdown, - * this value would be false, whereas on the removal of a dynamically created instance, this - * value would be true. - */ - public void destroy(boolean isPermanent); + public void revert(); } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanEvent.java index a723ec80ef..976ba995bc 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanEvent.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanEvent.java @@ -24,6 +24,8 @@ */ package org.alfresco.repo.management.subsystems; +import java.util.List; + import org.springframework.context.ApplicationEvent; /** @@ -33,8 +35,10 @@ import org.springframework.context.ApplicationEvent; */ public abstract class PropertyBackedBeanEvent extends ApplicationEvent { + private static final long serialVersionUID = -5414152423990988923L; - private static final long serialVersionUID = 1848914557290327762L; + /** The ID of the bean that emitted the event. */ + private List sourceId; /** * The Constructor. @@ -45,16 +49,16 @@ public abstract class PropertyBackedBeanEvent extends ApplicationEvent public PropertyBackedBeanEvent(PropertyBackedBean source) { super(source); + this.sourceId = source.getId(); } /** - * Gets the bean that emitted the event. + * Gets the ID of the bean that emitted the event. * - * @return the bean + * @return the ID */ - public PropertyBackedBean getBean() + public List getSourceId() { - return (PropertyBackedBean) getSource(); + return this.sourceId; } - } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegisteredEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegisteredEvent.java index af452b5501..65133c36dd 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegisteredEvent.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegisteredEvent.java @@ -31,7 +31,7 @@ package org.alfresco.repo.management.subsystems; */ public class PropertyBackedBeanRegisteredEvent extends PropertyBackedBeanEvent { - private static final long serialVersionUID = -5922059120018335685L; + private static final long serialVersionUID = -2860105961131524745L; /** * The Constructor. @@ -42,6 +42,5 @@ public class PropertyBackedBeanRegisteredEvent extends PropertyBackedBeanEvent public PropertyBackedBeanRegisteredEvent(PropertyBackedBean source) { super(source); - } - + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java index bd1b7fbbf6..2946687d9d 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java @@ -64,4 +64,21 @@ public interface PropertyBackedBeanRegistry * value would be true. */ public void deregister(PropertyBackedBean bean, boolean isPermanent); + + /** + * Signals that a {@link PropertyBackedBean} has been started. + * + * @param bean + * the bean + */ + public void broadcastStart(PropertyBackedBean bean); + + + /** + * Signals that a {@link PropertyBackedBean} has been stopped. + * + * @param bean + * the bean + */ + public void broadcastStop(PropertyBackedBean bean); } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStartedEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStartedEvent.java new file mode 100644 index 0000000000..e0cd5e37f1 --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStartedEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.management.subsystems; + +import java.util.List; + + +/** + * An event emitted after a {@link PropertyBackedBean} is started. + * + * @author dward + */ +public class PropertyBackedBeanStartedEvent extends PropertyBackedBeanEvent +{ + private static final long serialVersionUID = 6019157155489029474L; + + /** + * The Constructor. + * + * @param source + * the source of the event + */ + public PropertyBackedBeanStartedEvent(PropertyBackedBean source) + { + super(source); + } +} diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java new file mode 100644 index 0000000000..826cf680a4 --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.management.subsystems; + +import java.util.Set; + +/** + * A PropertyBackedBeanState represents the state of a configurable sub-component or subsystem in the + * Alfresco server. It exposes configurable properties, along with {@link #stop()} and {@link #start()} methods. To + * modify the state, first ensure its associated component is stopped by calling {@link #stop()}. Then set one or more + * properties. Then test out the changes with {@link #start()}. In the Alfresco enterprise edition + * PropertyBackedBeanStates are exposed as persistent MBeans and can be reconfigured at runtime across a + * cluster via JMX. + * + * @author dward + */ +public interface PropertyBackedBeanState +{ + /** + * Gets the names of all properties. + * + * @return the property names + */ + public Set getPropertyNames(); + + /** + * Gets a property value. + * + * @param name + * the name + * @return the property value + */ + public String getProperty(String name); + + /** + * Sets the value of a property. This may only be called after {@link #stop()} and should only be called for + * property names for which the {@link #isUpdateable(String)} method returns true. + * + * @param name + * the property name + * @param value + * the property value + */ + public void setProperty(String name, String value); + + /** + * Starts up the component, using its new property values. + */ + public void start(); + + /** + * Stops the component, so that its property values can be changed. + */ + public void stop(); +} diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStoppedEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStoppedEvent.java new file mode 100644 index 0000000000..926ed80596 --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanStoppedEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.management.subsystems; + +import java.util.List; + + +/** + * An event emitted before a {@link PropertyBackedBean} is stopped. + * + * @author dward + */ +public class PropertyBackedBeanStoppedEvent extends PropertyBackedBeanEvent +{ + private static final long serialVersionUID = -8096989839647678810L; + + /** + * The Constructor. + * + * @param source + * the source of the event + */ + public PropertyBackedBeanStoppedEvent(PropertyBackedBean source) + { + super(source); + } +} diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanUnregisteredEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanUnregisteredEvent.java index c9673e311c..2c56aa8784 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanUnregisteredEvent.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanUnregisteredEvent.java @@ -25,13 +25,13 @@ package org.alfresco.repo.management.subsystems; /** - * An event emitted after {@link PropertyBackedBean#destroy(boolean)} is called on a bean. + * An event emitted a {@link PropertyBackedBean} is destroyed. * * @author dward */ public class PropertyBackedBeanUnregisteredEvent extends PropertyBackedBeanEvent { - private static final long serialVersionUID = -7878510109531750057L; + private static final long serialVersionUID = 4154847737689541132L; private final boolean isPermanent; diff --git a/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java index 1102338762..e517c7f71a 100644 --- a/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.management.subsystems; +import java.io.IOException; import java.util.Collections; import java.util.Set; @@ -37,121 +38,162 @@ import org.springframework.context.ApplicationContext; public class SwitchableApplicationContextFactory extends AbstractPropertyBackedBean implements ApplicationContextFactory { - /** - * - */ + + /** The name of the property holding the bean name of the source {@link ApplicationContextFactory}. */ private static final String SOURCE_BEAN_PROPERTY = "sourceBeanName"; - /** The bean name of the source {@link ApplicationContextFactory}. */ + /** The default bean name of the source {@link ApplicationContextFactory}. */ private String sourceBeanName; - /** The current source application context factory. */ - private ApplicationContextFactory sourceApplicationContextFactory; - /** - * Sets the bean name of the source {@link ApplicationContextFactory}. + * Sets the default bean name of the source {@link ApplicationContextFactory}. * * @param sourceBeanName * the bean name - * @throws Exception - * on error */ - public synchronized void setSourceBeanName(String sourceBeanName) + public void setSourceBeanName(String sourceBeanName) { - if (this.sourceApplicationContextFactory != null) - { - stop(); - this.sourceBeanName = sourceBeanName; - start(); - } - else + this.sourceBeanName = sourceBeanName; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.ApplicationContextFactory#getApplicationContext() + */ + public ApplicationContext getApplicationContext() + { + return ((SwitchableState) getState(true)).getApplicationContext(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.AbstractPropertyBackedBean#createInitialState() + */ + @Override + protected PropertyBackedBeanState createInitialState() throws IOException + { + return new SwitchableState(this.sourceBeanName); + } + + /** + * Represents the state of a {@link SwitchableApplicationContextFactory}. + */ + protected class SwitchableState implements PropertyBackedBeanState + { + + /** The current source application context factory. */ + private ApplicationContextFactory sourceApplicationContextFactory; + + /** The bean name of the source {@link ApplicationContextFactory}. */ + private String sourceBeanName; + + /** + * Instantiates a new switchable state. + * + * @param sourceBeanName + * the source bean name + */ + protected SwitchableState(String sourceBeanName) { this.sourceBeanName = sourceBeanName; } - } - /* - * (non-Javadoc) - * @see org.alfresco.enterprise.repo.management.ConfigurableBean#onStart() - */ - public synchronized void start() - { - if (this.sourceApplicationContextFactory == null) + /* + * (non-Javadoc) + * @see org.alfresco.enterprise.repo.management.ConfigurableBean#onStart() + */ + public synchronized void start() { - this.sourceApplicationContextFactory = (ApplicationContextFactory) getParent().getBean(this.sourceBeanName); - this.sourceApplicationContextFactory.start(); - } - } - - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.SelfDescribingBean#onStop() - */ - public void stop() - { - if (this.sourceApplicationContextFactory != null) - { - try + if (this.sourceApplicationContextFactory == null) { - this.sourceApplicationContextFactory.stop(); - } - catch (Exception e) - { - throw new RuntimeException(e); + this.sourceApplicationContextFactory = (ApplicationContextFactory) getParent().getBean( + this.sourceBeanName); + this.sourceApplicationContextFactory.start(); } } - this.sourceApplicationContextFactory = null; - } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.ManagedApplicationContextFactory#getApplicationContext() - */ - public synchronized ApplicationContext getApplicationContext() - { - if (this.sourceApplicationContextFactory == null) + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.SelfDescribingBean#onStop() + */ + public void stop() { - start(); + if (this.sourceApplicationContextFactory != null) + { + try + { + this.sourceApplicationContextFactory.stop(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + this.sourceApplicationContextFactory = null; } - return this.sourceApplicationContextFactory.getApplicationContext(); - } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) - */ - public synchronized String getProperty(String name) - { - if (!name.equals(SOURCE_BEAN_PROPERTY)) + /** + * Gets the application context. + * + * @return the application context + */ + public synchronized ApplicationContext getApplicationContext() { - return null; + if (this.sourceApplicationContextFactory == null) + { + start(); + } + return this.sourceApplicationContextFactory.getApplicationContext(); } - return this.sourceBeanName; - } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() - */ - public Set getPropertyNames() - { - return Collections.singleton(SOURCE_BEAN_PROPERTY); - } + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getProperty(java.lang.String) + */ + public synchronized String getProperty(String name) + { + if (!name.equals(SwitchableApplicationContextFactory.SOURCE_BEAN_PROPERTY)) + { + return null; + } + return this.sourceBeanName; + } - /* - * (non-Javadoc) - * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, java.lang.String) - */ - public synchronized void setProperty(String name, String value) - { - if (!name.equals(SOURCE_BEAN_PROPERTY)) + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#getPropertyNames() + */ + public Set getPropertyNames() { - throw new IllegalStateException("Illegal attempt to write to property \"" + name + "\""); + return Collections.singleton(SwitchableApplicationContextFactory.SOURCE_BEAN_PROPERTY); } - if (!getParent().containsBean(value)) + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#setProperty(java.lang.String, + * java.lang.String) + */ + public synchronized void setProperty(String name, String value) { - throw new IllegalStateException("\"" + value + "\" is not a valid bean name"); + if (!name.equals(SwitchableApplicationContextFactory.SOURCE_BEAN_PROPERTY)) + { + throw new IllegalStateException("Illegal attempt to write to property \"" + name + "\""); + } + if (!getParent().containsBean(value)) + { + throw new IllegalStateException("\"" + value + "\" is not a valid bean name"); + } + if (this.sourceApplicationContextFactory != null) + { + stop(); + this.sourceBeanName = value; + start(); + } + else + { + this.sourceBeanName = value; + } } - setSourceBeanName(value); + } } diff --git a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationService.java b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationService.java index 1011aa9b00..5eddc4a099 100644 --- a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationService.java +++ b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationService.java @@ -27,48 +27,35 @@ package org.alfresco.repo.security.authentication; import java.util.List; import java.util.Set; -import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.service.cmr.security.AuthenticationService; -import org.springframework.beans.factory.InitializingBean; /** * Common code for authentication services * * @author andyh */ -public abstract class AbstractAuthenticationService implements AuthenticationService, InitializingBean +public abstract class AbstractAuthenticationService implements AuthenticationService { + private SysAdminParams sysAdminParams; - private SimpleCache sysAdminCache; - - private static final String KEY_SYSADMIN_ALLOWED_USERS = "sysAdminCache.authAllowedUsers"; - - private static final String KEY_SYSADMIN_MAX_USERS = "sysAdminCache.authMaxUsers"; - - private boolean initialised = false; - - private Integer initialMaxUsers = null; - - private List initialAllowedUsers = null; - - public void setSysAdminCache(SimpleCache sysAdminCache) + public void setSysAdminParams(SysAdminParams sysAdminParams) { - this.sysAdminCache = sysAdminCache; + this.sysAdminParams = sysAdminParams; } - @SuppressWarnings("unchecked") public void preAuthenticationCheck(String userName) throws AuthenticationException { - if (sysAdminCache != null) + if (sysAdminParams != null) { - List allowedUsers = (List) sysAdminCache.get(KEY_SYSADMIN_ALLOWED_USERS); + List allowedUsers = sysAdminParams.getAllowedUserList(); if ((allowedUsers != null) && (!allowedUsers.contains(userName))) { throw new AuthenticationDisallowedException("Username not allowed: " + userName); } - Integer maxUsers = (Integer) sysAdminCache.get(KEY_SYSADMIN_MAX_USERS); + Integer maxUsers = (Integer) sysAdminParams.getMaxUsers(); if ((maxUsers != null) && (maxUsers != -1) && (getUsersWithTickets(true).size() >= maxUsers)) { @@ -77,62 +64,14 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer } } - public void setAllowedUsers(List allowedUsers) - { - if (initialised) - { - if (sysAdminCache != null) - { - sysAdminCache.put(KEY_SYSADMIN_ALLOWED_USERS, allowedUsers); - } - } - else - { - initialAllowedUsers = allowedUsers; - } - - } - - @SuppressWarnings("unchecked") public List getAllowedUsers() { - if (sysAdminCache != null) - { - return (List) sysAdminCache.get(KEY_SYSADMIN_ALLOWED_USERS); - } - else - { - return null; - } + return sysAdminParams.getAllowedUserList(); } - public void setMaxUsers(int maxUsers) - { - if (initialised) - { - if (sysAdminCache != null) - { - sysAdminCache.put(KEY_SYSADMIN_MAX_USERS, new Integer(maxUsers)); - } - } - else - { - initialMaxUsers = new Integer(maxUsers); - } - } - - @SuppressWarnings("unchecked") public int getMaxUsers() { - if (sysAdminCache != null) - { - Integer maxUsers = (Integer) sysAdminCache.get(KEY_SYSADMIN_MAX_USERS); - return (maxUsers == null ? -1 : maxUsers.intValue()); - } - else - { - return -1; - } + return sysAdminParams.getMaxUsers(); } public abstract Set getUsersWithTickets(boolean nonExpiredOnly); @@ -142,15 +81,4 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer public abstract int countTickets(boolean nonExpiredOnly); public abstract Set getTicketComponents(); - - final public void afterPropertiesSet() throws Exception - { - initialised = true; - if (sysAdminCache != null) - { - sysAdminCache.put(KEY_SYSADMIN_MAX_USERS, initialMaxUsers); - sysAdminCache.put(KEY_SYSADMIN_ALLOWED_USERS, initialAllowedUsers); - } - } - } diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java index c0745a8bdc..3c0e8771e0 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java @@ -38,6 +38,8 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.attributes.Attribute; import org.alfresco.repo.attributes.LongAttributeValue; import org.alfresco.repo.attributes.MapAttributeValue; +import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -50,6 +52,8 @@ import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; @@ -65,7 +69,8 @@ import org.springframework.context.ApplicationEvent; * the 'chain' of application contexts, managed by a {@link ChildApplicationContextManager}, and compares its * timestamped user and group information with the local users and groups last retrieved from the same source. Any * updates and additions made to those users and groups are applied to the local copies. The ordering of each - * {@link UserRegistry} in the chain determines its precedence when it comes to user and group name collisions. + * {@link UserRegistry} in the chain determines its precedence when it comes to user and group name collisions. The + * {@link JobLockService} is used to ensure that in a cluster, no two nodes actually run a synchronize at the same time. *

* The force argument determines whether a complete or partial set of information is queried from the * {@link UserRegistry}. When true then all users and groups are queried. With this complete set of @@ -84,12 +89,20 @@ import org.springframework.context.ApplicationEvent; */ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean implements UserRegistrySynchronizer { - /** The number of users / groups we add at a time in a transaction **/ + + /** The number of users / groups we add at a time in a transaction *. */ private static final int BATCH_SIZE = 10; /** The logger. */ private static final Log logger = LogFactory.getLog(ChainingUserRegistrySynchronizer.class); + /** The name of the lock used to ensure that a synchronize does not run on more than one node at the same time. */ + private static final QName LOCK_QNAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, + "ChainingUserRegistrySynchronizer"); + + /** The maximum time this lock will be held for (1 day). */ + private static final long LOCK_TTL = 1000 * 60 * 60 * 24; + /** The path in the attribute service below which we persist attributes. */ private static final String ROOT_ATTRIBUTE_PATH = ".ChainingUserRegistrySynchronizer"; @@ -117,13 +130,16 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl /** The retrying transaction helper. */ private RetryingTransactionHelper retryingTransactionHelper; - /** Should we trigger a differential sync when missing people log in? */ + /** The job lock service. */ + private JobLockService jobLockService; + + /** Should we trigger a differential sync when missing people log in?. */ private boolean syncWhenMissingPeopleLogIn = true; - /** Should we trigger a differential sync on startup? */ + /** Should we trigger a differential sync on startup?. */ private boolean syncOnStartup = true; - /** Should we auto create a missing person on log in? */ + /** Should we auto create a missing person on log in?. */ private boolean autoCreatePeopleOnLogin = true; /** @@ -193,7 +209,18 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } /** - * Controls whether we auto create a missing person on log in + * Sets the job lock service. + * + * @param jobLockService + * the job lock service + */ + public void setJobLockService(JobLockService jobLockService) + { + this.jobLockService = jobLockService; + } + + /** + * Controls whether we auto create a missing person on log in. * * @param autoCreatePeopleOnLogin * true if we should auto create a missing person on log in @@ -204,7 +231,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } /** - * Controls whether we trigger a differential sync when missing people log in + * Controls whether we trigger a differential sync when missing people log in. * * @param syncWhenMissingPeopleLogIn * if we should trigger a sync when missing people log in @@ -215,7 +242,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } /** - * Controls whether we trigger a differential sync when the subsystem starts up + * Controls whether we trigger a differential sync when the subsystem starts up. * * @param syncOnStartup * if we should trigger a sync on startup @@ -231,10 +258,36 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl */ public void synchronize(boolean force, boolean splitTxns) { + // First, try to obtain a lock to ensure we are the only node trying to run this job + try + { + if (splitTxns) + { + // If this is an automated sync on startup or scheduled sync, don't even wait around for the lock. + // Assume the sync will be completed on another node. + this.jobLockService.getTransactionalLock(ChainingUserRegistrySynchronizer.LOCK_QNAME, + ChainingUserRegistrySynchronizer.LOCK_TTL, 0, 1); + } + else + { + // If this is a login-triggered sync, give it a few retries before giving up + this.jobLockService.getTransactionalLock(ChainingUserRegistrySynchronizer.LOCK_QNAME, + ChainingUserRegistrySynchronizer.LOCK_TTL, 3000, 10); + } + } + catch (LockAcquisitionException e) + { + // Don't proceed with the sync if it is running on another node + ChainingUserRegistrySynchronizer.logger + .warn("User registry synchronization already running in another thread. Synchronize aborted"); + return; + } + Set visitedZoneIds = new TreeSet(); Collection instanceIds = this.applicationContextManager.getInstanceIds(); - // Work out the set of all zone IDs in the authentication chain so that we can decide which users / groups need + // Work out the set of all zone IDs in the authentication chain so that we can decide which users / groups + // need // 're-zoning' Set allZoneIds = new TreeSet(); for (String id : instanceIds) @@ -894,6 +947,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl return zones; } + /* + * (non-Javadoc) + * @see org.alfresco.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent) + */ @Override protected void onBootstrap(ApplicationEvent event) { @@ -928,6 +985,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } } + /* + * (non-Javadoc) + * @see org.alfresco.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent) + */ @Override protected void onShutdown(ApplicationEvent event) { diff --git a/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java b/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java index 12c7a54c1c..9220d6ff94 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java +++ b/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -26,7 +26,8 @@ package org.alfresco.repo.transaction; import javax.transaction.UserTransaction; -import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.VmShutdownListener; import org.alfresco.util.transaction.SpringAwareUserTransaction; @@ -45,59 +46,65 @@ public class TransactionServiceImpl implements TransactionService private static VmShutdownListener shutdownListener = new VmShutdownListener("TransactionService"); private PlatformTransactionManager transactionManager; + private AuthenticationContext authenticationContext; private int maxRetries = -1; private int minRetryWaitMs = -1; private int maxRetryWaitMs = -1; private int retryWaitIncrementMs = -1; - - // SysAdmin cache - used to cluster certain JMX operations - private SimpleCache sysAdminCache; - private final static String KEY_SYSADMIN_ALLOW_WRITE = "sysAdminCache.txAllowWrite"; - - + + // SysAdmin cache - used to cluster certain configuration parameters + private SysAdminParams sysAdminParams; + private boolean allowWrite; + /** * Set the transaction manager to use * - * @param transactionManager platform transaction manager + * @param transactionManager + * platform transaction manager */ public void setTransactionManager(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } - - public void setSysAdminCache(SimpleCache sysAdminCache) + + /** + * Sets the authentication context. + * + * @param authenticationContext + * the authentication context + */ + public void setAuthenticationContext(AuthenticationContext authenticationContext) { - this.sysAdminCache = sysAdminCache; + this.authenticationContext = authenticationContext; + } + + public void setSysAdminParams(SysAdminParams sysAdminParams) + { + this.sysAdminParams = sysAdminParams; } /** * Set the read-only mode for all generated transactions. * - * @param allowWrite false if all transactions must be read-only + * @param allowWrite + * false if all transactions must be read-only */ public void setAllowWrite(boolean allowWrite) { - sysAdminCache.put(KEY_SYSADMIN_ALLOW_WRITE, allowWrite); + this.allowWrite = allowWrite; } - + public boolean isReadOnly() { if (shutdownListener.isVmShuttingDown()) { return true; } - try - { - Boolean allowWrite = (Boolean)sysAdminCache.get(KEY_SYSADMIN_ALLOW_WRITE); - return (allowWrite == null ? false : ! allowWrite); - } - catch (IllegalStateException e) - { - // The cache is not working - return true; - } + // Make the repo writable to the system user, so that e.g. the allow write flag can still be edited by JMX + return !this.allowWrite || !this.authenticationContext.isCurrentUserTheSystemUser() + && !this.sysAdminParams.getAllowWrite(); } - + /** * @see RetryingTransactionHelper#setMaxRetries(int) */ @@ -135,25 +142,19 @@ public class TransactionServiceImpl implements TransactionService */ public UserTransaction getUserTransaction() { - SpringAwareUserTransaction txn = new SpringAwareUserTransaction( - transactionManager, - isReadOnly(), - TransactionDefinition.ISOLATION_DEFAULT, - TransactionDefinition.PROPAGATION_REQUIRED, + SpringAwareUserTransaction txn = new SpringAwareUserTransaction(transactionManager, isReadOnly(), + TransactionDefinition.ISOLATION_DEFAULT, TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.TIMEOUT_DEFAULT); return txn; } - + /** * @see org.springframework.transaction.TransactionDefinition#PROPAGATION_REQUIRED */ public UserTransaction getUserTransaction(boolean readOnly) { - SpringAwareUserTransaction txn = new SpringAwareUserTransaction( - transactionManager, - (readOnly | isReadOnly()), - TransactionDefinition.ISOLATION_DEFAULT, - TransactionDefinition.PROPAGATION_REQUIRED, + SpringAwareUserTransaction txn = new SpringAwareUserTransaction(transactionManager, (readOnly | isReadOnly()), + TransactionDefinition.ISOLATION_DEFAULT, TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.TIMEOUT_DEFAULT); return txn; } @@ -163,11 +164,8 @@ public class TransactionServiceImpl implements TransactionService */ public UserTransaction getNonPropagatingUserTransaction() { - SpringAwareUserTransaction txn = new SpringAwareUserTransaction( - transactionManager, - isReadOnly(), - TransactionDefinition.ISOLATION_DEFAULT, - TransactionDefinition.PROPAGATION_REQUIRES_NEW, + SpringAwareUserTransaction txn = new SpringAwareUserTransaction(transactionManager, isReadOnly(), + TransactionDefinition.ISOLATION_DEFAULT, TransactionDefinition.PROPAGATION_REQUIRES_NEW, TransactionDefinition.TIMEOUT_DEFAULT); return txn; } @@ -177,18 +175,15 @@ public class TransactionServiceImpl implements TransactionService */ public UserTransaction getNonPropagatingUserTransaction(boolean readOnly) { - SpringAwareUserTransaction txn = new SpringAwareUserTransaction( - transactionManager, - (readOnly | isReadOnly()), - TransactionDefinition.ISOLATION_DEFAULT, - TransactionDefinition.PROPAGATION_REQUIRES_NEW, + SpringAwareUserTransaction txn = new SpringAwareUserTransaction(transactionManager, (readOnly | isReadOnly()), + TransactionDefinition.ISOLATION_DEFAULT, TransactionDefinition.PROPAGATION_REQUIRES_NEW, TransactionDefinition.TIMEOUT_DEFAULT); return txn; } /** - * Creates a new helper instance. It can be reused or customized by the client code: - * each instance is new and initialized afresh. + * Creates a new helper instance. It can be reused or customized by the client code: each instance is new and + * initialized afresh. */ public RetryingTransactionHelper getRetryingTransactionHelper() { diff --git a/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java b/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java index e9592e9c93..ff95c2f256 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java +++ b/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java @@ -29,10 +29,9 @@ import javax.transaction.Status; import javax.transaction.UserTransaction; import junit.framework.TestCase; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import org.alfresco.repo.cache.EhCacheAdapter; +import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.NodeService; @@ -59,17 +58,10 @@ public class TransactionServiceImplTest extends TestCase { transactionManager = (PlatformTransactionManager) ctx.getBean("transactionManager"); transactionService = new TransactionServiceImpl(); - transactionService.setTransactionManager(transactionManager); - - CacheManager cacheManager = new CacheManager(); - Cache sysAdminEhCache = new Cache("sysAdminCache", 10, false, true, 0L, 0L); - cacheManager.addCache(sysAdminEhCache); - EhCacheAdapter sysAdminCache = new EhCacheAdapter(); - sysAdminCache.setCache(sysAdminEhCache); - - transactionService.setSysAdminCache(sysAdminCache); - + transactionService.setTransactionManager(transactionManager); transactionService.setAllowWrite(true); + transactionService.setAuthenticationContext((AuthenticationContext) ctx.getBean("authenticationContext")); + transactionService.setSysAdminParams((SysAdminParams) ctx.getBean("sysAdminParams")); nodeService = (NodeService) ctx.getBean("dbNodeService"); } diff --git a/source/test-resources/sync-test-context.xml b/source/test-resources/sync-test-context.xml index 8eef1087c8..6658891f94 100644 --- a/source/test-resources/sync-test-context.xml +++ b/source/test-resources/sync-test-context.xml @@ -16,7 +16,10 @@ - + + + + userRegistry