mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
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
This commit is contained in:
@@ -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
|
@@ -142,8 +142,8 @@
|
||||
<!-- Each method 'chains' through all AuthenticationService implementations in the authentication chain -->
|
||||
|
||||
<bean id="authenticationService" class="org.alfresco.repo.security.authentication.subsystems.SubsystemChainingAuthenticationService">
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
<property name="applicationContextManager">
|
||||
<ref bean="Authentication" />
|
||||
|
@@ -387,6 +387,9 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- System Administration Parameters -->
|
||||
<bean id="sysAdmin" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean"/>
|
||||
|
||||
<!-- File Servers Subsystem -->
|
||||
<bean id="fileServers" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean">
|
||||
<property name="autoStart">
|
||||
|
@@ -498,39 +498,6 @@
|
||||
</bean>
|
||||
|
||||
|
||||
<!-- ===================================== -->
|
||||
<!-- SysAdmin (JMX Config) Cache -->
|
||||
<!-- ===================================== -->
|
||||
|
||||
<bean name="sysAdminSharedCache" class="org.alfresco.repo.cache.EhCacheAdapter">
|
||||
<property name="cache">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean" >
|
||||
<property name="cacheManager">
|
||||
<ref bean="internalEHCacheManager" />
|
||||
</property>
|
||||
<property name="cacheName">
|
||||
<value>org.alfresco.cache.sysAdminCache</value>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean name="sysAdminCache" class="org.alfresco.repo.cache.TransactionalCache">
|
||||
<property name="sharedCache">
|
||||
<ref bean="sysAdminSharedCache" />
|
||||
</property>
|
||||
<property name="cacheManager" >
|
||||
<ref bean="transactionalEHCacheManager" />
|
||||
</property>
|
||||
<property name="name">
|
||||
<value>org.alfresco.sysAdminTransactionalCache</value>
|
||||
</property>
|
||||
<property name="maxCacheSize">
|
||||
<value>10</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<!-- ===================================== -->
|
||||
<!-- Lookup for AVM entities -->
|
||||
<!-- ===================================== -->
|
||||
|
@@ -214,8 +214,6 @@
|
||||
<bean id="RepoServerMgmt" class="org.alfresco.repo.admin.RepoServerMgmt">
|
||||
<property name="transactionService"><ref bean="transactionService"/></property>
|
||||
<property name="authenticationService"><ref bean="authenticationService"/></property>
|
||||
<property name="maxUsers"><value>${server.maxusers}</value></property>
|
||||
<property name="singleUserOnly"><value>${server.singleuseronly.name}</value></property>
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -316,8 +314,11 @@
|
||||
<property name="transactionManager">
|
||||
<ref bean="transactionManager" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache"/>
|
||||
<property name="authenticationContext">
|
||||
<ref bean="authenticationContext" />
|
||||
</property>
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams"/>
|
||||
</property>
|
||||
<property name="allowWrite">
|
||||
<value>${server.transaction.allow-writes}</value>
|
||||
@@ -1396,5 +1397,17 @@
|
||||
<property name="defaultRetryCount"><value>10</value></property>
|
||||
<property name="defaultRetryWait"><value>20</value></property>
|
||||
</bean>
|
||||
|
||||
|
||||
<!-- Import the sys admin params from the sysAdmin subsystem -->
|
||||
<bean id="sysAdminParams" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
|
||||
<property name="sourceApplicationContextFactory">
|
||||
<ref bean="sysAdmin" />
|
||||
</property>
|
||||
<property name="interfaces">
|
||||
<list>
|
||||
<value>org.alfresco.repo.admin.SysAdminParams</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
@@ -317,12 +317,6 @@
|
||||
eternal="true"
|
||||
overflowToDisk="true"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.sysAdminCache"
|
||||
maxElementsInMemory="10"
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.aclCache"
|
||||
maxElementsInMemory="50000"
|
||||
|
@@ -257,9 +257,6 @@
|
||||
<property name="purgeStoreTxnListener">
|
||||
<ref bean="purgeStoreTxnListener"/>
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache"/>
|
||||
</property>
|
||||
<property name="repoServerMgmt">
|
||||
<ref bean="RepoServerMgmt"/>
|
||||
</property>
|
||||
|
@@ -114,8 +114,8 @@
|
||||
<property name="authenticationComponent">
|
||||
<ref bean="authenticationComponent" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
@@ -82,8 +82,8 @@
|
||||
<property name="authenticationComponent">
|
||||
<ref bean="authenticationComponent" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
@@ -98,8 +98,8 @@
|
||||
<property name="authenticationComponent">
|
||||
<ref bean="authenticationComponent" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
@@ -68,8 +68,8 @@
|
||||
<property name="authenticationComponent">
|
||||
<ref bean="authenticationComponent" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
@@ -103,8 +103,8 @@
|
||||
<property name="authenticationComponent">
|
||||
<ref bean="authenticationComponent" />
|
||||
</property>
|
||||
<property name="sysAdminCache">
|
||||
<ref bean="sysAdminCache" />
|
||||
<property name="sysAdminParams">
|
||||
<ref bean="sysAdminParams" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
@@ -1,9 +1,12 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
<!-- Job definition to import people and groups from one or more external user registries in the authentication chain (e.g. LDAP directory) -->
|
||||
|
||||
<beans>
|
||||
<!--
|
||||
Job definition to import people and groups from one or more external user registries in the authentication chain
|
||||
(e.g. LDAP directory)
|
||||
-->
|
||||
|
||||
<bean id="syncTrigger" class="org.alfresco.util.CronTriggerBean">
|
||||
<property name="jobDetail">
|
||||
<bean id="ldapPeopleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
|
||||
@@ -13,7 +16,7 @@
|
||||
<property name="jobDataAsMap">
|
||||
<map>
|
||||
<entry key="userRegistrySynchronizer">
|
||||
<ref bean="UserRegistrySynchronizer"/>
|
||||
<ref bean="UserRegistrySynchronizer" />
|
||||
</entry>
|
||||
<entry key="synchronizeChangesOnly">
|
||||
<value>${synchronization.synchronizeChangesOnly}</value>
|
||||
@@ -23,22 +26,22 @@
|
||||
</bean>
|
||||
</property>
|
||||
<property name="cronExpression">
|
||||
<value>${synchronization.import.cron}</value>
|
||||
</property>
|
||||
<value>${synchronization.import.cron}</value>
|
||||
</property>
|
||||
<property name="scheduler">
|
||||
<ref bean="schedulerFactory" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<!-- The chaining user registry synchronizer -->
|
||||
<bean id="userRegistrySynchronizer" class="org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizer">
|
||||
<property name="syncWhenMissingPeopleLogIn">
|
||||
<property name="syncWhenMissingPeopleLogIn">
|
||||
<value>${synchronization.syncWhenMissingPeopleLogIn}</value>
|
||||
</property>
|
||||
<property name="syncOnStartup">
|
||||
<property name="syncOnStartup">
|
||||
<value>${synchronization.syncOnStartup}</value>
|
||||
</property>
|
||||
<property name="autoCreatePeopleOnLogin">
|
||||
<property name="autoCreatePeopleOnLogin">
|
||||
<value>${synchronization.autoCreatePeopleOnLogin}</value>
|
||||
</property>
|
||||
<property name="authorityService">
|
||||
@@ -54,7 +57,10 @@
|
||||
<ref bean="Authentication" />
|
||||
</property>
|
||||
<property name="retryingTransactionHelper">
|
||||
<ref bean="retryingTransactionHelper"/>
|
||||
<ref bean="retryingTransactionHelper" />
|
||||
</property>
|
||||
<property name="jobLockService">
|
||||
<ref bean="jobLockService" />
|
||||
</property>
|
||||
<property name="sourceBeanName">
|
||||
<value>userRegistry</value>
|
||||
|
@@ -0,0 +1,18 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
|
||||
<bean id="sysAdminParams" class="org.alfresco.repo.admin.SysAdminParamsImpl">
|
||||
<property name="maxUsers">
|
||||
<value>${server.maxusers}</value>
|
||||
</property>
|
||||
<property name="allowedUsers">
|
||||
<value>${server.allowedusers}</value>
|
||||
</property>
|
||||
<property name="allowWrite">
|
||||
<value>${server.transaction.allow-writes}</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
@@ -0,0 +1,3 @@
|
||||
server.maxusers=-1
|
||||
server.allowedusers=
|
||||
server.transaction.allow-writes=true
|
@@ -190,7 +190,5 @@ public interface LinkValidationService
|
||||
//-------------------------------------------------------------------------
|
||||
public List<String> getHrefsDependentUponFile(String path);
|
||||
|
||||
public void setLinkValidationDisabled(boolean disabled);
|
||||
|
||||
public boolean isLinkValidationDisabled();
|
||||
}
|
||||
|
@@ -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<String> allowedUsers = null;
|
||||
if (PropertyCheck.isValidPropertyString(allowedUsername))
|
||||
{
|
||||
allowedUsers = new ArrayList<String>(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<String> 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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 ?
|
||||
*
|
||||
|
57
source/java/org/alfresco/repo/admin/SysAdminParams.java
Normal file
57
source/java/org/alfresco/repo/admin/SysAdminParams.java
Normal file
@@ -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 <code>true</code> 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 <code>null</code> if all users are allowed to log in
|
||||
*/
|
||||
public List<String> 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();
|
||||
}
|
155
source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java
Normal file
155
source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java
Normal file
@@ -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<String> 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 <code>null</code> 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<String>(length);
|
||||
while (tkn.hasMoreTokens())
|
||||
{
|
||||
this.allowedUsers.add(tkn.nextToken().trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.alfresco.repo.admin.SysAdminParams#getAllowedUserList()
|
||||
*/
|
||||
public List<String> 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
|
||||
* <code>true</code> 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;
|
||||
}
|
||||
|
||||
}
|
@@ -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<Descriptor> createDescriptorWork = new RetryingTransactionCallback<Descriptor>()
|
||||
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||
{
|
||||
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<LicenseService>()
|
||||
{
|
||||
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<Descriptor>()
|
||||
{
|
||||
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<Descriptor>()
|
||||
{
|
||||
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());
|
||||
|
||||
|
@@ -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<String> 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<String> 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<String> instancePath = Collections.singletonList(AbstractPropertyBackedBean.DEFAULT_INSTANCE_NAME);
|
||||
|
||||
/** The combined unique id. */
|
||||
private List<String> 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<String> id)
|
||||
public void setInstancePath(List<String> 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<String> path = getInstancePath();
|
||||
this.id = new ArrayList<String>(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<String> 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<String> 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 <code>false</code>, whereas on the removal of a dynamically created instance, this
|
||||
* value would be <code>true</code>.
|
||||
*/
|
||||
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<String> 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
|
||||
|
@@ -113,10 +113,10 @@ import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
*
|
||||
* <pre>
|
||||
* <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>
|
||||
* </pre>
|
||||
*
|
||||
@@ -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<String, Class<?>> compositePropertyTypes = Collections.emptyMap();
|
||||
|
||||
/** The composite property values. */
|
||||
private Map<String, Map<String, CompositeDataBean>> compositeProperties = new TreeMap<String, Map<String, CompositeDataBean>>();
|
||||
|
||||
/**
|
||||
* 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<String> id) throws IOException
|
||||
Properties propertyDefaults, String category, String typeName, List<String> 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<String> idList = getId();
|
||||
List<String> 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<String> 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<String> getPropertyNames()
|
||||
{
|
||||
Set<String> result = new TreeSet<String>(((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<String, CompositeDataBean> 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<String, CompositeDataBean> propertyValues = this.compositeProperties.get(name);
|
||||
if (propertyValues == null)
|
||||
{
|
||||
propertyValues = Collections.emptyMap();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Map<String, CompositeDataBean> newPropertyValues = new LinkedHashMap<String, CompositeDataBean>(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<String> childId = new ArrayList<String>(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<String> idsToRemove = new TreeSet<String>(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<String, CompositeDataBean> 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<String, Map<String, CompositeDataBean>> 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<String, Map<String, CompositeDataBean>> 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<String, CompositeDataBean> beans = ChildApplicationContextFactory.this.compositeProperties
|
||||
Map<String, CompositeDataBean> beans = ChildApplicationContext.this.compositeProperties
|
||||
.get(beanName);
|
||||
List<Object> 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<String, Map<String, CompositeDataBean>> compositeProperties = new TreeMap<String, Map<String, CompositeDataBean>>();
|
||||
|
||||
/** 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<String> getPropertyNames()
|
||||
{
|
||||
Set<String> result = new TreeSet<String>(((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<String, CompositeDataBean> 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<String, CompositeDataBean> propertyValues = this.compositeProperties.get(name);
|
||||
if (propertyValues == null)
|
||||
{
|
||||
propertyValues = Collections.emptyMap();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Map<String, CompositeDataBean> newPropertyValues = new LinkedHashMap<String, CompositeDataBean>(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<String> childPath = new ArrayList<String>(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<String> idsToRemove = new TreeSet<String>(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 <code>false</code>, whereas on the removal of a dynamically created instance,
|
||||
* this value would be <code>true</code>.
|
||||
*/
|
||||
public void destroy(boolean permanent)
|
||||
{
|
||||
// Cascade the destroy / shutdown
|
||||
for (Map<String, CompositeDataBean> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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<String> propertyNames;
|
||||
@@ -62,10 +61,10 @@ public class CompositeDataBean extends AbstractPropertyBackedBean
|
||||
/** The writeable properties. */
|
||||
private final Set<String> 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<String> id) throws IOException
|
||||
Properties propertyDefaults, String category, Class<?> type, List<String> 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<String>();
|
||||
this.writeableProperties = new TreeSet<String>();
|
||||
for (PropertyDescriptor descriptor : descriptors)
|
||||
@@ -149,7 +142,7 @@ public class CompositeDataBean extends AbstractPropertyBackedBean
|
||||
// Derive a default and instance key prefix of the form "<parent>.default." and "<parent>.value.<this>."
|
||||
StringBuilder defaultKeyPrefixBuff = new StringBuilder(200);
|
||||
StringBuilder instanceKeyPrefixBuff = new StringBuilder(200);
|
||||
List<String> id = getId();
|
||||
List<String> 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<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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<String> instanceIds = new ArrayList<String>(10);
|
||||
|
||||
/** The child application contexts. */
|
||||
private Map<String, ChildApplicationContextFactory> childApplicationContexts = new TreeMap<String, ChildApplicationContextFactory>();
|
||||
|
||||
/**
|
||||
* 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<String> 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<String> getInstanceIds()
|
||||
public Collection<String> 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<String> instanceIds = new ArrayList<String>(10);
|
||||
|
||||
/** The child application contexts. */
|
||||
private Map<String, ChildApplicationContextFactory> childApplicationContexts = new TreeMap<String, ChildApplicationContextFactory>();
|
||||
|
||||
/** 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<String> newInstanceIds = new ArrayList<String>(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<String> 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<String> 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<String> newInstanceIds = new ArrayList<String>(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<String> childId = new ArrayList<String>(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<String> idsToRemove = new TreeSet<String>(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<String> childId = new ArrayList<String>(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<String> idsToRemove = new TreeSet<String>(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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -25,64 +25,25 @@
|
||||
package org.alfresco.repo.management.subsystems;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A <code>PropertyBackedBean</code> 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 <code>PropertyBackedBean</code>s 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 <code>PropertyBackedBean</code>s
|
||||
* 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<String> getId();
|
||||
|
||||
/**
|
||||
* Gets the names of all properties.
|
||||
*
|
||||
* @return the property names
|
||||
*/
|
||||
public Set<String> 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 <code>true</code>.
|
||||
*
|
||||
* @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 <code>false</code>, whereas on the removal of a dynamically created instance, this
|
||||
* value would be <code>true</code>.
|
||||
*/
|
||||
public void destroy(boolean isPermanent);
|
||||
public void revert();
|
||||
}
|
||||
|
@@ -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<String> 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<String> getSourceId()
|
||||
{
|
||||
return (PropertyBackedBean) getSource();
|
||||
return this.sourceId;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -64,4 +64,21 @@ public interface PropertyBackedBeanRegistry
|
||||
* value would be <code>true</code>.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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 <code>PropertyBackedBeanState</code> 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
|
||||
* <code>PropertyBackedBeanState</code>s 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<String> 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 <code>true</code>.
|
||||
*
|
||||
* @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();
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
|
||||
|
@@ -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<String> 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<String> 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);
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -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<String, Object> 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<String> initialAllowedUsers = null;
|
||||
|
||||
public void setSysAdminCache(SimpleCache<String, Object> 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<String> allowedUsers = (List<String>) sysAdminCache.get(KEY_SYSADMIN_ALLOWED_USERS);
|
||||
List<String> 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<String> allowedUsers)
|
||||
{
|
||||
if (initialised)
|
||||
{
|
||||
if (sysAdminCache != null)
|
||||
{
|
||||
sysAdminCache.put(KEY_SYSADMIN_ALLOWED_USERS, allowedUsers);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initialAllowedUsers = allowedUsers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<String> getAllowedUsers()
|
||||
{
|
||||
if (sysAdminCache != null)
|
||||
{
|
||||
return (List<String>) 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<String> getUsersWithTickets(boolean nonExpiredOnly);
|
||||
@@ -142,15 +81,4 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer
|
||||
public abstract int countTickets(boolean nonExpiredOnly);
|
||||
|
||||
public abstract Set<TicketComponent> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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.
|
||||
* <p>
|
||||
* The <code>force</code> argument determines whether a complete or partial set of information is queried from the
|
||||
* {@link UserRegistry}. When <code>true</code> then <i>all</i> 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
|
||||
* <code>true</code> 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
|
||||
* <codetrue</code> 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
|
||||
* <codetrue</code> 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<String> visitedZoneIds = new TreeSet<String>();
|
||||
Collection<String> 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<String> allZoneIds = new TreeSet<String>();
|
||||
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)
|
||||
{
|
||||
|
@@ -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<String, Object> 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<String, Object> 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()
|
||||
{
|
||||
|
@@ -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<String, Object> sysAdminCache = new EhCacheAdapter<String, Object>();
|
||||
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");
|
||||
}
|
||||
|
@@ -16,7 +16,10 @@
|
||||
<ref bean="testApplicationContextManager" />
|
||||
</property>
|
||||
<property name="retryingTransactionHelper">
|
||||
<ref bean="retryingTransactionHelper"/>
|
||||
<ref bean="retryingTransactionHelper" />
|
||||
</property>
|
||||
<property name="jobLockService">
|
||||
<ref bean="jobLockService" />
|
||||
</property>
|
||||
<property name="sourceBeanName">
|
||||
<value>userRegistry</value>
|
||||
|
Reference in New Issue
Block a user