From 5c06b156047823d4c66f52abf1599c02777e1471 Mon Sep 17 00:00:00 2001 From: Derek Hulley Date: Tue, 9 Jul 2013 13:56:14 +0000 Subject: [PATCH] Merged DEV to HEAD 52232: Introduce filter to select only enabled tenants (ALF-19172) 52233: Missed file for rev 52232 (ALF-19172) 52246: MT: Make lowercasing of tenant domain a little more explicit 52247: MT: Clean up imports, redundant non-Javadoc and add @Override git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@52288 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/ibatis/alfresco-SqlMapConfig.xml | 1 + .../tenants-common-SqlMap.xml | 17 +- .../tenant/AbstractTenantAdminDAOImpl.java | 19 +- .../repo/domain/tenant/TenantAdminDAO.java | 2 +- .../domain/tenant/TenantAdminDAOTest.java | 12 +- .../repo/domain/tenant/TenantQueryEntity.java | 80 + .../tenant/ibatis/TenantAdminDAOImpl.java | 7 +- .../repo/module/ModuleComponentHelper.java | 1432 ++++++++--------- .../module/ModuleComponentHelperTest.java | 2 +- .../repo/module/ModuleServiceImpl.java | 8 +- .../AbstractTenantRoutingContentStore.java | 2 +- .../repo/tenant/MultiTAdminServiceImpl.java | 2 +- 12 files changed, 839 insertions(+), 745 deletions(-) create mode 100644 source/java/org/alfresco/repo/domain/tenant/TenantQueryEntity.java diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml index 7922f8476f..e00786ac07 100644 --- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml +++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml @@ -180,6 +180,7 @@ Inbound settings from iBatis + diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/tenants-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/tenants-common-SqlMap.xml index bd78d9fef3..ec294384ab 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/tenants-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/tenants-common-SqlMap.xml @@ -61,16 +61,19 @@ tenant_domain = #{tenantDomain} - select - tenant_domain as tenantDomain, - version as version, - enabled as enabled, - tenant_name as tenantName, - content_root as contentRoot, - db_url as dbUrl + tenant_domain as tenantDomain, + version as version, + enabled as enabled, + tenant_name as tenantName, + content_root as contentRoot, + db_url as dbUrl from alf_tenant + + enabled = #{enabled} + diff --git a/source/java/org/alfresco/repo/domain/tenant/AbstractTenantAdminDAOImpl.java b/source/java/org/alfresco/repo/domain/tenant/AbstractTenantAdminDAOImpl.java index 92ec20360f..4e10aff4b4 100644 --- a/source/java/org/alfresco/repo/domain/tenant/AbstractTenantAdminDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/tenant/AbstractTenantAdminDAOImpl.java @@ -105,11 +105,12 @@ public abstract class AbstractTenantAdminDAOImpl implements TenantAdminDAO private TenantEntity getTenantImpl(String tenantDomain) { + tenantDomain = tenantDomain.toLowerCase(); Pair entityPair = tenantEntityCache.getByKey(tenantDomain); if (entityPair == null) { // try lower-case to make sure - entityPair = tenantEntityCache.getByKey(tenantDomain.toLowerCase()); + entityPair = tenantEntityCache.getByKey(tenantDomain); if (entityPair == null) { return null; @@ -119,9 +120,16 @@ public abstract class AbstractTenantAdminDAOImpl implements TenantAdminDAO } @Override - public List listTenants() + public List listTenants(boolean enabledOnly) { - return getTenantEntities(); + if (enabledOnly) + { + return getTenantEntities(Boolean.TRUE); + } + else + { + return getTenantEntities(null); + } } @Override @@ -241,7 +249,10 @@ public abstract class AbstractTenantAdminDAOImpl implements TenantAdminDAO protected abstract TenantEntity createTenantEntity(TenantEntity tenantEntity); protected abstract TenantEntity getTenantEntity(String tenantDomain); - protected abstract List getTenantEntities(); + /** + * @param enabled Enabled or disabled tenants or null for no filter + */ + protected abstract List getTenantEntities(Boolean enabled); protected abstract int updateTenantEntity(TenantEntity tenantEntity); protected abstract int deleteTenantEntity(String tenantDomain); } diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAO.java b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAO.java index 3358aea6cc..4e748d98a0 100644 --- a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAO.java +++ b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAO.java @@ -42,7 +42,7 @@ public interface TenantAdminDAO /** * List tenants */ - List listTenants(); + List listTenants(boolean enabledOnly); /** * Get tenant for update diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java index eeb987f7e4..04b88ff32b 100644 --- a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java +++ b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java @@ -119,13 +119,13 @@ public class TenantAdminDAOTest extends TestCase return txnHelper.doInTransaction(callback, true); } - private List listTenants() throws Exception + private List listTenants(final boolean enabledOnly) throws Exception { RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { public List execute() throws Throwable { - return tenantAdminDAO.listTenants(); + return tenantAdminDAO.listTenants(enabledOnly); } }; return txnHelper.doInTransaction(callback, true); @@ -229,7 +229,8 @@ public class TenantAdminDAOTest extends TestCase final String tenantDomainPrefix = getName() + "-" + System.currentTimeMillis(); final int cnt = 5; - int beforeCnt = listTenants().size(); + int beforeCnt = listTenants(false).size(); + int enabledCnt = listTenants(true).size(); for (int i = 1; i <= cnt; i++) { @@ -240,7 +241,8 @@ public class TenantAdminDAOTest extends TestCase tenantEntity = createTenant(tenantDomain, false); assertNotNull(tenantEntity); - assertEquals(i+beforeCnt, listTenants().size()); + assertEquals(i+beforeCnt, listTenants(false).size()); + assertEquals("Tenant enabled/disabled count incorrect.", enabledCnt, listTenants(true).size()); tenantEntity = getTenant(tenantDomain); assertNotNull(tenantEntity); @@ -254,7 +256,7 @@ public class TenantAdminDAOTest extends TestCase deleteTenant(tenantDomain); - assertEquals(i-1+beforeCnt, listTenants().size()); + assertEquals(i-1+beforeCnt, listTenants(false).size()); tenantEntity = getTenant(tenantDomain); assertNull(tenantEntity); diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantQueryEntity.java b/source/java/org/alfresco/repo/domain/tenant/TenantQueryEntity.java new file mode 100644 index 0000000000..a5e03d5a5a --- /dev/null +++ b/source/java/org/alfresco/repo/domain/tenant/TenantQueryEntity.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.domain.tenant; + +/** + * Entity for alf_tenant queries. + * + * @author Derek Hulley + * @since 4.2 + */ +public class TenantQueryEntity +{ + private String tenantDomain; + private String tenantName; + private Boolean enabled; + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(512); + sb.append("TenantQueryEntity") + .append("[ tenantDomain=").append(tenantDomain) + .append(", tenantName=").append(tenantName) + .append(", enabled=").append(enabled) + .append("]"); + return sb.toString(); + } + + /** Framework usage only */ + @SuppressWarnings("unused") + private String getTenantDomain() + { + return tenantDomain; + } + + public void setTenantDomain(String tenantDomain) + { + this.tenantDomain = tenantDomain; + } + + /** Framework usage only */ + @SuppressWarnings("unused") + private String getTenantName() + { + return tenantName; + } + + public void setTenantName(String tenantName) + { + this.tenantName = tenantName; + } + + /** Framework usage only */ + @SuppressWarnings("unused") + private Boolean getEnabled() + { + return enabled; + } + + public void setEnabled(Boolean enabled) + { + this.enabled = enabled; + } +} diff --git a/source/java/org/alfresco/repo/domain/tenant/ibatis/TenantAdminDAOImpl.java b/source/java/org/alfresco/repo/domain/tenant/ibatis/TenantAdminDAOImpl.java index 3b53b02bbf..9ddd945383 100644 --- a/source/java/org/alfresco/repo/domain/tenant/ibatis/TenantAdminDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/tenant/ibatis/TenantAdminDAOImpl.java @@ -24,6 +24,7 @@ import java.util.Map; import org.alfresco.repo.domain.tenant.AbstractTenantAdminDAOImpl; import org.alfresco.repo.domain.tenant.TenantEntity; +import org.alfresco.repo.domain.tenant.TenantQueryEntity; import org.mybatis.spring.SqlSessionTemplate; /** @@ -67,9 +68,11 @@ public class TenantAdminDAOImpl extends AbstractTenantAdminDAOImpl @SuppressWarnings("unchecked") @Override - protected List getTenantEntities() + protected List getTenantEntities(Boolean enabled) { - return (List)template.selectList(SELECT_TENANTS); + TenantQueryEntity entity = new TenantQueryEntity(); + entity.setEnabled(enabled); + return (List)template.selectList(SELECT_TENANTS, entity); } @Override diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java index fbe647cb8b..b0d83abefd 100644 --- a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java +++ b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2005-2013 Alfresco Software Limited. +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -14,717 +14,717 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.module; - -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.springframework.extensions.surf.util.I18NUtil; -import org.alfresco.repo.admin.registry.RegistryKey; -import org.alfresco.repo.admin.registry.RegistryService; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.tenant.Tenant; -import org.alfresco.repo.tenant.TenantAdminService; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.tenant.TenantUtil; -import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.module.ModuleDependency; -import org.alfresco.service.cmr.module.ModuleDetails; -import org.alfresco.service.cmr.module.ModuleService; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.PropertyCheck; -import org.alfresco.util.VersionNumber; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Helper class to split up some of the code for managing module components. This class handles - * the execution of the module components. - * - * @author Derek Hulley - */ -public class ModuleComponentHelper -{ - public static final String URI_MODULES_1_0 = "http://www.alfresco.org/system/modules/1.0"; - private static final String REGISTRY_PATH_MODULES = "modules"; - private static final String REGISTRY_PROPERTY_INSTALLED_VERSION = "installedVersion"; - private static final String REGISTRY_PROPERTY_CURRENT_VERSION = "currentVersion"; - private static final String REGISTRY_PATH_COMPONENTS = "components"; - private static final String REGISTRY_PROPERTY_EXECUTION_DATE = "executionDate"; - - private static final String MSG_FOUND_MODULES = "module.msg.found_modules"; - private static final String MSG_STARTING = "module.msg.starting"; - private static final String MSG_INSTALLING = "module.msg.installing"; - private static final String MSG_UPGRADING = "module.msg.upgrading"; - private static final String MSG_DEPENDENCIES = "module.msg.dependencies"; - private static final String MSG_MISSING = "module.msg.missing"; - private static final String WARN_NO_INSTALL_VERSION = "module.warn.no_install_version"; - private static final String ERR_MISSING_DEPENDENCY = "module.err.missing_dependency"; - private static final String ERR_UNSUPPORTED_REPO_VERSION = "module.err.unsupported_repo_version"; - private static final String ERR_NO_DOWNGRADE = "module.err.downgrading_not_supported"; - private static final String ERR_COMPONENT_ALREADY_REGISTERED = "module.err.component_already_registered"; - private static final String ERR_COMPONENT_IN_MISSING_MODULE = "module.err.component_in_missing_module"; - private static final String ERR_ORPHANED_COMPONENTS = "module.err.orphaned_components"; - - private static Log logger = LogFactory.getLog(ModuleComponentHelper.class); - private static Log loggerService = LogFactory.getLog(ModuleServiceImpl.class); - - private ServiceRegistry serviceRegistry; - private DescriptorService descriptorService; - private RegistryService registryService; - private ModuleService moduleService; - private TenantAdminService tenantAdminService; - private boolean applyToTenants; - private Map> componentsByNameByModule; - - /** Default constructor */ - public ModuleComponentHelper() - { - componentsByNameByModule = new HashMap>(7); - } - - /** - * @param serviceRegistry provides access to the service APIs - */ - public void setServiceRegistry(ServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - - /** - * @param descriptorService gives access to the current repository version - */ - public void setDescriptorService(DescriptorService descriptorService) - { - this.descriptorService = descriptorService; - } - - /** - * @param registryService the service used to persist component execution details. - */ - public void setRegistryService(RegistryService registryService) - { - this.registryService = registryService; - } - - /** - * @param moduleService the service from which to get the available modules. - */ - public void setModuleService(ModuleService moduleService) - { - this.moduleService = moduleService; - } - - public void setTenantAdminService(TenantAdminService tenantAdminService) - { - this.tenantAdminService = tenantAdminService; - } - - public void setApplyToTenants(boolean applyToTenants) - { - this.applyToTenants = applyToTenants; - } - - /** - * Add a managed module component to the registry of components. These will be controlled - * by the {@link #startModules()} method. - * - * @param component a module component to be executed - */ - public synchronized void registerComponent(ModuleComponent component) - { - String moduleId = component.getModuleId(); - String name = component.getName(); - // Get the map of components for the module - Map componentsByName = componentsByNameByModule.get(moduleId); - if (componentsByName == null) - { - componentsByName = new HashMap(11); - componentsByNameByModule.put(moduleId, componentsByName); - } - // Check if the component has already been registered - if (componentsByName.containsKey(name)) - { - throw AlfrescoRuntimeException.create(ERR_COMPONENT_ALREADY_REGISTERED, name, moduleId); - } - // Add it - componentsByName.put(name, component); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Registered component: " + component); - } - } - - /** - * @return Returns the map of components keyed by name. The map could be empty but - * will never be null. - */ - private synchronized Map getComponents(String moduleId) - { - Map componentsByName = componentsByNameByModule.get(moduleId); - if (componentsByName != null) - { - // Done - return componentsByName; - } - else - { - // Done - return Collections.emptyMap(); - } - } - - /** - * {@inheritDoc} - */ - public synchronized void startModules() - { - // Check properties - PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); - PropertyCheck.mandatory(this, "registryService", registryService); - PropertyCheck.mandatory(this, "moduleService", moduleService); - PropertyCheck.mandatory(this, "tenantAdminService", tenantAdminService); - /* - * Ensure transactionality and the correct authentication - */ - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - try - { - TransactionService transactionService = serviceRegistry.getTransactionService(); - - // Note: for system bootstrap this will be the default domain, else tenant domain for tenant create/import - final String tenantDomainCtx = tenantAdminService.getCurrentUserDomain(); - - if (tenantAdminService.isEnabled() && (! tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN)) && (! applyToTenants)) - { - // nothing to start (eg. when creating/importing tenant and applyToTenants = false) - return null; - } - - // Get all the modules - List modules = moduleService.getAllModules(); - loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); - - // Process each module in turn. Ordering is not important. - final Map> mapExecutedComponents = new HashMap>(1); - final Map> mapStartedModules = new HashMap>(1); - - mapExecutedComponents.put(tenantDomainCtx, new HashSet(10)); - mapStartedModules.put(tenantDomainCtx, new HashSet(2)); - - List tenantsNonFinal = null; - if (tenantAdminService.isEnabled()) - { - if (tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN) && applyToTenants) - { - tenantsNonFinal = tenantAdminService.getAllTenants(); - for (Tenant tenant : tenantsNonFinal) - { - mapExecutedComponents.put(tenant.getTenantDomain(), new HashSet(10)); - mapStartedModules.put(tenant.getTenantDomain(), new HashSet(2)); - } - } - } - - final List tenants = tenantsNonFinal; - - for (final ModuleDetails module : modules) - { - RetryingTransactionCallback startModuleWork = new RetryingTransactionCallback() - { - public Object execute() throws Exception - { - startModule(module, mapStartedModules.get(tenantDomainCtx), mapExecutedComponents.get(tenantDomainCtx)); - - if (tenants != null) - { - for (Tenant tenant : tenants) - { - final String tenantDomain = tenant.getTenantDomain(); - TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - startModule(module, mapStartedModules.get(tenantDomain), mapExecutedComponents.get(tenantDomain)); - return null; - } - }, tenantDomain); - } - } - - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(startModuleWork, transactionService.isReadOnly()); - } - - // Check for missing modules. - checkForMissingModules(); - - if (tenants != null) - { - for (Tenant tenant : tenants) - { - TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - checkForMissingModules(); - return null; - } - }, tenant.getTenantDomain()); - } - } - - // Check that all components where executed, or considered for execution - checkForOrphanComponents(mapExecutedComponents.get(tenantDomainCtx)); - - if (tenants != null) - { - for (Tenant tenant : tenants) - { - final String tenantDomain = tenant.getTenantDomain(); - TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - checkForOrphanComponents(mapExecutedComponents.get(tenantDomain)); - return null; - } - }, tenantDomain); - } - } - } - catch (Throwable e) - { - throw new AlfrescoRuntimeException("Failed to start modules", e); - } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - - /** - * {@inheritDoc} - */ - public synchronized void shutdownModules() - { - // Check properties - PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); - PropertyCheck.mandatory(this, "registryService", registryService); - PropertyCheck.mandatory(this, "moduleService", moduleService); - PropertyCheck.mandatory(this, "tenantAdminService", tenantAdminService); - - /* - * Ensure correct authentication - */ - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - // Get all the modules - List modules = moduleService.getAllModules(); - loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); - - for (ModuleDetails module : modules) - { - Map components = getComponents(module.getId()); - for (ModuleComponent component : components.values()) - { - component.shutdown(); - } - } - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - - /** - * Checks that all components have been executed or considered for execution. - * @param executedComponents - */ - private void checkForOrphanComponents(Set executedComponents) - { - Set missedComponents = new HashSet(executedComponents); - - // Iterate over each module registered by components - for (Map.Entry> entry : componentsByNameByModule.entrySet()) - { - String moduleId = entry.getKey(); - Map componentsByName = entry.getValue(); - // Iterate over each component registered against the module ID - for (Map.Entry entryInner : componentsByName.entrySet()) - { - String componentName = entryInner.getKey(); - ModuleComponent component = entryInner.getValue(); - // Check if it has been executed - if (executedComponents.contains(component)) - { - // It was executed, so remove it from the missed components set - missedComponents.remove(component); - } - else - { - String msg = I18NUtil.getMessage( - ERR_COMPONENT_IN_MISSING_MODULE, - componentName, moduleId); - logger.error(msg); - } - } - } - // Dump if there were orphans - if (missedComponents.size() > 0) - { - throw AlfrescoRuntimeException.create(ERR_ORPHANED_COMPONENTS, missedComponents.size()); - } - } - - /** - * Gets a list of all registered modules. - * - * @return A Collection of module IDs - */ - Collection getRegistryModuleIDs() - { - // Get the IDs of all modules from the registry - RegistryKey moduleKeyAllIds = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, null); - - return registryService.getChildElements(moduleKeyAllIds); - } - - /** - * Returns the version number of a module from the Registry. - * - * @param moduleId - * @return - */ - VersionNumber getVersion(String moduleId) - { - RegistryKey moduleKeyCurrentVersion = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_CURRENT_VERSION); - VersionNumber versionCurrent = (VersionNumber) registryService.getProperty(moduleKeyCurrentVersion); - return versionCurrent; - } - - /** - * Checks to see if there are any modules registered as installed that aren't in the - * list of modules taken from the WAR. - *

- * Currently, the behaviour specified is that a warning is generated only. - */ - private void checkForMissingModules() - { - // Get the IDs of all modules from the registry - Collection moduleIds = getRegistryModuleIDs(); - - // Check that each module is present in the distribution - for (String moduleId : moduleIds) - { - ModuleDetails moduleDetails = moduleService.getModule(moduleId); - if (moduleDetails != null) - { - if (logger.isDebugEnabled()) - { - logger.debug("Installed module found in distribution: " + moduleId); - } - } - else - { - // Get the specifics of the missing module - - VersionNumber versionCurrent = getVersion(moduleId); - // The module is missing, so warn - loggerService.warn(I18NUtil.getMessage(MSG_MISSING, moduleId, versionCurrent)); - } - } - } - - /** - * Copies, where necessary, the module registry details from the alias details - * and removes the alias details. - */ - private void renameModule(ModuleDetails module) - { - String moduleId = module.getId(); - List moduleAliases = module.getAliases(); - - // Get the IDs of all modules from the registry - RegistryKey moduleKeyAllIds = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, null); - Collection registeredModuleIds = registryService.getChildElements(moduleKeyAllIds); - - // Firstly, is the module installed? - if (registeredModuleIds.contains(moduleId)) - { - // It is there, so we do nothing - return; - } - // Check if any of the registered modules are on the alias list - for (String moduleAlias : moduleAliases) - { - // Is this alias registered? - if (!registeredModuleIds.contains(moduleAlias)) - { - // No alias registered - continue; - } - // We found an alias and have to rename it to the new module ID - RegistryKey moduleKeyNew = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleId, null); - RegistryKey moduleKeyOld = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleAlias, null); - // Copy it all - registryService.copy(moduleKeyOld, moduleKeyNew); - // Remove the source - registryService.delete(moduleKeyOld); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Moved old module alias to new module ID: \n" + - " Alias: " + moduleAlias + "\n" + - " Module: " + moduleId); - } - break; - } - } - - /** - * Does the actual work without fussing about transactions and authentication. - * Module dependencies will be started first, but a module will only be started - * once. - * - * @param module the module to start - * @param startedModules the IDs of modules that have already started - * @param executedComponents keep track of the executed components - */ - private void startModule(ModuleDetails module, Set startedModules, Set executedComponents) - { - String moduleId = module.getId(); - VersionNumber moduleNewVersion = module.getVersion(); - - // Double check whether we have done this module already - if (startedModules.contains(moduleId)) - { - if (logger.isDebugEnabled()) - { - logger.debug("Module '" + module + "' already started"); - } - return; - } - - // Start dependencies - List moduleDependencies = module.getDependencies(); - for (ModuleDependency moduleDependency : moduleDependencies) - { - if (logger.isDebugEnabled()) - { - logger.debug("Module '" + module + "' depends on: " + moduleDependency); - } - // Get the dependency - String moduleDependencyId = moduleDependency.getDependencyId(); - ModuleDetails moduleDependencyDetails = moduleService.getModule(moduleDependencyId); - // Check that it is there - if (moduleDependencyDetails == null) - { - // The dependency is not there - // List required dependencies - StringBuilder sb = new StringBuilder(128); - for (ModuleDependency dependency : moduleDependencies) - { - sb.append("\n").append(dependency); - } - String msg = I18NUtil.getMessage( - MSG_DEPENDENCIES, - moduleId, moduleNewVersion, sb.toString()); - logger.info(msg); - // Now fail - throw AlfrescoRuntimeException.create( - ERR_MISSING_DEPENDENCY, - moduleId, moduleNewVersion, moduleDependency); - } - // The dependency is installed, so start it - startModule(moduleDependencyDetails, startedModules, executedComponents); - } - - // Check if the module needs a rename first - renameModule(module); - - // First check that the module version is fundamentally compatible with the repository - VersionNumber repoVersionNumber = descriptorService.getServerDescriptor().getVersionNumber(); - VersionNumber minRepoVersionNumber = module.getRepoVersionMin(); - VersionNumber maxRepoVersionNumber = module.getRepoVersionMax(); - if ((minRepoVersionNumber != null && repoVersionNumber.compareTo(minRepoVersionNumber) < 0) || - (maxRepoVersionNumber != null && repoVersionNumber.compareTo(maxRepoVersionNumber) > 0)) - { - // The current repo version is not supported - throw AlfrescoRuntimeException.create( - ERR_UNSUPPORTED_REPO_VERSION, - moduleId, moduleNewVersion, repoVersionNumber, minRepoVersionNumber, maxRepoVersionNumber); - } - - // Get the module details from the registry - RegistryKey moduleKeyInstalledVersion = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_INSTALLED_VERSION); - RegistryKey moduleKeyCurrentVersion = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_CURRENT_VERSION); - VersionNumber moduleInstallVersion = (VersionNumber) registryService.getProperty(moduleKeyInstalledVersion); - VersionNumber moduleCurrentVersion = (VersionNumber) registryService.getProperty(moduleKeyCurrentVersion); - String msg = null; - if (moduleCurrentVersion == null) // No previous record of it - { - msg = I18NUtil.getMessage(MSG_INSTALLING, moduleId, moduleNewVersion); - // Record the install version - registryService.addProperty(moduleKeyInstalledVersion, moduleNewVersion); - moduleInstallVersion = moduleNewVersion; - moduleCurrentVersion = moduleNewVersion; - } - else // It is an upgrade or is the same - { - // Check that we have an installed version - if (moduleInstallVersion == null) - { - // A current version, but no installed version - logger.warn(I18NUtil.getMessage(WARN_NO_INSTALL_VERSION, moduleId, moduleCurrentVersion)); - // Record the install version - registryService.addProperty(moduleKeyInstalledVersion, moduleCurrentVersion); - moduleInstallVersion = moduleCurrentVersion; - } - - if (moduleCurrentVersion.compareTo(moduleNewVersion) == 0) // The current version is the same - { - msg = I18NUtil.getMessage(MSG_STARTING, moduleId, moduleNewVersion); - } - else if (moduleCurrentVersion.compareTo(moduleNewVersion) > 0) // Downgrading not supported - { - throw AlfrescoRuntimeException.create(ERR_NO_DOWNGRADE, moduleId, moduleCurrentVersion, moduleNewVersion); - } - else // This is an upgrade - { - msg = I18NUtil.getMessage(MSG_UPGRADING, moduleId, moduleNewVersion, moduleCurrentVersion); - } - } - loggerService.info(msg); - // Record the current version - registryService.addProperty(moduleKeyCurrentVersion, moduleNewVersion); - - Map componentsByName = getComponents(moduleId); - for (ModuleComponent component : componentsByName.values()) - { - executeComponent(moduleId, moduleNewVersion, component, executedComponents); - } - - // Keep track of the ID as it started successfully - startedModules.add(moduleId); - - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Started module '" + module + "' including " + executedComponents.size() + "components."); - } - } - - /** - * Execute the component, respecting dependencies. - */ - private void executeComponent( - String moduleId, - VersionNumber currentVersion, - ModuleComponent component, - Set executedComponents) - { - // Ignore if it has been executed in this run already - if (executedComponents.contains(component)) - { - // Already done - if (logger.isDebugEnabled()) - { - logger.debug("Skipping component already executed in this run: \n" + - " Component: " + component); - } - return; - } - // Keep track of the fact that we considered it for execution - executedComponents.add(component); - - // Check the version applicability - VersionNumber minVersion = component.getAppliesFromVersionNumber(); - VersionNumber maxVersion = component.getAppliesToVersionNumber(); - if (currentVersion.compareTo(minVersion) < 0 || currentVersion.compareTo(maxVersion) > 0) - { - // It is out of the allowable range for execution so we just ignore it - if (logger.isDebugEnabled()) - { - logger.debug("Skipping component that doesn't apply to the module installation version: \n" + - " Component: " + component + "\n" + - " Module: " + moduleId + "\n" + - " Current Version: " + currentVersion + "\n" + - " Applies From : " + minVersion + "\n" + - " Applies To : " + maxVersion); - } - return; - } - - // Construct the registry key to store the execution date - String name = component.getName(); - RegistryKey executionDateKey = new RegistryKey( - ModuleComponentHelper.URI_MODULES_1_0, - REGISTRY_PATH_MODULES, moduleId, REGISTRY_PATH_COMPONENTS, name, REGISTRY_PROPERTY_EXECUTION_DATE); - - // Check if the component has been executed - Date executionDate = (Date) registryService.getProperty(executionDateKey); - if (executionDate != null && component.isExecuteOnceOnly()) - { - // It has been executed and is scheduled for a single execution - leave it - if (logger.isDebugEnabled()) - { - logger.debug("Skipping already-executed module component: \n" + - " Component: " + component + "\n" + - " Execution Time: " + executionDate); - } - return; - } - // It may have been executed, but not in this run and it is allowed to be repeated - // Check for dependencies - List dependencies = component.getDependsOn(); - for (ModuleComponent dependency : dependencies) - { - executeComponent(moduleId, currentVersion, dependency, executedComponents); - } - // Execute the component itself - component.execute(); - // Keep track of it in the registry - registryService.addProperty(executionDateKey, new Date()); - // Done - if (logger.isDebugEnabled()) - { - logger.debug("Executed module component: \n" + - " Component: " + component); - } - } -} + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.module; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.repo.admin.registry.RegistryKey; +import org.alfresco.repo.admin.registry.RegistryService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.Tenant; +import org.alfresco.repo.tenant.TenantAdminService; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.module.ModuleDependency; +import org.alfresco.service.cmr.module.ModuleDetails; +import org.alfresco.service.cmr.module.ModuleService; +import org.alfresco.service.descriptor.DescriptorService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.VersionNumber; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Helper class to split up some of the code for managing module components. This class handles + * the execution of the module components. + * + * @author Derek Hulley + */ +public class ModuleComponentHelper +{ + public static final String URI_MODULES_1_0 = "http://www.alfresco.org/system/modules/1.0"; + private static final String REGISTRY_PATH_MODULES = "modules"; + private static final String REGISTRY_PROPERTY_INSTALLED_VERSION = "installedVersion"; + private static final String REGISTRY_PROPERTY_CURRENT_VERSION = "currentVersion"; + private static final String REGISTRY_PATH_COMPONENTS = "components"; + private static final String REGISTRY_PROPERTY_EXECUTION_DATE = "executionDate"; + + private static final String MSG_FOUND_MODULES = "module.msg.found_modules"; + private static final String MSG_STARTING = "module.msg.starting"; + private static final String MSG_INSTALLING = "module.msg.installing"; + private static final String MSG_UPGRADING = "module.msg.upgrading"; + private static final String MSG_DEPENDENCIES = "module.msg.dependencies"; + private static final String MSG_MISSING = "module.msg.missing"; + private static final String WARN_NO_INSTALL_VERSION = "module.warn.no_install_version"; + private static final String ERR_MISSING_DEPENDENCY = "module.err.missing_dependency"; + private static final String ERR_UNSUPPORTED_REPO_VERSION = "module.err.unsupported_repo_version"; + private static final String ERR_NO_DOWNGRADE = "module.err.downgrading_not_supported"; + private static final String ERR_COMPONENT_ALREADY_REGISTERED = "module.err.component_already_registered"; + private static final String ERR_COMPONENT_IN_MISSING_MODULE = "module.err.component_in_missing_module"; + private static final String ERR_ORPHANED_COMPONENTS = "module.err.orphaned_components"; + + private static Log logger = LogFactory.getLog(ModuleComponentHelper.class); + private static Log loggerService = LogFactory.getLog(ModuleServiceImpl.class); + + private ServiceRegistry serviceRegistry; + private DescriptorService descriptorService; + private RegistryService registryService; + private ModuleService moduleService; + private TenantAdminService tenantAdminService; + private boolean applyToTenants; + private Map> componentsByNameByModule; + + /** Default constructor */ + public ModuleComponentHelper() + { + componentsByNameByModule = new HashMap>(7); + } + + /** + * @param serviceRegistry provides access to the service APIs + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + /** + * @param descriptorService gives access to the current repository version + */ + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + /** + * @param registryService the service used to persist component execution details. + */ + public void setRegistryService(RegistryService registryService) + { + this.registryService = registryService; + } + + /** + * @param moduleService the service from which to get the available modules. + */ + public void setModuleService(ModuleService moduleService) + { + this.moduleService = moduleService; + } + + public void setTenantAdminService(TenantAdminService tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + + public void setApplyToTenants(boolean applyToTenants) + { + this.applyToTenants = applyToTenants; + } + + /** + * Add a managed module component to the registry of components. These will be controlled + * by the {@link #startModules()} method. + * + * @param component a module component to be executed + */ + public synchronized void registerComponent(ModuleComponent component) + { + String moduleId = component.getModuleId(); + String name = component.getName(); + // Get the map of components for the module + Map componentsByName = componentsByNameByModule.get(moduleId); + if (componentsByName == null) + { + componentsByName = new HashMap(11); + componentsByNameByModule.put(moduleId, componentsByName); + } + // Check if the component has already been registered + if (componentsByName.containsKey(name)) + { + throw AlfrescoRuntimeException.create(ERR_COMPONENT_ALREADY_REGISTERED, name, moduleId); + } + // Add it + componentsByName.put(name, component); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Registered component: " + component); + } + } + + /** + * @return Returns the map of components keyed by name. The map could be empty but + * will never be null. + */ + private synchronized Map getComponents(String moduleId) + { + Map componentsByName = componentsByNameByModule.get(moduleId); + if (componentsByName != null) + { + // Done + return componentsByName; + } + else + { + // Done + return Collections.emptyMap(); + } + } + + /** + * {@inheritDoc} + */ + public synchronized void startModules() + { + // Check properties + PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); + PropertyCheck.mandatory(this, "registryService", registryService); + PropertyCheck.mandatory(this, "moduleService", moduleService); + PropertyCheck.mandatory(this, "tenantAdminService", tenantAdminService); + /* + * Ensure transactionality and the correct authentication + */ + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + try + { + TransactionService transactionService = serviceRegistry.getTransactionService(); + + // Note: for system bootstrap this will be the default domain, else tenant domain for tenant create/import + final String tenantDomainCtx = tenantAdminService.getCurrentUserDomain(); + + if (tenantAdminService.isEnabled() && (! tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN)) && (! applyToTenants)) + { + // nothing to start (eg. when creating/importing tenant and applyToTenants = false) + return null; + } + + // Get all the modules + List modules = moduleService.getAllModules(); + loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); + + // Process each module in turn. Ordering is not important. + final Map> mapExecutedComponents = new HashMap>(1); + final Map> mapStartedModules = new HashMap>(1); + + mapExecutedComponents.put(tenantDomainCtx, new HashSet(10)); + mapStartedModules.put(tenantDomainCtx, new HashSet(2)); + + List tenantsNonFinal = null; + if (tenantAdminService.isEnabled()) + { + if (tenantDomainCtx.equals(TenantService.DEFAULT_DOMAIN) && applyToTenants) + { + tenantsNonFinal = tenantAdminService.getTenants(false); + for (Tenant tenant : tenantsNonFinal) + { + mapExecutedComponents.put(tenant.getTenantDomain(), new HashSet(10)); + mapStartedModules.put(tenant.getTenantDomain(), new HashSet(2)); + } + } + } + + final List tenants = tenantsNonFinal; + + for (final ModuleDetails module : modules) + { + RetryingTransactionCallback startModuleWork = new RetryingTransactionCallback() + { + public Object execute() throws Exception + { + startModule(module, mapStartedModules.get(tenantDomainCtx), mapExecutedComponents.get(tenantDomainCtx)); + + if (tenants != null) + { + for (Tenant tenant : tenants) + { + final String tenantDomain = tenant.getTenantDomain(); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + startModule(module, mapStartedModules.get(tenantDomain), mapExecutedComponents.get(tenantDomain)); + return null; + } + }, tenantDomain); + } + } + + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(startModuleWork, transactionService.isReadOnly()); + } + + // Check for missing modules. + checkForMissingModules(); + + if (tenants != null) + { + for (Tenant tenant : tenants) + { + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + checkForMissingModules(); + return null; + } + }, tenant.getTenantDomain()); + } + } + + // Check that all components where executed, or considered for execution + checkForOrphanComponents(mapExecutedComponents.get(tenantDomainCtx)); + + if (tenants != null) + { + for (Tenant tenant : tenants) + { + final String tenantDomain = tenant.getTenantDomain(); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + checkForOrphanComponents(mapExecutedComponents.get(tenantDomain)); + return null; + } + }, tenantDomain); + } + } + } + catch (Throwable e) + { + throw new AlfrescoRuntimeException("Failed to start modules", e); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * {@inheritDoc} + */ + public synchronized void shutdownModules() + { + // Check properties + PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry); + PropertyCheck.mandatory(this, "registryService", registryService); + PropertyCheck.mandatory(this, "moduleService", moduleService); + PropertyCheck.mandatory(this, "tenantAdminService", tenantAdminService); + + /* + * Ensure correct authentication + */ + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + // Get all the modules + List modules = moduleService.getAllModules(); + loggerService.info(I18NUtil.getMessage(MSG_FOUND_MODULES, modules.size())); + + for (ModuleDetails module : modules) + { + Map components = getComponents(module.getId()); + for (ModuleComponent component : components.values()) + { + component.shutdown(); + } + } + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + /** + * Checks that all components have been executed or considered for execution. + * @param executedComponents + */ + private void checkForOrphanComponents(Set executedComponents) + { + Set missedComponents = new HashSet(executedComponents); + + // Iterate over each module registered by components + for (Map.Entry> entry : componentsByNameByModule.entrySet()) + { + String moduleId = entry.getKey(); + Map componentsByName = entry.getValue(); + // Iterate over each component registered against the module ID + for (Map.Entry entryInner : componentsByName.entrySet()) + { + String componentName = entryInner.getKey(); + ModuleComponent component = entryInner.getValue(); + // Check if it has been executed + if (executedComponents.contains(component)) + { + // It was executed, so remove it from the missed components set + missedComponents.remove(component); + } + else + { + String msg = I18NUtil.getMessage( + ERR_COMPONENT_IN_MISSING_MODULE, + componentName, moduleId); + logger.error(msg); + } + } + } + // Dump if there were orphans + if (missedComponents.size() > 0) + { + throw AlfrescoRuntimeException.create(ERR_ORPHANED_COMPONENTS, missedComponents.size()); + } + } + + /** + * Gets a list of all registered modules. + * + * @return A Collection of module IDs + */ + Collection getRegistryModuleIDs() + { + // Get the IDs of all modules from the registry + RegistryKey moduleKeyAllIds = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, null); + + return registryService.getChildElements(moduleKeyAllIds); + } + + /** + * Returns the version number of a module from the Registry. + * + * @param moduleId + * @return + */ + VersionNumber getVersion(String moduleId) + { + RegistryKey moduleKeyCurrentVersion = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_CURRENT_VERSION); + VersionNumber versionCurrent = (VersionNumber) registryService.getProperty(moduleKeyCurrentVersion); + return versionCurrent; + } + + /** + * Checks to see if there are any modules registered as installed that aren't in the + * list of modules taken from the WAR. + *

+ * Currently, the behaviour specified is that a warning is generated only. + */ + private void checkForMissingModules() + { + // Get the IDs of all modules from the registry + Collection moduleIds = getRegistryModuleIDs(); + + // Check that each module is present in the distribution + for (String moduleId : moduleIds) + { + ModuleDetails moduleDetails = moduleService.getModule(moduleId); + if (moduleDetails != null) + { + if (logger.isDebugEnabled()) + { + logger.debug("Installed module found in distribution: " + moduleId); + } + } + else + { + // Get the specifics of the missing module + + VersionNumber versionCurrent = getVersion(moduleId); + // The module is missing, so warn + loggerService.warn(I18NUtil.getMessage(MSG_MISSING, moduleId, versionCurrent)); + } + } + } + + /** + * Copies, where necessary, the module registry details from the alias details + * and removes the alias details. + */ + private void renameModule(ModuleDetails module) + { + String moduleId = module.getId(); + List moduleAliases = module.getAliases(); + + // Get the IDs of all modules from the registry + RegistryKey moduleKeyAllIds = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, null); + Collection registeredModuleIds = registryService.getChildElements(moduleKeyAllIds); + + // Firstly, is the module installed? + if (registeredModuleIds.contains(moduleId)) + { + // It is there, so we do nothing + return; + } + // Check if any of the registered modules are on the alias list + for (String moduleAlias : moduleAliases) + { + // Is this alias registered? + if (!registeredModuleIds.contains(moduleAlias)) + { + // No alias registered + continue; + } + // We found an alias and have to rename it to the new module ID + RegistryKey moduleKeyNew = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleId, null); + RegistryKey moduleKeyOld = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleAlias, null); + // Copy it all + registryService.copy(moduleKeyOld, moduleKeyNew); + // Remove the source + registryService.delete(moduleKeyOld); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Moved old module alias to new module ID: \n" + + " Alias: " + moduleAlias + "\n" + + " Module: " + moduleId); + } + break; + } + } + + /** + * Does the actual work without fussing about transactions and authentication. + * Module dependencies will be started first, but a module will only be started + * once. + * + * @param module the module to start + * @param startedModules the IDs of modules that have already started + * @param executedComponents keep track of the executed components + */ + private void startModule(ModuleDetails module, Set startedModules, Set executedComponents) + { + String moduleId = module.getId(); + VersionNumber moduleNewVersion = module.getVersion(); + + // Double check whether we have done this module already + if (startedModules.contains(moduleId)) + { + if (logger.isDebugEnabled()) + { + logger.debug("Module '" + module + "' already started"); + } + return; + } + + // Start dependencies + List moduleDependencies = module.getDependencies(); + for (ModuleDependency moduleDependency : moduleDependencies) + { + if (logger.isDebugEnabled()) + { + logger.debug("Module '" + module + "' depends on: " + moduleDependency); + } + // Get the dependency + String moduleDependencyId = moduleDependency.getDependencyId(); + ModuleDetails moduleDependencyDetails = moduleService.getModule(moduleDependencyId); + // Check that it is there + if (moduleDependencyDetails == null) + { + // The dependency is not there + // List required dependencies + StringBuilder sb = new StringBuilder(128); + for (ModuleDependency dependency : moduleDependencies) + { + sb.append("\n").append(dependency); + } + String msg = I18NUtil.getMessage( + MSG_DEPENDENCIES, + moduleId, moduleNewVersion, sb.toString()); + logger.info(msg); + // Now fail + throw AlfrescoRuntimeException.create( + ERR_MISSING_DEPENDENCY, + moduleId, moduleNewVersion, moduleDependency); + } + // The dependency is installed, so start it + startModule(moduleDependencyDetails, startedModules, executedComponents); + } + + // Check if the module needs a rename first + renameModule(module); + + // First check that the module version is fundamentally compatible with the repository + VersionNumber repoVersionNumber = descriptorService.getServerDescriptor().getVersionNumber(); + VersionNumber minRepoVersionNumber = module.getRepoVersionMin(); + VersionNumber maxRepoVersionNumber = module.getRepoVersionMax(); + if ((minRepoVersionNumber != null && repoVersionNumber.compareTo(minRepoVersionNumber) < 0) || + (maxRepoVersionNumber != null && repoVersionNumber.compareTo(maxRepoVersionNumber) > 0)) + { + // The current repo version is not supported + throw AlfrescoRuntimeException.create( + ERR_UNSUPPORTED_REPO_VERSION, + moduleId, moduleNewVersion, repoVersionNumber, minRepoVersionNumber, maxRepoVersionNumber); + } + + // Get the module details from the registry + RegistryKey moduleKeyInstalledVersion = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_INSTALLED_VERSION); + RegistryKey moduleKeyCurrentVersion = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleId, REGISTRY_PROPERTY_CURRENT_VERSION); + VersionNumber moduleInstallVersion = (VersionNumber) registryService.getProperty(moduleKeyInstalledVersion); + VersionNumber moduleCurrentVersion = (VersionNumber) registryService.getProperty(moduleKeyCurrentVersion); + String msg = null; + if (moduleCurrentVersion == null) // No previous record of it + { + msg = I18NUtil.getMessage(MSG_INSTALLING, moduleId, moduleNewVersion); + // Record the install version + registryService.addProperty(moduleKeyInstalledVersion, moduleNewVersion); + moduleInstallVersion = moduleNewVersion; + moduleCurrentVersion = moduleNewVersion; + } + else // It is an upgrade or is the same + { + // Check that we have an installed version + if (moduleInstallVersion == null) + { + // A current version, but no installed version + logger.warn(I18NUtil.getMessage(WARN_NO_INSTALL_VERSION, moduleId, moduleCurrentVersion)); + // Record the install version + registryService.addProperty(moduleKeyInstalledVersion, moduleCurrentVersion); + moduleInstallVersion = moduleCurrentVersion; + } + + if (moduleCurrentVersion.compareTo(moduleNewVersion) == 0) // The current version is the same + { + msg = I18NUtil.getMessage(MSG_STARTING, moduleId, moduleNewVersion); + } + else if (moduleCurrentVersion.compareTo(moduleNewVersion) > 0) // Downgrading not supported + { + throw AlfrescoRuntimeException.create(ERR_NO_DOWNGRADE, moduleId, moduleCurrentVersion, moduleNewVersion); + } + else // This is an upgrade + { + msg = I18NUtil.getMessage(MSG_UPGRADING, moduleId, moduleNewVersion, moduleCurrentVersion); + } + } + loggerService.info(msg); + // Record the current version + registryService.addProperty(moduleKeyCurrentVersion, moduleNewVersion); + + Map componentsByName = getComponents(moduleId); + for (ModuleComponent component : componentsByName.values()) + { + executeComponent(moduleId, moduleNewVersion, component, executedComponents); + } + + // Keep track of the ID as it started successfully + startedModules.add(moduleId); + + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Started module '" + module + "' including " + executedComponents.size() + "components."); + } + } + + /** + * Execute the component, respecting dependencies. + */ + private void executeComponent( + String moduleId, + VersionNumber currentVersion, + ModuleComponent component, + Set executedComponents) + { + // Ignore if it has been executed in this run already + if (executedComponents.contains(component)) + { + // Already done + if (logger.isDebugEnabled()) + { + logger.debug("Skipping component already executed in this run: \n" + + " Component: " + component); + } + return; + } + // Keep track of the fact that we considered it for execution + executedComponents.add(component); + + // Check the version applicability + VersionNumber minVersion = component.getAppliesFromVersionNumber(); + VersionNumber maxVersion = component.getAppliesToVersionNumber(); + if (currentVersion.compareTo(minVersion) < 0 || currentVersion.compareTo(maxVersion) > 0) + { + // It is out of the allowable range for execution so we just ignore it + if (logger.isDebugEnabled()) + { + logger.debug("Skipping component that doesn't apply to the module installation version: \n" + + " Component: " + component + "\n" + + " Module: " + moduleId + "\n" + + " Current Version: " + currentVersion + "\n" + + " Applies From : " + minVersion + "\n" + + " Applies To : " + maxVersion); + } + return; + } + + // Construct the registry key to store the execution date + String name = component.getName(); + RegistryKey executionDateKey = new RegistryKey( + ModuleComponentHelper.URI_MODULES_1_0, + REGISTRY_PATH_MODULES, moduleId, REGISTRY_PATH_COMPONENTS, name, REGISTRY_PROPERTY_EXECUTION_DATE); + + // Check if the component has been executed + Date executionDate = (Date) registryService.getProperty(executionDateKey); + if (executionDate != null && component.isExecuteOnceOnly()) + { + // It has been executed and is scheduled for a single execution - leave it + if (logger.isDebugEnabled()) + { + logger.debug("Skipping already-executed module component: \n" + + " Component: " + component + "\n" + + " Execution Time: " + executionDate); + } + return; + } + // It may have been executed, but not in this run and it is allowed to be repeated + // Check for dependencies + List dependencies = component.getDependsOn(); + for (ModuleComponent dependency : dependencies) + { + executeComponent(moduleId, currentVersion, dependency, executedComponents); + } + // Execute the component itself + component.execute(); + // Keep track of it in the registry + registryService.addProperty(executionDateKey, new Date()); + // Done + if (logger.isDebugEnabled()) + { + logger.debug("Executed module component: \n" + + " Component: " + component); + } + } +} diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java b/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java index cb3f342469..b057ca05ec 100644 --- a/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java +++ b/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java @@ -145,7 +145,7 @@ public class ModuleComponentHelperTest extends BaseAlfrescoTestCase int tenantCount = 0; if (tenantDeployerService.isEnabled()) { - tenantCount = tenantDeployerService.getAllTenants().size(); + tenantCount = tenantDeployerService.getTenants(true).size(); } // Check assertEquals( diff --git a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java index f71b0fa64d..44e2895b70 100644 --- a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java +++ b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java @@ -27,10 +27,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.TreeMap; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.admin.registry.RegistryKey; import org.alfresco.repo.admin.registry.RegistryService; import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.service.ServiceRegistry; @@ -46,7 +44,6 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.extensions.surf.util.I18NUtil; /** * This component controls the execution of @@ -118,10 +115,7 @@ public class ModuleServiceImpl implements ApplicationContextAware, ModuleService this.moduleComponentHelper.setApplyToTenants(applyToTenants); } - - /* (non-Javadoc) - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.resolver = applicationContext; diff --git a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java index 9abe8b30c4..7cbf3f80ed 100644 --- a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java +++ b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java @@ -113,7 +113,7 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC if ((currentUser == null) || (tenantService.getBaseNameUser(currentUser).equals(AuthenticationUtil.getSystemUserName()))) { // return enabled stores across all tenants, if running as system/null user, for example, ContentStoreCleaner scheduled job - List tenants = tenantAdminDAO.listTenants(); + List tenants = tenantAdminDAO.listTenants(false); for (TenantEntity tenant : tenants) { if (tenant.getEnabled()) diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index 61ff1e66a8..1b3e134fa4 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -850,7 +850,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo @Deprecated public List getTenants(boolean enabledOnly) { - List tenantEntities = tenantAdminDAO.listTenants(); + List tenantEntities = tenantAdminDAO.listTenants(enabledOnly); List tenants = new ArrayList(tenantEntities.size()); for (TenantEntity tenantEntity : tenantEntities) {