diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index b6e104e713..9a2dd30f0b 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -278,6 +278,7 @@ + @@ -314,7 +315,7 @@ - ${server.transaction.allow-writes} + ${create.missing.people} diff --git a/config/alfresco/authority-services-context.xml b/config/alfresco/authority-services-context.xml index 23ccb5d757..bc0c6e4a60 100644 --- a/config/alfresco/authority-services-context.xml +++ b/config/alfresco/authority-services-context.xml @@ -88,6 +88,9 @@ + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index daf493795f..a58806787c 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -793,10 +793,10 @@ - ${spaces.store} /${spaces.company_home.childname} + diff --git a/config/alfresco/bootstrap/categoriesEmptyRoot.xml b/config/alfresco/bootstrap/categoriesEmptyRoot.xml new file mode 100644 index 0000000000..b7bbabcf28 --- /dev/null +++ b/config/alfresco/bootstrap/categoriesEmptyRoot.xml @@ -0,0 +1,15 @@ + + + + categories + + + General + + + + + + \ No newline at end of file diff --git a/config/alfresco/bootstrap/webScriptsNoSamples.xml b/config/alfresco/bootstrap/webScriptsNoSamples.xml new file mode 100644 index 0000000000..f9550b2d75 --- /dev/null +++ b/config/alfresco/bootstrap/webScriptsNoSamples.xml @@ -0,0 +1,34 @@ + + + + + + + + ${webscripts.url_addressable_web_services} + space-icon-default + ${webscripts.url_addressable_web_services} + ${webscripts.web_scripts} + + + + + + + + + + + true + ${webscripts.what_are_web_scripts} + contentUrl=classpath:alfresco/bootstrap/webscripts/readme.html|mimetype=text/html|size=|encoding=UTF-8|locale=en_US_ + + + readme.html + + + + + + + \ No newline at end of file diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index d53fb654b4..25d03b122a 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -432,7 +432,7 @@ - + @@ -457,8 +457,19 @@ - + + + + + + + + + + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index de23c78b72..9c0da6bbcb 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -238,10 +238,12 @@ - - + + + + - + ${db.driver} diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index ca88cd2fdd..9aa2f29eb8 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -40,9 +40,6 @@ - - - @@ -93,9 +90,6 @@ - - - diff --git a/config/alfresco/mt/mt-base-context.xml b/config/alfresco/mt/mt-base-context.xml index 217a420718..562a9fb310 100644 --- a/config/alfresco/mt/mt-base-context.xml +++ b/config/alfresco/mt/mt-base-context.xml @@ -33,6 +33,14 @@ + + + + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index f721a960d3..5d1be6df72 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -513,6 +513,13 @@ system.usages.updateBatchSize=50 # Repository endpoint - used by Activity Service repo.remote.endpoint=/service +# Some authentication mechanisms may need to create people +# in the repository on demand. This enables that feature. +# If disabled an error will be generated for missing +# people. If enabled then a person will be created and +# persisted. +create.missing.people=${server.transaction.allow-writes} + # Create home folders as people are created (true) or create them lazily (false) home.folder.creation.eager=true @@ -979,6 +986,7 @@ cache.immutableSingletonSharedCache.maxItems=12000 cache.remoteAlfrescoTicketService.ticketsCache.maxItems=1000 cache.contentDiskDriver.fileInfoCache.maxItems=1000 cache.globalConfigSharedCache.maxItems=1000 +cache.siteNodeRefSharedCache.maxItems=5000 # # Download Service Limits, in bytes diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml index 5409b4f5f1..c5d0751f00 100644 --- a/config/alfresco/site-services-context.xml +++ b/config/alfresco/site-services-context.xml @@ -137,18 +137,16 @@ - + - ./${spaces.company_home.childname}/st:sites - @@ -156,6 +154,9 @@ + + + diff --git a/config/alfresco/tx-cache-context.xml b/config/alfresco/tx-cache-context.xml index 0a75fedd9c..1b3e3bedf9 100644 --- a/config/alfresco/tx-cache-context.xml +++ b/config/alfresco/tx-cache-context.xml @@ -563,7 +563,7 @@ org.alfresco.tenantsTransactionalCache - + @@ -583,4 +583,19 @@ + + + + + + + + + org.alfresco.cache.siteNodeRefTransactionalCache + + + + + + diff --git a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java index a17eeb7455..24bd8be62d 100644 --- a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java +++ b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java @@ -536,7 +536,16 @@ public abstract class CMISAbstractDictionaryService extends AbstractLifecycleBea protected void onBootstrap(ApplicationEvent event) { afterDictionaryInit(); - dictionaryDAO.register(this); + + // TODO revisit (for KS and/or 1.1) + if (dictionaryDAO != null) + { + dictionaryDAO.register(this); + } + else + { + logger.error("DictionaryDAO is null - hence CMIS Dictionary not registered for updates"); + } } /* diff --git a/source/java/org/alfresco/cmis/dictionary/CMISStrictDictionaryService.java b/source/java/org/alfresco/cmis/dictionary/CMISStrictDictionaryService.java index 5f3daa08b9..d59a39c60b 100644 --- a/source/java/org/alfresco/cmis/dictionary/CMISStrictDictionaryService.java +++ b/source/java/org/alfresco/cmis/dictionary/CMISStrictDictionaryService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -106,6 +106,17 @@ public class CMISStrictDictionaryService extends CMISAbstractDictionaryService { CMISTypeId typeId = cmisMapping.getCmisTypeId(CMISScope.RELATIONSHIP, CMISMapping.RELATIONSHIP_QNAME); ClassDefinition classDef = dictionaryService.getClass(cmisMapping.getCmisType(typeId.getQName())); + + // from Thor + if (classDef == null) + { + if (classQNames.size() != 0) + { + logger.warn("Unexpected - no class for "+cmisMapping.getCmisType(typeId.getQName())+" - cannot create assocDefs for: "+classQNames); + } + return; + } + CMISAbstractTypeDefinition objectTypeDef = new CMISRelationshipTypeDefinition(cmisMapping, typeId, dictionaryService, classDef, null); registry.registerTypeDefinition(objectTypeDef); diff --git a/source/java/org/alfresco/repo/activities/feed/FeedNotifierJob.java b/source/java/org/alfresco/repo/activities/feed/FeedNotifierJob.java index af79b56791..c3e2018c61 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedNotifierJob.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedNotifierJob.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -24,6 +24,8 @@ 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.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; @@ -74,15 +76,14 @@ public class FeedNotifierJob implements Job List tenants = tenantAdminService.getAllTenants(); for (Tenant tenant : tenants) { - String tenantDomain = tenant.getTenantDomain(); - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() throws Exception { feedNotifier.execute(repeatIntervalMins); return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenant.getTenantDomain()); } } } diff --git a/source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java b/source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java index 8740ad05a9..c8dc9a2a1b 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java @@ -238,9 +238,8 @@ public class FeedNotifierTest /** * ALF-16155 test */ - // TODO fix CLOUD-1348 - //@Test - public void xtestFailedNotifications() + @Test + public void testFailedNotifications() { AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); diff --git a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java index 965c363ce6..ea3e5af168 100644 --- a/source/java/org/alfresco/repo/admin/ConfigurationChecker.java +++ b/source/java/org/alfresco/repo/admin/ConfigurationChecker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -227,6 +227,12 @@ public class ConfigurationChecker extends AbstractLifecycleBean { String msg = I18NUtil.getMessage(ERR_MISSING_INDEXES, missingStoreIndexes); logger.error(msg); + + for (StoreRef missingIndexStoreRef : missingIndexStoreRefs) + { + logger.error("---> "+missingIndexStoreRef); + } + String msgRecover = I18NUtil.getMessage(MSG_HOWTO_INDEX_RECOVER); logger.info(msgRecover); } diff --git a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java index ef47fed96b..1f5f4e8e36 100644 --- a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -25,13 +25,13 @@ import java.util.Date; import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; -import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationContext; 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.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.admin.PatchException; @@ -43,6 +43,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.extensions.surf.util.I18NUtil; /** * Base implementation of the patch. This class ensures that the patch is thread- and transaction-safe. @@ -403,24 +404,24 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher { // downgrade integrity checking IntegrityChecker.setWarnInTransaction(); - + String report = applyInternal(); - if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants) + if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants) { - List tenants = tenantAdminService.getAllTenants(); + List tenants = tenantAdminService.getAllTenants(); for (Tenant tenant : tenants) - { - String tenantDomain = tenant.getTenantDomain(); - String tenantReport = AuthenticationUtil.runAs(new RunAsWork() + { + String tenantDomain = tenant.getTenantDomain(); + String tenantReport = TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - public String doWork() throws Exception + public String doWork() throws Exception { - return applyInternal(); + return applyInternal(); } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - - report = report + "\n" + tenantReport + " (for tenant: " + tenantDomain + ")"; + }, tenantDomain); + + report = report + "\n" + tenantReport + " (for tenant: " + tenantDomain + ")"; } return report; diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AuthorityMigrationPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AuthorityMigrationPatch.java index 9877b268c8..00fe3b57d4 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AuthorityMigrationPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AuthorityMigrationPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -290,9 +290,9 @@ public class AuthorityMigrationPatch extends AbstractPatch // Disable rules ruleService.disableRules(); // Authentication - String systemUser = AuthenticationUtil.getSystemUserName(); - systemUser = tenantAdminService.getDomainUser(systemUser, tenantDomain); - AuthenticationUtil.setRunAsUser(systemUser); + // TODO tenant switch + String tenantSystemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); + AuthenticationUtil.setRunAsUser(tenantSystemUser); } public void afterProcess() throws Throwable diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index 54b36b7018..5b8de39c0c 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -751,6 +751,9 @@ public class SchemaBootstrap extends AbstractLifecycleBean try { stmt.executeUpdate("drop table alf_bootstrap_lock"); + + // from Thor + executedStatementsThreadLocal.set(null); } catch (Throwable e) { @@ -791,6 +794,8 @@ public class SchemaBootstrap extends AbstractLifecycleBean if (create) { + long start = System.currentTimeMillis(); + // execute pre-create scripts (not patches) for (String scriptUrl : this.preCreateScriptUrls) { @@ -828,6 +833,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean { executeScriptUrl(cfg, connection, scriptUrl); } + + if (logger.isInfoEnabled()) + { + logger.info("Create scripts executed in "+(System.currentTimeMillis()-start)+" ms"); + } } else { @@ -1478,12 +1488,16 @@ public class SchemaBootstrap extends AbstractLifecycleBean SchemaBootstrap.setMaxStringLength(maximumStringLength); } } - + @Override - protected synchronized void onBootstrap(ApplicationEvent event) + public synchronized void onBootstrap(ApplicationEvent event) { - // Use the application context to load resources - rpr = (ApplicationContext)event.getSource(); + // from Thor + if (event != null) + { + // Use the application context to load resources + rpr = (ApplicationContext)event.getSource(); + } // do everything in a transaction Session session = getSessionFactory().openSession(); @@ -1602,9 +1616,12 @@ public class SchemaBootstrap extends AbstractLifecycleBean // Reset the configuration cfg.setProperty(Environment.CONNECTION_PROVIDER, defaultConnectionProviderFactoryClass); - - // all done successfully - ((ApplicationContext) event.getSource()).publishEvent(new SchemaAvailableEvent(this)); + + if (event != null) + { + // all done successfully + ((ApplicationContext) event.getSource()).publishEvent(new SchemaAvailableEvent(this)); + } } catch (BootstrapStopException e) { diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java index 44bc128ad0..eeb987f7e4 100644 --- a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java +++ b/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -33,7 +33,7 @@ import org.springframework.context.ApplicationContext; * @see TenantAdminDAO * * @author janv - * @since 4.0 (thor) + * @since 4.0 */ public class TenantAdminDAOTest extends TestCase { @@ -138,6 +138,10 @@ public class TenantAdminDAOTest extends TestCase TenantEntity tenantEntity= getTenant(tenantDomain); assertNull(tenantEntity); + TenantEntity newTenantEntity = new TenantEntity(); + newTenantEntity.setTenantDomain(tenantDomain); + newTenantEntity.setEnabled(false); + TenantEntity createTenantEntity = createTenant(tenantDomain, false); assertNotNull(createTenantEntity); @@ -153,6 +157,10 @@ public class TenantAdminDAOTest extends TestCase { final String tenantDomain = getName() + "-" + System.currentTimeMillis(); + final TenantEntity newTenantEntity = new TenantEntity(); + newTenantEntity.setTenantDomain(tenantDomain); + newTenantEntity.setEnabled(false); + RetryingTransactionCallback callback = new RetryingTransactionCallback() { public Void execute() throws Throwable @@ -192,6 +200,12 @@ public class TenantAdminDAOTest extends TestCase assertEquals(createTenantEntity, tenantUpdateEntity); assertFalse(tenantUpdateEntity.getEnabled()); + assertEquals(createTenantEntity.getTenantDomain(), tenantUpdateEntity.getTenantDomain()); + assertEquals(createTenantEntity.getEnabled(), tenantUpdateEntity.getEnabled()); + assertEquals(createTenantEntity.getTenantName(), tenantUpdateEntity.getTenantName()); + assertEquals(createTenantEntity.getContentRoot(), tenantUpdateEntity.getContentRoot()); + assertEquals(createTenantEntity.getDbUrl(), tenantUpdateEntity.getDbUrl()); + tenantUpdateEntity.setEnabled(true); updateTenant(tenantUpdateEntity); @@ -199,6 +213,12 @@ public class TenantAdminDAOTest extends TestCase assertNotNull(tenantEntity); assertTrue(tenantEntity.getEnabled()); + assertEquals(tenantEntity.getTenantDomain(), tenantUpdateEntity.getTenantDomain()); + assertEquals(tenantEntity.getEnabled(), tenantUpdateEntity.getEnabled()); + assertEquals(tenantEntity.getTenantName(), tenantUpdateEntity.getTenantName()); + assertEquals(tenantEntity.getContentRoot(), tenantUpdateEntity.getContentRoot()); + assertEquals(tenantEntity.getDbUrl(), tenantUpdateEntity.getDbUrl()); + deleteTenant(tenantDomain); assertNull(getTenant(tenantDomain)); diff --git a/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java b/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java index d5d6ef425e..f3020e14be 100644 --- a/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java +++ b/source/java/org/alfresco/repo/i18n/MessageServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -45,6 +45,8 @@ import org.alfresco.repo.dictionary.RepositoryLocation; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; @@ -770,7 +772,7 @@ public class MessageServiceImpl implements MessageService logger.debug("Resetting messages ..."); } - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() { @@ -783,8 +785,8 @@ public class MessageServiceImpl implements MessageService } return null; - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + }, tenantDomain); if (logger.isDebugEnabled()) { diff --git a/source/java/org/alfresco/repo/importer/ImporterComponent.java b/source/java/org/alfresco/repo/importer/ImporterComponent.java index cf0458da95..4f1c3c859f 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponent.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -35,7 +35,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.importer.view.NodeContext; import org.alfresco.repo.model.filefolder.HiddenAspect; import org.alfresco.repo.policy.BehaviourFilter; -import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.usage.ContentUsageImpl; import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.common.VersionUtil; @@ -87,8 +87,7 @@ import org.xml.sax.ContentHandler; * * @author David Caruana */ -public class ImporterComponent - implements ImporterService +public class ImporterComponent implements ImporterService { // Logger private static final Log logger = LogFactory.getLog(ImporterComponent.class); @@ -107,7 +106,6 @@ public class ImporterComponent private RuleService ruleService; private PermissionService permissionService; private AuthorityService authorityService; - private AuthenticationContext authenticationContext; private OwnableService ownableService; private VersionService versionService; private HiddenAspect hiddenAspect; @@ -205,14 +203,6 @@ public class ImporterComponent { this.authorityService = authorityService; } - - /** - * @param authenticationContext authenticationContext - */ - public void setAuthenticationContext(AuthenticationContext authenticationContext) - { - this.authenticationContext = authenticationContext; - } /** * @param ownableService ownableService @@ -1417,7 +1407,8 @@ public class ImporterComponent NodeRef nodeRef = assocRef.getChildRef(); // Note: non-admin authorities take ownership of new nodes - if (!(authenticationContext.isCurrentUserTheSystemUser() || authorityService.hasAdminAuthority())) + // from Thor + if (!(AuthenticationUtil.isRunAsUserTheSystemUser() || authorityService.hasAdminAuthority())) { ownableService.takeOwnership(nodeRef); } @@ -1425,7 +1416,9 @@ public class ImporterComponent // apply permissions List permissions = null; AccessStatus writePermission = permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS); - if (authenticationContext.isCurrentUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) + + // from Thor + if (AuthenticationUtil.isRunAsUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) { permissions = bindPermissions(node.getAccessControlEntries()); @@ -1603,7 +1596,9 @@ public class ImporterComponent // Apply permissions List permissions = null; AccessStatus writePermission = permissionService.hasPermission(existingNodeRef, PermissionService.CHANGE_PERMISSIONS); - if (authenticationContext.isCurrentUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) + + // from Thor + if (AuthenticationUtil.isRunAsUserTheSystemUser() || writePermission.equals(AccessStatus.ALLOWED)) { boolean inheritPermissions = node.getInheritPermissions(); if (!inheritPermissions) diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java index c4e3de0d62..da75ecd8a9 100644 --- a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java +++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -35,11 +35,11 @@ import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.PasswordGenerator; import org.alfresco.repo.security.authentication.UserNameGenerator; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.repo.workflow.CancelWorkflowActionExecuter; @@ -1064,6 +1064,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli final String finalUserName = inviteeUserName; AuthenticationUtil.runAs(new RunAsWork() { + @SuppressWarnings("synthetic-access") public Object doWork() throws Exception { NodeRef person = personService.createPerson(properties); @@ -1265,6 +1266,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli String inviteePassword = created ? AuthenticationUtil.runAs(new RunAsWork() { + @SuppressWarnings("synthetic-access") public String doWork() { return createInviteeDisabledAccount(initeeUserNameFinal); @@ -1452,6 +1454,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli // Run as system user AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { + @SuppressWarnings("synthetic-access") public Object doWork() throws Exception { QName type = nodeService.getType(siteRef); diff --git a/source/java/org/alfresco/repo/model/Repository.java b/source/java/org/alfresco/repo/model/Repository.java index 616b185b06..eeb0b6adee 100644 --- a/source/java/org/alfresco/repo/model/Repository.java +++ b/source/java/org/alfresco/repo/model/Repository.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -21,17 +21,13 @@ package org.alfresco.repo.model; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.tenant.TenantAdminService; -import org.alfresco.repo.tenant.TenantDeployer; -import org.alfresco.repo.tenant.TenantUtil; -import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; @@ -51,7 +47,6 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; import org.springframework.extensions.surf.util.AbstractLifecycleBean; /** @@ -59,10 +54,10 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; * * @author davidc */ -public class Repository implements ApplicationContextAware, ApplicationListener, TenantDeployer +public class Repository implements ApplicationContextAware { private ProcessorLifecycle lifecycle = new ProcessorLifecycle(); - + // dependencies private RetryingTransactionHelper retryingTransactionHelper; private NamespaceService namespaceService; @@ -71,13 +66,14 @@ public class Repository implements ApplicationContextAware, ApplicationListener, private FileFolderService fileFolderService; private PersonService personService; private AVMService avmService; - private TenantAdminService tenantAdminService; // company home - private StoreRef companyHomeStore; - private String companyHomePath; - final private Map companyHomeRefs = new ConcurrentHashMap(); - final private Map rootRefs = new ConcurrentHashMap(); + private StoreRef companyHomeStore; // ie. workspace://SpaceStore + private String companyHomePath; // ie. /app:company_home + + // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + private SimpleCache singletonCache; // eg. for companyHomeNodeRef + private final String KEY_COMPANYHOME_NODEREF = "key.companyhome.noderef"; /** @@ -99,7 +95,12 @@ public class Repository implements ApplicationContextAware, ApplicationListener, { this.companyHomePath = companyHomePath; } - + + public void setSingletonCache(SimpleCache singletonCache) + { + this.singletonCache = singletonCache; + } + /** * Sets helper that provides transaction callbacks */ @@ -148,14 +149,6 @@ public class Repository implements ApplicationContextAware, ApplicationListener, this.personService = personService; } - /** - * Sets the tenant admin service - */ - public void setTenantAdminService(TenantAdminService tenantAdminService) - { - this.tenantAdminService = tenantAdminService; - } - /** * Sets the AVM service */ @@ -184,21 +177,19 @@ public class Repository implements ApplicationContextAware, ApplicationListener, { initContext(); } - + @Override protected void onShutdown(ApplicationEvent event) { } } - + /** * Initialise Repository Context */ protected void initContext() { - tenantAdminService.register(this); - - getCompanyHome(); + //NOOP } /** @@ -208,22 +199,10 @@ public class Repository implements ApplicationContextAware, ApplicationListener, */ public NodeRef getRootHome() { - String tenantDomain = tenantAdminService.getCurrentUserDomain(); - NodeRef rootRef = rootRefs.get(tenantDomain); - if (rootRef == null) - { - rootRef = TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public NodeRef doWork() throws Exception - { - return nodeService.getRootNode(companyHomeStore); - } - }, tenantDomain); - rootRefs.put(tenantDomain, rootRef); - } - return rootRef; + // note: store root nodes are cached by the NodeDAO + return nodeService.getRootNode(companyHomeStore); } - + /** * Gets the Company Home * @@ -231,11 +210,10 @@ public class Repository implements ApplicationContextAware, ApplicationListener, */ public NodeRef getCompanyHome() { - String tenantDomain = tenantAdminService.getCurrentUserDomain(); - NodeRef companyHomeRef = companyHomeRefs.get(tenantDomain); + NodeRef companyHomeRef = singletonCache.get(KEY_COMPANYHOME_NODEREF); if (companyHomeRef == null) { - companyHomeRef = TenantUtil.runAsSystemTenant(new TenantRunAsWork() + companyHomeRef = AuthenticationUtil.runAs(new RunAsWork() { public NodeRef doWork() throws Exception { @@ -247,13 +225,14 @@ public class Repository implements ApplicationContextAware, ApplicationListener, if (refs.size() != 1) { throw new IllegalStateException("Invalid company home path: " + companyHomePath + " - found: " + refs.size()); + } + return refs.get(0); } - return refs.get(0); - } - }, true); - } - }, tenantDomain); - companyHomeRefs.put(tenantDomain, companyHomeRef); + }, true); + } + }, AuthenticationUtil.getSystemUserName()); + + singletonCache.put(KEY_COMPANYHOME_NODEREF, companyHomeRef); } return companyHomeRef; } @@ -460,25 +439,5 @@ public class Repository implements ApplicationContextAware, ApplicationListener, } return nodeRef; - } - - public void onEnableTenant() - { - init(); - } - - public void onDisableTenant() - { - destroy(); - } - - public void init() - { - initContext(); - } - - public void destroy() - { - companyHomeRefs.remove(tenantAdminService.getCurrentUserDomain()); } } diff --git a/source/java/org/alfresco/repo/module/AbstractModuleComponent.java b/source/java/org/alfresco/repo/module/AbstractModuleComponent.java index acdda771be..b6a40cb038 100644 --- a/source/java/org/alfresco/repo/module/AbstractModuleComponent.java +++ b/source/java/org/alfresco/repo/module/AbstractModuleComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -362,4 +362,11 @@ public abstract class AbstractModuleComponent implements ModuleComponent, BeanNa executed.put(tenantDomain, true); } } + + // from Thor + public final synchronized void shutdown() + { + String tenantDomain = tenantAdminService.getCurrentUserDomain(); + executed.put(tenantDomain, false); + } } diff --git a/source/java/org/alfresco/repo/module/ModuleComponent.java b/source/java/org/alfresco/repo/module/ModuleComponent.java index e4cc7b41de..058def40cf 100644 --- a/source/java/org/alfresco/repo/module/ModuleComponent.java +++ b/source/java/org/alfresco/repo/module/ModuleComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -92,4 +92,10 @@ public interface ModuleComponent * the associated module infrastructure. */ void execute(); + + /** + * Perform any cleanup required to remove module. + */ + // from Thor + void shutdown(); } diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java index f662109450..df19604f41 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-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -36,6 +36,8 @@ 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; @@ -242,15 +244,15 @@ public class ModuleComponentHelper { for (Tenant tenant : tenants) { - final String tenantDomain = tenant.getTenantDomain(); - AuthenticationUtil.runAs(new RunAsWork() + final String tenantDomain = tenant.getTenantDomain(); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - public Object doWork() throws Exception + public Object doWork() throws Exception { - startModule(module, mapStartedModules.get(tenantDomain), mapExecutedComponents.get(tenantDomain)); - return null; + startModule(module, mapStartedModules.get(tenantDomain), mapExecutedComponents.get(tenantDomain)); + return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } } @@ -267,14 +269,14 @@ public class ModuleComponentHelper { for (Tenant tenant : tenants) { - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - public Object doWork() throws Exception + public Object doWork() throws Exception { - checkForMissingModules(); - return null; + checkForMissingModules(); + return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); + }, tenant.getTenantDomain()); } } @@ -285,15 +287,15 @@ public class ModuleComponentHelper { for (Tenant tenant : tenants) { - final String tenantDomain = tenant.getTenantDomain(); - AuthenticationUtil.runAs(new RunAsWork() + final String tenantDomain = tenant.getTenantDomain(); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - public Object doWork() throws Exception + public Object doWork() throws Exception { - checkForOrphanComponents(mapExecutedComponents.get(tenantDomain)); - return null; + checkForOrphanComponents(mapExecutedComponents.get(tenantDomain)); + return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } } } @@ -306,7 +308,42 @@ public class ModuleComponentHelper } }, 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 diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java b/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java index 82c6d32325..cb3f342469 100644 --- a/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java +++ b/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -247,7 +247,7 @@ public class ModuleComponentHelperTest extends BaseAlfrescoTestCase { throw new UnsupportedOperationException(); } - + @Override public List getMissingModules() { @@ -265,6 +265,11 @@ public class ModuleComponentHelperTest extends BaseAlfrescoTestCase // Done return details; } + + public void shutdownModules() + { + throw new UnsupportedOperationException(); + } } /** Keep track of the execution count */ diff --git a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java index 390816a023..7bfaae30fd 100644 --- a/source/java/org/alfresco/repo/module/ModuleServiceImpl.java +++ b/source/java/org/alfresco/repo/module/ModuleServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -140,6 +140,16 @@ public class ModuleServiceImpl implements ApplicationContextAware, ModuleService { moduleComponentHelper.startModules(); } + + /** + * {@inheritDoc} + * + * @see ModuleComponentHelper#shutdownModules() + */ + public void shutdownModules() + { + moduleComponentHelper.shutdownModules(); + } /** * {@inheritDoc} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java index 0d71a759f0..65b43b8d2c 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -58,6 +58,8 @@ import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.AspectDefinition; @@ -710,14 +712,14 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp { // ETHREEOH-2014 - dictionary access should be in context of tenant (eg. full reindex with MT dynamic // models) - return AuthenticationUtil.runAs(new RunAsWork>() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork>() { public List doWork() { return createDocumentsImpl(stringNodeRef, ftsStatus, indexAllProperties, includeDirectoryDocuments, cascade, pathsToRegenerate, childAssociationsSinceFlush, deltaReader, mainReader); } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantService.getDomain(new NodeRef(stringNodeRef).getStoreRef().getIdentifier()))); + }, tenantService.getDomain(new NodeRef(stringNodeRef).getStoreRef().getIdentifier())); } else { diff --git a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java index a312cd0dd0..2aa54dbfb7 100644 --- a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java +++ b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java @@ -31,7 +31,6 @@ import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.dao.User; import org.alfresco.model.ContentModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.sync.UserRegistrySynchronizer; import org.alfresco.repo.tenant.TenantContextHolder; import org.alfresco.repo.tenant.TenantService; @@ -249,12 +248,12 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { throw new AuthenticationException("Null user name"); } - + if (isSystemUserName(userName)) { return setSystemUserAsCurrentUser(getUserDomain(userName)); } - + try { UserDetails ud = null; @@ -420,10 +419,9 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC return setCurrentUser(getGuestUserName(tenantDomain)); } else -{ + { throw new AuthenticationException("Guest authentication is not allowed"); } - } } @@ -489,7 +487,7 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC final String tenantDomain = userTenant.getSecond(); Authentication authentication = setCurrentUserImpl(userName); - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() throws Exception { @@ -645,7 +643,7 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { return authenticationContext.getSystemUserName(tenantDomain); } - + public String getUserDomain(String userName) { return authenticationContext.getUserDomain(userName); diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java index ab4604378f..db73f00f21 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java @@ -30,8 +30,9 @@ import net.sf.acegisecurity.context.ContextHolder; import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.ntlm.NLTMAuthenticator; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.tenant.TenantContextHolder; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.util.Pair; @@ -84,7 +85,7 @@ public class AuthenticationComponentImpl extends AbstractAuthenticationComponent { public String execute() throws Throwable { - return AuthenticationUtil.runAs(new RunAsWork() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public String doWork() throws Exception { @@ -94,7 +95,7 @@ public class AuthenticationComponentImpl extends AbstractAuthenticationComponent authenticationManager.authenticate(authentication); return normalized; } - }, getSystemUserName(getUserDomain(userName))); + }, tenantDomain); } }, true); @@ -104,7 +105,7 @@ public class AuthenticationComponentImpl extends AbstractAuthenticationComponent } else { - setCurrentUser(normalized, UserNameValidationMode.NONE); + setCurrentUser(normalized, UserNameValidationMode.NONE); } TenantContextHolder.setTenantDomain(tenantDomain); diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index 0a547a0e3f..af7a890bea 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -118,10 +118,8 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor private SimpleCache, List> zoneAuthorityCache; private SimpleCache, List>> childAuthorityCache; private AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache; - - /** System Container ref cache (Tennant aware) */ - private Map systemContainerRefs = new ConcurrentHashMap(4); - + private SimpleCache singletonCache; // eg. for system container nodeRefs (authorityContainer and zoneContainer) + private final String KEY_SYSTEMCONTAINER_NODEREF = "key.systemcontainer.noderef"; /** Limit the number of copies of authority names floating about by keeping them in a pool **/ private ConcurrentMap authorityNamePool = new ConcurrentHashMap(); @@ -228,6 +226,11 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor this.tenantService = tenantService; } + public void setSingletonCache(SimpleCache singletonCache) + { + this.singletonCache = singletonCache; + } + public void setAclDAO(AclDAO aclDao) { this.aclDao = aclDao; @@ -338,7 +341,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor } authorityLookupCache.put(cacheKey(name), childRef); } - + private Pair cacheKey(String authorityName) { String tenantDomain = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER ? tenantService.getDomain(authorityName) : tenantService.getCurrentUserDomain(); @@ -350,7 +353,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor String pooledName = authorityNamePool.putIfAbsent(authorityName, authorityName); return pooledName == null ? authorityName : pooledName; } - + public void deleteAuthority(String name) { NodeRef nodeRef = getAuthorityOrNull(name); @@ -1242,8 +1245,8 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor */ private NodeRef getSystemContainer(QName assocQName) { - final String cacheKey = tenantService.getCurrentUserDomain() + assocQName.toString(); - NodeRef systemContainerRef = systemContainerRefs.get(cacheKey); + final String cacheKey = KEY_SYSTEMCONTAINER_NODEREF + "." + assocQName.toString(); + NodeRef systemContainerRef = (NodeRef)singletonCache.get(cacheKey); if (systemContainerRef == null) { NodeRef rootNodeRef = nodeService.getRootNode(this.storeRef); @@ -1259,7 +1262,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor throw new AlfrescoRuntimeException("Required path not found: " + assocQName); } systemContainerRef = results.get(0).getChildRef(); - systemContainerRefs.put(cacheKey, systemContainerRef); + singletonCache.put(cacheKey, systemContainerRef); } return systemContainerRef; } diff --git a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java index 22a1d614ae..037bd11516 100644 --- a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java +++ b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java @@ -38,6 +38,8 @@ 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; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.model.FileExistsException; @@ -178,8 +180,8 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean final String overrideProviderName = getOverrideHomeFolderProviderName(); // Scan users in default and each Tenant - String systemUserName = AuthenticationUtil.getSystemUserName(); - scanPeople(systemUserName, TenantService.DEFAULT_DOMAIN, overrideProviderName); + + scanPeople(AuthenticationUtil.getSystemUserName(), TenantService.DEFAULT_DOMAIN, overrideProviderName); if (tenantAdminService.isEnabled()) { @@ -188,8 +190,16 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean { if (tenant.isEnabled()) { - systemUserName = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain()); - scanPeople(systemUserName, tenant.getTenantDomain(), overrideProviderName); + final String tenantDomain = tenant.getTenantDomain(); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public NodeRef doWork() throws Exception + { + scanPeople(AuthenticationUtil.getSystemUserName(), tenantDomain, overrideProviderName); + return null; + } + }, tenantDomain); + } } } @@ -206,7 +216,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean * in place of the all home folders existing providers. If {@code null} * the existing provider is used. */ - private void scanPeople(final String systemUserName, String tenantDomain, final String overrideProvider) + private void scanPeople(final String systemUserName, final String tenantDomain, final String overrideProvider) { /* * To avoid problems with existing home folders that are located in locations @@ -240,7 +250,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean final String createParentFoldersPhaseName = "createParentFolders"; RunAsWorker[] workers = new RunAsWorker[] { - new RunAsWorker(systemUserName, "calculateParentFolderStructure") + new RunAsWorker(systemUserName, tenantDomain, "calculateParentFolderStructure") { @Override public void doWork(NodeRef person) throws Exception @@ -249,8 +259,8 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean parentFolderStructure, person, overrideProvider); } }, - - new RunAsWorker(systemUserName, "moveHomeFolderThatClashesWithParentFolderStructure") + + new RunAsWorker(systemUserName, tenantDomain, "moveHomeFolderThatClashesWithParentFolderStructure") { @Override public void doWork(NodeRef person) throws Exception @@ -259,8 +269,8 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean parentFolderStructure, tmpFolders, person, overrideProvider); } }, - - new RunAsWorker(systemUserName, createParentFoldersPhaseName) + + new RunAsWorker(systemUserName, tenantDomain, createParentFoldersPhaseName) { @Override public void doWork(NodeRef person) throws Exception @@ -268,8 +278,8 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean createParentFolders(person, overrideProvider); } }, - - new RunAsWorker(systemUserName, "moveHomeFolderIfRequired") + + new RunAsWorker(systemUserName, tenantDomain, "moveHomeFolderIfRequired") { @Override public void doWork(NodeRef person) throws Exception @@ -283,6 +293,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean for (RunAsWorker worker: workers) { String name = worker.getName(); + if (logger.isInfoEnabled()) { logger.info(" -- "+ @@ -908,26 +919,34 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean } final String userName; + final String tenantDomain; final String name; - public RunAsWorker(String userName, String name) + public RunAsWorker(String userName, String tenantDomain, String name) { this.userName = userName; + this.tenantDomain = tenantDomain; this.name = name; } public void process(final NodeRef person) throws Throwable { - RunAsWork runAsWork = new RunAsWork() + // note: runAs before runAsTenant (to avoid clearing tenant context, if no previous auth) + AuthenticationUtil.runAs(new RunAsWork() { @Override public Object doWork() throws Exception { - RunAsWorker.this.doWork(person); - return null; + return TenantUtil.runAsTenant(new TenantRunAsWork() + { + public Void doWork() throws Exception + { + RunAsWorker.this.doWork(person); + return null; + } + }, tenantDomain); } - }; - AuthenticationUtil.runAs(runAsWork, userName); + }, userName); } public abstract void doWork(NodeRef person) throws Exception; diff --git a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java index 5240b4d27a..d1228a9037 100644 --- a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java +++ b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -39,6 +39,8 @@ 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.model.FileFolderService; @@ -235,7 +237,6 @@ public class HomeFolderProviderSynchronizerTest nodeService.getProperty(nodeRef, ContentModel.PROP_USERNAME)); final String domainUsername = tenantService.getBaseNameUser(username); String tenantDomain = tenantService.getUserDomain(username); - String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); boolean disabled = !TenantService.DEFAULT_DOMAIN.equals(tenantDomain) && !tenantAdminService.isEnabledTenant(tenantDomain); try @@ -244,14 +245,14 @@ public class HomeFolderProviderSynchronizerTest { tenantAdminService.enableTenant(tenantDomain); } - AuthenticationUtil.runAs(new RunAsWork() - { + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { public Object doWork() throws Exception { deleteUser(adminGuestUserHomeFolders, nodeRef, username, domainUsername); return null; } - }, systemUser); + }, tenantDomain); } finally { @@ -322,8 +323,7 @@ public class HomeFolderProviderSynchronizerTest final boolean createHomeDirectory) throws Exception { final String domainUsername = tenantService.getDomainUser(username, tenantDomain); - String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); - return AuthenticationUtil.runAs(new RunAsWork() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public NodeRef doWork() throws Exception { @@ -374,7 +374,7 @@ public class HomeFolderProviderSynchronizerTest } return person; } - }, systemUser); + }, tenantDomain); } private NodeRef createFolder(String path) throws Exception @@ -538,24 +538,25 @@ public class HomeFolderProviderSynchronizerTest try { final String domainUsername = tenantService.getDomainUser(username, tenantDomain); - String systemUser = tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public NodeRef doWork() throws Exception { NodeRef person = personService.getPerson(domainUsername, false); NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER)); + if (expectedPath != null) { assertNotNull("User: "+domainUsername+" home folder should exist", homeFolder); } + NodeRef rootPath = homeFolderManager.getRootPathNodeRef(largeHomeFolderProvider); String actualPath = toPath(rootPath, homeFolder); assertEquals("User: "+domainUsername+" home folder location", expectedPath, actualPath); return null; } - }, systemUser); + }, tenantDomain); } catch (RuntimeException e) { diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index d8fb83303d..64fc45e021 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; -import java.util.concurrent.ConcurrentHashMap; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -54,6 +53,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.PermissionServiceSPI; import org.alfresco.repo.tenant.TenantDomainMismatchException; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -154,8 +155,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per /** a transactionally-safe cache to be injected */ private SimpleCache> personCache; - /** People Container ref cache (Tennant aware) */ - private Map peopleContainerRefs = new ConcurrentHashMap(4); + // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + private SimpleCache singletonCache; // eg. for peopleContainerNodeRef + private final String KEY_PEOPLECONTAINER_NODEREF = "key.peoplecontainer.noderef"; private UserNameMatcher userNameMatcher; @@ -238,7 +240,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per ContentModel.TYPE_USER, new JavaBehaviour(this, "onUpdatePropertiesUser")); } - + /** * {@inheritDoc} */ @@ -281,17 +283,22 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { this.serviceRegistry = serviceRegistry; } - + public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } - + public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } - + + public void setSingletonCache(SimpleCache singletonCache) + { + this.singletonCache = singletonCache; + } + public void setSearchService(SearchService searchService) { this.searchService = searchService; @@ -440,13 +447,13 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { final String tenantDomain = tenantService.getUserDomain(userName); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public NodeRef doWork() throws Exception { return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, exceptionOrNull); } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } else { @@ -1068,33 +1075,32 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per */ public NodeRef getPeopleContainer() { - String cacheKey = tenantService.getCurrentUserDomain(); - NodeRef peopleNodeRef = peopleContainerRefs.get(cacheKey); + NodeRef peopleNodeRef = (NodeRef)singletonCache.get(KEY_PEOPLECONTAINER_NODEREF); if (peopleNodeRef == null) { - NodeRef rootNodeRef = nodeService.getRootNode(tenantService.getName(storeRef)); + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); List children = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(SYSTEM_FOLDER_SHORT_QNAME, namespacePrefixResolver), false); - + if (children.size() == 0) { throw new AlfrescoRuntimeException("Required people system path not found: " + SYSTEM_FOLDER_SHORT_QNAME); } - + NodeRef systemNodeRef = children.get(0).getChildRef(); - + children = nodeService.getChildAssocs(systemNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName( PEOPLE_FOLDER_SHORT_QNAME, namespacePrefixResolver), false); - + if (children.size() == 0) { throw new AlfrescoRuntimeException("Required people system path not found: " + PEOPLE_FOLDER_SHORT_QNAME); } - + peopleNodeRef = children.get(0).getChildRef(); - peopleContainerRefs.put(cacheKey, peopleNodeRef); + singletonCache.put(KEY_PEOPLECONTAINER_NODEREF, peopleNodeRef); } return peopleNodeRef; } @@ -1113,7 +1119,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per NodeRef personRef = getPersonOrNullImpl(userName); - deletePersonImpl(userName, personRef); + deletePersonAndAuthenticationImpl(userName, personRef); } /** @@ -1125,7 +1131,31 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per if (typeQName.equals(ContentModel.TYPE_PERSON)) { String userName = (String) this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); - deletePersonImpl(userName, personRef); + deletePersonAndAuthenticationImpl(userName, personRef); + } + else + { + throw new AlfrescoRuntimeException("deletePerson: invalid type of node "+personRef+" (actual="+typeQName+", expected="+ContentModel.TYPE_PERSON+")"); + } + } + + /** + * {@inheritDoc} + */ + public void deletePerson(NodeRef personRef, boolean deleteAuthentication) + { + QName typeQName = nodeService.getType(personRef); + if (typeQName.equals(ContentModel.TYPE_PERSON)) + { + if (deleteAuthentication) + { + String userName = (String) this.nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); + deletePersonAndAuthenticationImpl(userName, personRef); + } + else + { + deletePersonImpl(personRef); + } } else { @@ -1133,7 +1163,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per } } - private void deletePersonImpl(String userName, NodeRef personRef) + + private void deletePersonAndAuthenticationImpl(String userName, NodeRef personRef) { if (userName != null) { @@ -1161,6 +1192,11 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per permissionServiceSPI.deletePermissions(userName); } + deletePersonImpl(personRef); + } + + private void deletePersonImpl(NodeRef personRef) + { // delete the person if (personRef != null) { @@ -1185,7 +1221,6 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { AlfrescoTransactionSupport.bindListener(this); } - } /** diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 4467f02630..72900cbced 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.SortedSet; import java.util.StringTokenizer; import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -43,6 +42,7 @@ import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.activities.ActivityType; import org.alfresco.repo.admin.SysAdminParams; +import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.OnRestoreNodePolicy; import org.alfresco.repo.node.getchildren.FilterProp; @@ -58,8 +58,9 @@ import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; -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; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.activities.ActivityService; @@ -125,12 +126,13 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic private static final int GROUP_PREFIX_LENGTH = PermissionService.GROUP_PREFIX.length(); private static final int GROUP_SITE_PREFIX_LENGTH = GROUP_SITE_PREFIX.length(); - /** Site home ref cache (Tennant aware) */ - private Map siteHomeRefs = new ConcurrentHashMap(4); + // note: caches are tenant-aware (if using EhCacheAdapter shared cache) + + private SimpleCache singletonCache; // eg. for siteHomeNodeRef + private final String KEY_SITEHOME_NODEREF = "key.sitehome.noderef"; + + private SimpleCache siteNodeRefCache; // for site shortname to nodeRef lookup - /** Site node ref cache (Tennant aware) */ - private Map siteNodeRefs = new ConcurrentHashMap(256); - private String sitesXPath; /** Messages */ @@ -144,7 +146,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic private static final String MSG_CAN_NOT_CHANGE_MSHIP="site_service.can_not_change_membership"; private static final String MSG_SITE_CONTAINER_NOT_FOLDER = "site_service.site_container_not_folder"; private static final String MSG_INVALID_SITE_TYPE = "site_service.invalid_site_type"; - + /* Services */ private NodeService nodeService; private NodeService directNodeService; @@ -159,7 +161,6 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic private AuthorityService authorityService; private DictionaryService dictionaryService; private TenantService tenantService; - private TenantAdminService tenantAdminService; private RetryingTransactionHelper retryingTransactionHelper; private Comparator roleComparator; private SysAdminParams sysAdminParams; @@ -292,12 +293,14 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic this.tenantService = tenantService; } - /** - * Sets the tenant admin service - */ - public void setTenantAdminService(TenantAdminService tenantAdminService) + public void setSingletonCache(SimpleCache singletonCache) { - this.tenantAdminService = tenantAdminService; + this.singletonCache = singletonCache; + } + + public void setSiteNodeRefCache(SimpleCache siteNodeRefCache) + { + this.siteNodeRefCache = siteNodeRefCache; } /** @@ -745,8 +748,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic */ public NodeRef getSiteRoot() { - String tenantDomain = tenantAdminService.getCurrentUserDomain(); - NodeRef siteHomeRef = siteHomeRefs.get(tenantDomain); + NodeRef siteHomeRef = (NodeRef)singletonCache.get(KEY_SITEHOME_NODEREF); if (siteHomeRef == null) { siteHomeRef = AuthenticationUtil.runAs(new RunAsWork() @@ -782,7 +784,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // There may be domains with no sites (e.g. JSF-only clients). if (siteHomeRef != null) { - siteHomeRefs.put(tenantDomain, siteHomeRef); + singletonCache.put(KEY_SITEHOME_NODEREF, siteHomeRef); } } return siteHomeRef; @@ -930,13 +932,13 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic { final String tenantDomain = tenantService.getUserDomain(userName); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() - { - public List doWork() throws Exception + return TenantUtil.runAsSystemTenant(new TenantRunAsWork>() { - return listSitesImpl(userName, size); - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + public List doWork() throws Exception + { + return listSitesImpl(userName, size); + } + }, tenantDomain); } else { @@ -1167,14 +1169,14 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic final String tenantDomain = tenantService.getDomain(shortName); final String sName = tenantService.getBaseName(shortName, true); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public SiteInfo doWork() throws Exception { SiteInfo site = getSiteImpl(sName); return new SiteInfoImpl(site.getSitePreset(), shortName, site.getTitle(), site.getDescription(), site.getVisibility(), site.getCustomProperties(), site.getNodeRef()); } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } else { @@ -1266,14 +1268,13 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic */ private NodeRef getSiteNodeRef(final String shortName, boolean enforcePermissions) { - final String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; - NodeRef siteNodeRef = this.siteNodeRefs.get(cacheKey); + NodeRef siteNodeRef = siteNodeRefCache.get(shortName); if (siteNodeRef != null) { // test for existance - and remove from cache if no longer exists if (!this.directNodeService.exists(siteNodeRef)) { - this.siteNodeRefs.remove(cacheKey); + siteNodeRefCache.remove(shortName); siteNodeRef = null; } } @@ -1292,7 +1293,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // cache the result if found - null results will be required to ensure new sites are found later if (siteNode != null) { - siteNodeRefs.put(cacheKey, siteNode); + siteNodeRefCache.put(shortName, siteNode); } return siteNode; } @@ -1309,7 +1310,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic return siteNodeRef; } } - + /** * @see org.alfresco.service.cmr.site.SiteService#updateSite(org.alfresco.service.cmr.site.SiteInfo) */ @@ -1417,10 +1418,9 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic throw new SiteServiceException(MSG_CAN_NOT_DELETE, new Object[]{shortName}); } final QName siteType = this.directNodeService.getType(siteNodeRef); - + // Delete the cached reference - String cacheKey = this.tenantAdminService.getCurrentUserDomain() + '_' + shortName; - this.siteNodeRefs.remove(cacheKey); + siteNodeRefCache.remove(shortName); // Collection for recording the group memberships present on the site final Map> groupsMemberships = new HashMap>(); @@ -1515,13 +1515,13 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic final String tenantDomain = tenantService.getDomain(shortName); final String sName = tenantService.getBaseName(shortName, true); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork>() + return TenantUtil.runAsSystemTenant(new TenantRunAsWork>() { public Map doWork() throws Exception { return listMembersImpl(sName, nameFilter, roleFilter, size, collapseGroups); } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenantDomain); } else { diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index 5bf1fd1460..e9ce9e0846 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -21,6 +21,7 @@ package org.alfresco.repo.tenant; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -42,8 +43,9 @@ import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.thumbnail.ThumbnailRegistry; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.usage.UserUsageTrackingComponent; import org.alfresco.repo.workflow.WorkflowDeployer; import org.alfresco.service.cmr.admin.RepoAdminService; @@ -74,7 +76,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo private static Log logger = LogFactory.getLog(MultiTAdminServiceImpl.class); // Keep hold of the app context - private ApplicationContext ctx; + protected ApplicationContext ctx; // Dependencies private NodeService nodeService; @@ -89,6 +91,8 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo protected ContentStore tenantFileContentStore; private ThumbnailRegistry thumbnailRegistry; + private String contentRootContainerPath = null; + private WorkflowService workflowService; private RepositoryExporterService repositoryExporterService; private ModuleService moduleService; @@ -96,6 +100,9 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo private String baseAdminUsername = null; + // Experimental: Thor + private TenantRoutingDataSource trds; + /* * Tenant domain/ids are unique strings that are case-insensitive. Tenant ids must be valid filenames. * They may also map onto domains and hence should allow valid FQDN. @@ -161,17 +168,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo { this.tenantAdminDAO = tenantAdminDAO; } - + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } - + public void setTenantFileContentStore(ContentStore tenantFileContentStore) { this.tenantFileContentStore = tenantFileContentStore; } - + public void setWorkflowService(WorkflowService workflowService) { this.workflowService = workflowService; @@ -206,6 +213,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo this.baseAdminUsername = baseAdminUsername; } + public void setTenantRoutingDataSource(TenantRoutingDataSource trds) + { + this.trds = trds; + } + + // if set then tenant are not co-mingled and all content roots will appear below this container (in sub-folder) + public void setContentRootContainerPath(String contentRootContainerPath) + { + this.contentRootContainerPath = contentRootContainerPath; + } + public static final String PROTOCOL_STORE_USER = "user"; public static final String PROTOCOL_STORE_WORKSPACE = "workspace"; public static final String PROTOCOL_STORE_SYSTEM = "system"; @@ -219,6 +237,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo public static final String TENANTS_ATTRIBUTE_PATH = "alfresco-tenants"; public static final String TENANT_ATTRIBUTE_ENABLED = "enabled"; public static final String TENANT_ATTRIBUTE_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir"; + public static final String TENANT_ATTRIBUTE_DB_URL = "dbUrl"; // if not co-mingled private List tenantDeployers = new ArrayList(); @@ -350,17 +369,59 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo /** * @see TenantAdminService.createTenant() */ - public void createTenant(String tenantDomainIn, final char[] tenantAdminRawPassword, String rootContentStoreDir) + public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword, String contentRoot) + { + createTenant(tenantDomain, tenantAdminRawPassword, contentRoot, null); + } + + /** + * @see TenantAdminService.createTenant() + */ + public void createTenant(final String tenantDomainIn, final char[] tenantAdminRawPassword, String contentRootPath, String dbUrl) { ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword); final String tenantDomain = getTenantDomain(tenantDomainIn); - + AuthenticationUtil.setMtEnabled(true); // in case this is the 1st tenant long start = System.currentTimeMillis(); + + if ((contentRootContainerPath != null) && (! contentRootContainerPath.isEmpty())) + { + String defaultContentRoot = null; + + if (! contentRootContainerPath.endsWith("/")) + { + defaultContentRoot = contentRootContainerPath + "/" + tenantDomain; + } + else + { + defaultContentRoot = contentRootContainerPath + tenantDomain; + } + + if ((contentRootPath != null) && (! contentRootPath.isEmpty())) + { + logger.warn("Use default content root path: "+defaultContentRoot+" (ignoring: "+contentRootPath+")"); + } + + contentRootPath = defaultContentRoot; + } - initTenant(tenantDomain, rootContentStoreDir); + initTenant(tenantDomain, contentRootPath, dbUrl); + + if ((dbUrl != null) && (trds != null)) + { + try + { + // note: experimental - currently assumes a bootstrapped DB schema exists for this dbUrl ! + trds.addTenantDataSource(tenantDomain, dbUrl); + } + catch (SQLException se) + { + throw new AlfrescoRuntimeException("Failed to create tenant '"+tenantDomain+"' for dbUrl '"+dbUrl+"'", se); + } + } try { @@ -375,42 +436,58 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo ((TenantDeployer)tenantFileContentStore).init(); } - // create tenant-specific stores - ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap-mt"); - bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); - - ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap-mt"); - bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); - - // deprecated - ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap-mt"); - bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); - - ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap-mt"); - bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap-mt"); - bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); - - ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap-mt"); - bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); - - thumbnailRegistry.initThumbnailDefinitions(); - - // notify listeners that tenant has been created & hence enabled - for (TenantDeployer tenantDeployer : tenantDeployers) + // callback + RetryingTransactionCallback doImportCallback = new RetryingTransactionCallback() { - tenantDeployer.onEnableTenant(); - } + public Object execute() throws Throwable + { + // create tenant-specific stores + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)ctx.getBean("userBootstrap-mt"); + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); + + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)ctx.getBean("systemBootstrap-mt"); + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); + + // deprecated + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap-mt"); + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + + ImporterBootstrap version2ImporterBootstrap = (ImporterBootstrap)ctx.getBean("version2Bootstrap-mt"); + bootstrapVersionTenantStore(version2ImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap-mt"); + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesBootstrap-mt"); + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + + thumbnailRegistry.initThumbnailDefinitions(); + + // TODO janv - resolve this conflict later + /* Note: assume for now that all tenant deployers can lazily init + + // notify listeners that tenant has been created & hence enabled + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + */ + + // bootstrap workflows + for (WorkflowDeployer workflowDeployer : workflowDeployers) + { + workflowDeployer.init(); + } + + // bootstrap modules (if any) + moduleService.startModules(); + + return null; + } + }; - // bootstrap workflows - for (WorkflowDeployer workflowDeployer : workflowDeployers) - { - workflowDeployer.init(); - } - - // bootstrap modules (if any) - moduleService.startModules(); + // if not default DB (ie. dbUrl != null) then run in new Spring managed txn (to ensure datasource is switched) + transactionService.getRetryingTransactionHelper().doInTransaction(doImportCallback, transactionService.isReadOnly(), (dbUrl != null)); } finally { @@ -426,20 +503,23 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo /** * Export tenant - equivalent to the tenant admin running a 'complete repo' export from the Web Client Admin */ - public void exportTenant(String tenantDomain, final File directoryDestination) + public void exportTenant(String tenantDomainIn, final File directoryDestination) { - final String lowerTenantDomain = getTenantDomain(tenantDomain); - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - repositoryExporterService.export(directoryDestination, lowerTenantDomain); - return null; - } - }, getSystemUser(tenantDomain)); + final String tenantDomain = getTenantDomain(tenantDomainIn); - logger.info("Tenant exported: " + tenantDomain); + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() + { + repositoryExporterService.export(directoryDestination, tenantDomain); + return null; + } + }, tenantDomain); + + if (logger.isInfoEnabled()) + { + logger.info("Tenant exported: " + tenantDomain); + } } /** @@ -451,7 +531,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo AuthenticationUtil.setMtEnabled(true); // in case this is the 1st tenant - initTenant(tenantDomain, contentRoot); + initTenant(tenantDomain, contentRoot, null); try { @@ -489,14 +569,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } // bootstrap modules (if any) - moduleService.startModules(); + moduleService.startModules(); } finally { AuthenticationUtil.popAuthentication(); } - - logger.info("Tenant imported: " + tenantDomain); + + if (logger.isInfoEnabled()) + { + logger.info("Tenant imported: " + tenantDomain); + } } public boolean existsTenant(String tenantDomain) @@ -505,7 +588,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo ParameterCheck.mandatory("tenantDomain", tenantDomain); tenantDomain = getTenantDomain(tenantDomain); - + return (getTenantAttributes(tenantDomain) != null); } @@ -518,15 +601,15 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } else { - Tenant tenant = new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot()); + Tenant tenant = new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot(), null); return tenant; } } public void enableTenant(String tenantDomain) { - tenantDomain = getTenantDomain(tenantDomain); - + tenantDomain = getTenantDomain(tenantDomain); + if (! existsTenant(tenantDomain)) { throw new AuthenticationException("Tenant does not exist: " + tenantDomain); @@ -537,11 +620,13 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo logger.warn("Tenant already enabled: " + tenantDomain); } - enableTenant(tenantDomain, true); + // Note: assume for now that all tenant deployers can lazily init + boolean notifyTenantDeployers = false; + enableTenant(tenantDomain, notifyTenantDeployers); } - private void enableTenant(String tenantDomain, boolean notifyTenantDeployers) - { + protected void enableTenant(String tenantDomain, boolean notifyTenantDeployers) + { // Check that all the passed values are not null ParameterCheck.mandatory("tenantDomain", tenantDomain); @@ -552,26 +637,29 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo if (notifyTenantDeployers) { // notify listeners that tenant has been enabled - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onEnableTenant(); - } - return null; - } - }, getSystemUser(tenantDomain)); + tenantDeployer.onEnableTenant(); + } + return null; + } + }, tenantDomain); } - logger.info("Tenant enabled: " + tenantDomain); - } + if (logger.isInfoEnabled()) + { + logger.info("Tenant enabled: " + tenantDomain); + } + } public void disableTenant(String tenantDomain) { - tenantDomain = getTenantDomain(tenantDomain); - + tenantDomain = getTenantDomain(tenantDomain); + if (! existsTenant(tenantDomain)) { throw new AuthenticationException("Tenant does not exist: " + tenantDomain); @@ -585,24 +673,24 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo disableTenant(tenantDomain, true); } - public void disableTenant(String tenantDomain, boolean notifyTenantDeployers) + protected void disableTenant(String tenantDomain, boolean notifyTenantDeployers) { - tenantDomain = getTenantDomain(tenantDomain); - + tenantDomain = getTenantDomain(tenantDomain); + if (notifyTenantDeployers) { // notify listeners that tenant has been disabled - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Object doWork() + { + for (TenantDeployer tenantDeployer : tenantDeployers) { - public Object doWork() - { - for (TenantDeployer tenantDeployer : tenantDeployers) - { - tenantDeployer.onDisableTenant(); - } - return null; - } - }, getSystemUser(tenantDomain)); + tenantDeployer.onDisableTenant(); + } + return null; + } + }, tenantDomain); } // update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable) @@ -610,27 +698,30 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo tenantUpdateEntity.setEnabled(false); tenantAdminDAO.updateTenant(tenantUpdateEntity); - logger.info("Tenant disabled: " + tenantDomain); + if (logger.isInfoEnabled()) + { + logger.info("Tenant disabled: " + tenantDomain); + } } public boolean isEnabledTenant(String tenantDomain) { // Check that all the passed values are not null ParameterCheck.mandatory("tenantDomain", tenantDomain); - + tenantDomain = getTenantDomain(tenantDomain); - + Tenant tenant = getTenantAttributes(tenantDomain); if (tenant != null) { return tenant.isEnabled(); } - + return false; } protected String getRootContentStoreDir(String tenantDomain) - { + { // Check that all the passed values are not null ParameterCheck.mandatory("tenantDomain", tenantDomain); @@ -645,13 +736,13 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo public Tenant getTenant(String tenantDomain) { - tenantDomain = getTenantDomain(tenantDomain); + tenantDomain = getTenantDomain(tenantDomain); if (! existsTenant(tenantDomain)) { throw new AuthenticationException("Tenant does not exist: " + tenantDomain); } - return new Tenant(tenantDomain, isEnabledTenant(tenantDomain), getRootContentStoreDir(tenantDomain)); + return getTenantAttributes(tenantDomain); } /** @@ -659,17 +750,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo */ public void deleteTenant(String tenantDomain) { - tenantDomain = getTenantDomain(tenantDomain); - + tenantDomain = getTenantDomain(tenantDomain); + if (! existsTenant(tenantDomain)) { throw new AuthenticationException("Tenant does not exist: " + tenantDomain); } else { - try + try { - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() { @@ -702,7 +793,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo return null; } - }, getSystemUser(tenantDomain)); + }, tenantDomain); final String tenantAdminUser = getTenantAdminUser(tenantDomain); @@ -714,19 +805,22 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_SYSTEM, STORE_BASE_ID_SYSTEM))); nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_USER, STORE_BASE_ID_USER))); - - // notify listeners that tenant has been deleted & hence disabled - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() { + // shutdown modules (if any) + moduleService.shutdownModules(); + + // notify listeners that tenant has been deleted & hence disabled for (TenantDeployer tenantDeployer : tenantDeployers) { tenantDeployer.onDisableTenant(); } + return null; } - }, getSystemUser(tenantDomain)); + }, tenantDomain); // remove tenant tenantAdminDAO.deleteTenant(tenantDomain); @@ -752,7 +846,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo List tenants = new ArrayList(tenantEntities.size()); for (TenantEntity tenantEntity : tenantEntities) { - tenants.add(new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot())); + tenants.add(new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot(), null)); } return tenants; } @@ -773,7 +867,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); } - private void bootstrapSystemTenantStore(ImporterBootstrap systemImporterBootstrap, String tenantDomain) + protected void bootstrapSystemTenantStore(ImporterBootstrap systemImporterBootstrap, String tenantDomain) { // Bootstrap Tenant-Specific System Store StoreRef bootstrapStoreRef = systemImporterBootstrap.getStoreRef(); @@ -790,7 +884,10 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo // reset since systemImporter is singleton (hence reused) systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - logger.debug("Bootstrapped store: " + tenantService.getBaseName(tenantBootstrapStoreRef)); + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrapped store: "+tenantService.getBaseName(tenantBootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + } } private void importBootstrapUserTenantStore(String tenantDomain, File directorySource) @@ -809,54 +906,60 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, null); } - private void bootstrapUserTenantStore(ImporterBootstrap userImporterBootstrap, String tenantDomain, char[] tenantAdminRawPassword) + protected void bootstrapUserTenantStore(ImporterBootstrap userImporterBootstrap, String tenantDomain, char[] tenantAdminRawPassword) { // Bootstrap Tenant-Specific User Store StoreRef bootstrapStoreRef = userImporterBootstrap.getStoreRef(); bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - + // override admin username property - Properties props = userImporterBootstrap.getConfiguration(); + Properties props = userImporterBootstrap.getConfiguration(); props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain)); if (tenantAdminRawPassword != null) { String salt = null; // GUID.generate(); - props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt)); + props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt)); } userImporterBootstrap.bootstrap(); - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrapped store: "+tenantService.getBaseName(bootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + } } - + private void importBootstrapVersionTenantStore(String tenantDomain, File directorySource) { // Import Bootstrap (restore) Tenant-Specific Version Store Properties bootstrapView = new Properties(); bootstrapView.put("path", "/"); bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_versions2.acp"); - + List bootstrapViews = new ArrayList(1); bootstrapViews.add(bootstrapView); ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)ctx.getBean("versionBootstrap"); versionImporterBootstrap.setBootstrapViews(bootstrapViews); - + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); } - private void bootstrapVersionTenantStore(ImporterBootstrap versionImporterBootstrap, String tenantDomain) + protected void bootstrapVersionTenantStore(ImporterBootstrap versionImporterBootstrap, String tenantDomain) { // Bootstrap Tenant-Specific Version Store StoreRef bootstrapStoreRef = versionImporterBootstrap.getStoreRef(); bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); - + versionImporterBootstrap.bootstrap(); - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrapped store: "+tenantService.getBaseName(bootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + } } private void importBootstrapSpacesArchiveTenantStore(String tenantDomain, File directorySource) @@ -865,17 +968,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo Properties bootstrapView = new Properties(); bootstrapView.put("path", "/"); bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces_archive.acp"); - + List bootstrapViews = new ArrayList(1); bootstrapViews.add(bootstrapView); ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)ctx.getBean("spacesArchiveBootstrap"); spacesArchiveImporterBootstrap.setBootstrapViews(bootstrapViews); - + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); } - private void bootstrapSpacesArchiveTenantStore(ImporterBootstrap spacesArchiveImporterBootstrap, String tenantDomain) + protected void bootstrapSpacesArchiveTenantStore(ImporterBootstrap spacesArchiveImporterBootstrap, String tenantDomain) { // Bootstrap Tenant-Specific Spaces Archive Store StoreRef bootstrapStoreRef = spacesArchiveImporterBootstrap.getStoreRef(); @@ -885,11 +988,14 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo // override default property (archive://SpacesStore) List mustNotExistStoreUrls = new ArrayList(); mustNotExistStoreUrls.add(bootstrapStoreRef.toString()); - spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); + spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls); spacesArchiveImporterBootstrap.bootstrap(); - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrapped store: "+tenantService.getBaseName(bootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + } } private void importBootstrapSpacesModelsTenantStore(String tenantDomain, File directorySource) @@ -927,7 +1033,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); } - private void bootstrapSpacesTenantStore(ImporterBootstrap spacesImporterBootstrap, String tenantDomain) + protected void bootstrapSpacesTenantStore(ImporterBootstrap spacesImporterBootstrap, String tenantDomain) { // Bootstrap Tenant-Specific Spaces Store StoreRef bootstrapStoreRef = spacesImporterBootstrap.getStoreRef(); @@ -940,14 +1046,17 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo // override guest username property props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain)); - + spacesImporterBootstrap.bootstrap(); // calculate any missing usages UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)ctx.getBean("userUsageTrackingComponent"); userUsageTrackingComponent.bootstrapInternal(); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + + if (logger.isDebugEnabled()) + { + logger.debug("Bootstrapped store: "+tenantService.getBaseName(bootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + } } public void deployTenants(final TenantDeployer deployer, Log logger) @@ -991,15 +1100,15 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo try { // deploy within context of tenant domain - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() - { + { // init the service within tenant context deployer.init(); return null; } - }, getSystemUser(tenant.getTenantDomain())); + }, tenant.getTenantDomain()); } catch (Throwable e) @@ -1022,22 +1131,22 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo if (deployer == null) { throw new AlfrescoRuntimeException("Deployer must be provided"); - } + } if (logger == null) { throw new AlfrescoRuntimeException("Logger must be provided"); - } - + } + if (tenantService.isEnabled()) - { - UserTransaction userTransaction = transactionService.getUserTransaction(); + { + UserTransaction userTransaction = transactionService.getUserTransaction(); authenticationContext.setSystemUserAsCurrentUser(); - - List tenants = null; - try + + List tenants = null; + try { - userTransaction.begin(); - tenants = getAllTenants(); + userTransaction.begin(); + tenants = getAllTenants(); userTransaction.commit(); } catch(Throwable e) @@ -1047,28 +1156,27 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo try {authenticationContext.clearCurrentSecurityContext(); } catch (Exception ex) {} throw new AlfrescoRuntimeException("Failed to get tenants", e); } - - try + + try { AuthenticationUtil.pushAuthentication(); for (Tenant tenant : tenants) - { - if (tenant.isEnabled()) + { + if (tenant.isEnabled()) { try { // undeploy within context of tenant domain - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() - { + { // destroy the service within tenant context deployer.destroy(); return null; - } - }, getSystemUser(tenant.getTenantDomain())); - - } + } + }, tenant.getTenantDomain()); + } catch (Throwable e) { logger.error("Undeployment failed" + e); @@ -1083,11 +1191,11 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } } finally - { + { AuthenticationUtil.popAuthentication(); } } - } + } public void register(TenantDeployer deployer) { @@ -1159,7 +1267,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } } - private void initTenant(String tenantDomain, String rootContentStoreDir) + protected void initTenant(String tenantDomain, String contentRoot, String dbUrl) { validateTenantName(tenantDomain); @@ -1168,7 +1276,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain); } - if (rootContentStoreDir != null) + if (contentRoot != null) { if (! (tenantFileContentStore instanceof AbstractTenantRoutingContentStore)) { @@ -1176,22 +1284,23 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo throw new AlfrescoRuntimeException("MT: cannot initialse tenant - TenantRoutingContentStore is not configured AND tenant is not using co-mingled content store (ie. default root location)"); } - File tenantRootDir = new File(rootContentStoreDir); + File tenantRootDir = new File(contentRoot); if ((tenantRootDir.exists()) && (tenantRootDir.list().length != 0)) { - logger.warn("Tenant root directory is not empty: " + rootContentStoreDir); + logger.warn("Tenant root directory is not empty: " + contentRoot); } } - if (rootContentStoreDir == null) + if (contentRoot == null) { - rootContentStoreDir = tenantFileContentStore.getRootLocation(); + contentRoot = tenantFileContentStore.getRootLocation(); } // init - need to enable tenant (including tenant service) before stores bootstrap TenantEntity tenantEntity = new TenantEntity(tenantDomain); tenantEntity.setEnabled(true); - tenantEntity.setContentRoot(rootContentStoreDir); + tenantEntity.setContentRoot(contentRoot); + tenantEntity.setDbUrl(dbUrl); tenantAdminDAO.createTenant(tenantEntity); } @@ -1254,14 +1363,14 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo public String getDomainUser(String baseUsername, String tenantDomain) { - tenantDomain = getTenantDomain(tenantDomain); + tenantDomain = getTenantDomain(tenantDomain); return tenantService.getDomainUser(baseUsername, tenantDomain); } public String getDomain(String name) { - name = getTenantDomain(name); - return tenantService.getDomain(name); + name = getTenantDomain(name); + return tenantService.getDomain(name); } // local helpers @@ -1276,7 +1385,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo return getBaseNameUser(AuthenticationUtil.getAdminUserName()); } - private String getSystemUser(String tenantDomain) + protected String getSystemUser(String tenantDomain) { return tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain); } diff --git a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java index ce03d66447..5ea9e63d52 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java +++ b/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java @@ -102,6 +102,7 @@ public class MultiTDemoTest extends TestCase private NodeService nodeService; private NodeArchiveService nodeArchiveService; + private NamespaceService namespaceService; private MutableAuthenticationService authenticationService; private PersonService personService; private SiteService siteService; @@ -175,6 +176,7 @@ public class MultiTDemoTest extends TestCase nodeService = (NodeService) ctx.getBean("NodeService"); nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService"); + namespaceService = (NamespaceService) ctx.getBean("NamespaceService"); authenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService"); tenantAdminService = (TenantAdminService) ctx.getBean("tenantAdminService"); tenantService = (TenantService) ctx.getBean("tenantService"); @@ -200,6 +202,7 @@ public class MultiTDemoTest extends TestCase AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); // authenticate as super-admin createTenants(); + createUsers(); } @Override @@ -216,23 +219,93 @@ public class MultiTDemoTest extends TestCase } } - public synchronized void test_ALF_17681() throws Exception + private void createTenant(final String tenantDomain) + { + // create tenants (if not already created) + TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + if (! tenantAdminService.existsTenant(tenantDomain)) + { + //tenantAdminService.createTenant(tenantDomain, DEFAULT_ADMIN_PW.toCharArray(), ROOT_DIR + "/" + tenantDomain); + tenantAdminService.createTenant(tenantDomain, (DEFAULT_ADMIN_PW+" "+tenantDomain).toCharArray(), null); // use default root dir + + logger.info("Created tenant " + tenantDomain); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + private void deleteTenant(final String tenantDomain) + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // delete tenant (if it exists) + TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + if (tenantAdminService.existsTenant(tenantDomain)) + { + tenantAdminService.deleteTenant(tenantDomain); + + logger.info("Deleted tenant " + tenantDomain); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + return null; + } + }); + } + + private void createUsers() + { + for (final String tenantDomain : tenants) + { + String tenantAdminName = tenantService.getDomainUser(AuthenticationUtil.getAdminUserName(), tenantDomain); + TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() + { + public Object doWork() throws Exception + { + createUser(TEST_USER1, tenantDomain, TEST_USER1+" "+tenantDomain); + createUser(TEST_USER2, tenantDomain, TEST_USER2+" "+tenantDomain); + + if (tenantDomain.equals(TEST_TENANT_DOMAIN2)) + { + createUser(TEST_USER3, tenantDomain, TEST_USER3+" "+tenantDomain); + } + + return null; + } + }, tenantAdminName); + } + } + + // note: needs to come before test10CreateCategories & test15COCIandSearch ? + public synchronized void test00_ALF_17681() throws Exception { // The issue was found on Lucene final String tenantDomain = TEST_RUN+".alf17681"; final String query = "PATH:\"/app:company_home/app:dictionary\""; - + // Create tenant createTenant(tenantDomain); - + final String tenantAdminName = tenantService.getDomainUser(AuthenticationUtil.getAdminUserName(), tenantDomain); // Search for Data dictionary by tenant admin int count = searchForDataDictionary(tenantAdminName, query); - + assertEquals("Data dictionary should be found for tenant. ", 1, count); - + indexRecoverer.setRecoveryMode(FullIndexRecoveryComponent.RecoveryMode.FULL.name()); - + // reindex Thread reindexThread = new Thread() { @@ -241,48 +314,57 @@ public class MultiTDemoTest extends TestCase indexRecoverer.reindex(); } }; - + reindexThread.start(); - + // must allow the rebuild to complete or the test after this one will fail to validate their indexes // - as they now will be deleted. reindexThread.join(); - + // wait a bit and then terminate wait(20000); indexRecoverer.setShutdown(true); wait(20000); - + // Search for Data dictionary by tenant admin int countAfter = searchForDataDictionary(tenantAdminName, query); - + assertEquals("Data dictionary should be found for tenant after FULL reindex. ", 1, countAfter); } - private int searchForDataDictionary(String tenantAdminName, final String query) + /* + public void test00_Setup() throws Throwable { - return AuthenticationUtil.runAs(new RunAsWork() - { - public Integer doWork() throws Exception - { - ResultSet resultSet = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, query, null); - return resultSet.length(); - } - }, tenantAdminName); + // test common setup (see "setUp") } + */ public void test01CreateTenants() throws Throwable - { - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); // authenticate as super-admin + { + // ignore common setup - test here explicitly logger.info("Create tenants"); - List persons = personService.getPeople(null, null, null, new PagingRequest(0, Integer.MAX_VALUE, null)).getPage(); + final String tenantDomain1 = TEST_RUN+".one.createTenant"; + final String tenantDomain2 = TEST_RUN+".two.createTenant"; + + String[] tenantDomains = new String[] {tenantDomain1, tenantDomain2 }; + + for (final String tenantDomain : tenantDomains) + { + createTenant(tenantDomain); + } + + // check default (super-tenant) domain + + List persons = personService.getPeople(null, true, null, new PagingRequest(0, Integer.MAX_VALUE, null)).getPage(); + //assertEquals(2, personRefs.size()); // super-tenant: admin, guest (note: checking for 2 assumes that this test is run in a fresh bootstrap env) + for (PersonInfo person : persons) { String userName = person.getUserName(); - for (final String tenantDomain : tenants) + for (final String tenantDomain : tenantDomains) { assertFalse("Unexpected (tenant) user: "+userName, userName.endsWith(tenantDomain)); } @@ -290,7 +372,7 @@ public class MultiTDemoTest extends TestCase } - private void deleteTestAuthoritiesForTenant(final String[] uniqueGroupNames, final String tenantName) + private void deleteTestAuthoritiesForTenant(final String[] uniqueGroupNames, final String userName) { // Check deletion for tenant1 TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() @@ -298,7 +380,7 @@ public class MultiTDemoTest extends TestCase public Object doWork() throws Exception { // find person - NodeRef personNodeRef = personService.getPerson(tenantName); + NodeRef personNodeRef = personService.getPerson(userName); NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); assertNotNull(homeSpaceRef); @@ -309,9 +391,9 @@ public class MultiTDemoTest extends TestCase } return null; } - }, tenantName); + }, userName); } - private void createTestAuthoritiesForTenant(final String[] uniqueGroupNames, final String tenantName) + private void createTestAuthoritiesForTenant(final String[] uniqueGroupNames, final String userName) { // Create groups for tenant TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() @@ -319,7 +401,7 @@ public class MultiTDemoTest extends TestCase public Object doWork() throws Exception { // find person - NodeRef personNodeRef = personService.getPerson(tenantName); + NodeRef personNodeRef = personService.getPerson(userName); NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); assertNotNull(homeSpaceRef); @@ -331,17 +413,17 @@ public class MultiTDemoTest extends TestCase return null; } - }, tenantName); + }, userName); } - private void checkTestAuthoritiesPresence(final String[] uniqueGroupNames, final String tenantName, final boolean shouldPresent) + private void checkTestAuthoritiesPresence(final String[] uniqueGroupNames, final String userName, final boolean shouldPresent) { // Check that created permissions are not visible to tenant 2 TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() { public Object doWork() throws Exception { - NodeRef personNodeRef = personService.getPerson(tenantName); + NodeRef personNodeRef = personService.getPerson(userName); NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); Set perms = permissionService.getAllSetPermissions(homeSpaceRef); Set auths = authorityService.getAllAuthorities(AuthorityType.GROUP); @@ -363,12 +445,314 @@ public class MultiTDemoTest extends TestCase return null; } - }, tenantName); + }, userName); } - // TODO fix CLOUD-1348 - public void xtest02NonSharedGroupDeletion() + private void createGroup(String shortName, String parentShortName) { + // create new Group using authority Service + String groupName = this.authorityService.getName(AuthorityType.GROUP, shortName); + if (this.authorityService.authorityExists(groupName) == false) + { + String parentGroupName = null; + if (parentShortName != null) + { + parentGroupName = this.authorityService.getName(AuthorityType.GROUP, parentShortName); + if (this.authorityService.authorityExists(parentGroupName) == false) + { + logger.warn("Parent group does not exist: " + parentShortName); + return; + } + } + + this.authorityService.createAuthority(AuthorityType.GROUP, shortName); + + if (parentGroupName != null) + { + addToGroup(parentShortName, groupName); + } + } + else + { + logger.warn("Group already exists: " + shortName); + } + } + + private void addToGroup(String parentGroupShortName, String authorityName) + { + String parentGroupName = this.authorityService.getName(AuthorityType.GROUP, parentGroupShortName); + authorityService.addAuthority(parentGroupName, authorityName); + } + + private NodeRef createUser(String baseUserName, String tenantDomain, String password) + { + String userName = tenantService.getDomainUser(baseUserName, tenantDomain); + + NodeRef personNodeRef = null; + + if (! this.authenticationService.authenticationExists(userName)) + { + NodeRef baseHomeFolder = getUserHomesNodeRef(SPACES_STORE); + + // Create the users home folder + NodeRef homeFolder = createHomeSpaceFolderNode( + baseHomeFolder, + baseUserName, + userName); + + // Create the authentication + this.authenticationService.createAuthentication(userName, password.toCharArray()); + + // Create the person + Map personProperties = new HashMap(); + personProperties.put(ContentModel.PROP_USERNAME, userName); + personProperties.put(ContentModel.PROP_HOMEFOLDER, homeFolder); + personProperties.put(ContentModel.PROP_FIRSTNAME, baseUserName); + personProperties.put(ContentModel.PROP_LASTNAME, baseUserName+"-"+tenantDomain); // add domain suffix here for demo only + personProperties.put(ContentModel.PROP_EMAIL, userName); + + personNodeRef = this.personService.createPerson(personProperties); + + // ensure the user can access their own Person object + this.permissionService.setPermission(personNodeRef, userName, permissionService.getAllPermission(), true); + + NodeRef checkHomeSpaceRef = (NodeRef)nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); + assertNotNull(checkHomeSpaceRef); + + logger.info("Created user " + userName); + } + else + { + personNodeRef = personService.getPerson(userName); + + logger.info("Found existing user " + userName); + } + + return personNodeRef; + } + + private void loginLogoutUser(String username, String password) + { + // authenticate via the authentication service + authenticationService.authenticate(username, password.toCharArray()); + + // set the user name as stored by the back end + username = authenticationService.getCurrentUserName(); + + NodeRef personRef = personService.getPerson(username); + NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personRef, ContentModel.PROP_HOMEFOLDER); + + // check that the home space node exists - else user cannot login + if (nodeService.exists(homeSpaceRef) == false) + { + throw new InvalidNodeRefException(homeSpaceRef); + } + + // logout + authenticationService.clearCurrentSecurityContext(); + } + + private NodeRef getUserHomesNodeRef(StoreRef storeRef) + { + // get the "User Homes" location + return findFolderNodeRef(storeRef, "/app:company_home/app:user_homes"); + } + + private NodeRef getWebClientExtensionNodeRef(StoreRef storeRef) + { + // get the "Web Client Extensions" location + return findFolderNodeRef(storeRef, "/app:company_home/app:dictionary/app:webclient_extension"); + } + + private NodeRef findFolderNodeRef(StoreRef storeRef, String folderXPath) + { + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + + List nodeRefs = searchService.selectNodes(storeRootNodeRef, folderXPath, null, namespaceService, false); + + NodeRef folderNodeRef = null; + if (nodeRefs.size() != 1) + { + throw new AlfrescoRuntimeException("Cannot find folder location: " + folderXPath); + } + else + { + folderNodeRef = nodeRefs.get(0); + } + return folderNodeRef; + } + + private NodeRef createFolderNode(NodeRef parentFolderNodeRef, String nameValue) + { + if (nameValue != null) + { + Map folderProps = new HashMap(); + folderProps.put(ContentModel.PROP_NAME, nameValue); + + return this.nodeService.createNode( + parentFolderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nameValue), + ContentModel.TYPE_FOLDER, + folderProps).getChildRef(); + } + + return null; + } + + private NodeRef createCategory(StoreRef storeRef, NodeRef parentCategoryRef, String name, String description) + { + // create category using categoryservice + NodeRef ref; + if (parentCategoryRef == null) + { + ref = this.categoryService.createRootCategory(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE, name); + } + else + { + ref = categoryService.createCategory(parentCategoryRef, name); + } + + // apply the titled aspect - for description + Map titledProps = new HashMap(1, 1.0f); + titledProps.put(ContentModel.PROP_DESCRIPTION, description); + this.nodeService.addAspect(ref, ContentModel.ASPECT_TITLED, titledProps); + + return ref; + } + + private NodeRef createHomeSpaceFolderNode(NodeRef folderNodeRef, String spaceName, String userName) + { + if (spaceName != null) + { + Map folderProps = new HashMap(); + folderProps.put(ContentModel.PROP_NAME, spaceName); + + NodeRef nodeRef = this.nodeService.createNode( + folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, spaceName), + ContentModel.TYPE_FOLDER, + folderProps).getChildRef(); + + // apply the uifacets aspect - icon and title props + Map uiFacetsProps = new HashMap(3); + uiFacetsProps.put(ApplicationModel.PROP_ICON, "space-icon-default"); + uiFacetsProps.put(ContentModel.PROP_TITLE, spaceName); + this.nodeService.addAspect(nodeRef, ApplicationModel.ASPECT_UIFACETS, uiFacetsProps); + + setupHomeSpacePermissions(nodeRef, userName); + + return nodeRef; + } + + return null; + } + + private void setupHomeSpacePermissions(NodeRef homeSpaceRef, String userName) + { + // Admin Authority has full permissions by default (automatic - set in the permission config) + // give full permissions to the new user + this.permissionService.setPermission(homeSpaceRef, userName, permissionService.getAllPermission(), true); + + // by default other users will only have GUEST access to the space contents + String permission = "Consumer"; + + if (permission != null && permission.length() != 0) + { + this.permissionService.setPermission(homeSpaceRef, permissionService.getAllAuthorities(), permission, true); + } + + // the new user is the OWNER of their own space and always has full permissions + this.ownableService.setOwner(homeSpaceRef, userName); + this.permissionService.setPermission(homeSpaceRef, permissionService.getOwnerAuthority(), permissionService.getAllPermission(), true); + + // now detach (if we did this first we could not set any permissions!) + this.permissionService.setInheritParentPermissions(homeSpaceRef, false); + } + + private NodeRef getHomeSpaceFolderNode(String userName) + { + return (NodeRef)this.nodeService.getProperty(personService.getPerson(userName), ContentModel.PROP_HOMEFOLDER); + } + + private NodeRef addContent(NodeRef spaceRef, String name, String textData, String mimeType) + { + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, name); + + ChildAssociationRef association = nodeService.createNode(spaceRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + contentProps); + + NodeRef content = association.getChildRef(); + + // add titled aspect (for Web Client display) + Map titledProps = new HashMap(); + titledProps.put(ContentModel.PROP_TITLE, name); + titledProps.put(ContentModel.PROP_DESCRIPTION, name); + this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); + + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); + + writer.setMimetype(mimeType); + writer.setEncoding("UTF-8"); + + writer.putContent(textData); + + return content; + } + + private NodeRef addContent(NodeRef spaceRef, String name, InputStream is, String mimeType) + { + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, name); + + ChildAssociationRef association = nodeService.createNode(spaceRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), + ContentModel.TYPE_CONTENT, + contentProps); + + NodeRef content = association.getChildRef(); + + // add titled aspect (for Web Client display) + Map titledProps = new HashMap(); + titledProps.put(ContentModel.PROP_TITLE, name); + titledProps.put(ContentModel.PROP_DESCRIPTION, name); + this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); + + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); + + writer.setMimetype(mimeType); + writer.setEncoding("UTF-8"); + + writer.putContent(is); + + return content; + } + + private SiteInfo createSite(String siteId) + { + SiteInfo siteInfo = siteService.createSite(null, siteId, "title - "+siteId, "description - "+siteId, SiteVisibility.PRIVATE); + + // ensure that the Document Library folder is pre-created so that test code can start creating content straight away. + // At the time of writing V4.1 does not create this folder automatically, but Thor does. + NodeRef result = siteService.getContainer(siteId, SiteService.DOCUMENT_LIBRARY); + if (result == null) + { + result = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); + } + + return siteInfo; + } + + public void test02NonSharedGroupDeletion() + { + // ignore common setup - test here explicitly + final String tenantDomain1 = TEST_RUN+".groupdel1"; final String tenantDomain2 = TEST_RUN+".groupdel2"; @@ -434,9 +818,10 @@ public class MultiTDemoTest extends TestCase } } - // TODO fix CLOUD-1348 - public void xtest03SharedGroupDeletion() + public void test03SharedGroupDeletion() { + // ignore common setup - test here explicitly + final String tenantDomain1 = TEST_RUN+".groupdel3"; final String tenantDomain2 = TEST_RUN+".groupdel4"; @@ -526,54 +911,11 @@ public class MultiTDemoTest extends TestCase } - private void createTenant(final String tenantDomain) - { - // create tenants (if not already created) - TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - if (! tenantAdminService.existsTenant(tenantDomain)) - { - //tenantAdminService.createTenant(tenantDomain, DEFAULT_ADMIN_PW.toCharArray(), ROOT_DIR + "/" + tenantDomain); - tenantAdminService.createTenant(tenantDomain, (DEFAULT_ADMIN_PW+" "+tenantDomain).toCharArray(), null); // use default root dir - - logger.info("Created tenant " + tenantDomain); - } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - - private void deleteTenant(final String tenantDomain) - { - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - // delete tenant (if it exists) - TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - if (tenantAdminService.existsTenant(tenantDomain)) - { - tenantAdminService.deleteTenant(tenantDomain); - - logger.info("Deleted tenant " + tenantDomain); - } - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - return null; - } - }); - } public void test04_ETHREEOH_2015() { + // ignore common setup - test here explicitly + final String tenantDomain1 = TEST_RUN+".one.ethreeoh2015"; final String tenantDomain2 = TEST_RUN+".two.ethreeoh2015"; @@ -607,44 +949,32 @@ public class MultiTDemoTest extends TestCase } - public void test05CreateUsers() throws Throwable + public void test05ValidateUsers() throws Throwable { - logger.info("Create demo users"); + // use common setup + logger.info("Validate demo users"); - List persons = personService.getPeople(null, null, null, new PagingRequest(0, Integer.MAX_VALUE, null)).getPage(); - //assertEquals(2, personRefs.size()); // super-tenant: admin, guest (note: checking for 2 assumes that this test is run in a fresh bootstrap env) - for (PersonInfo person : persons) + AuthenticationUtil.runAs(new RunAsWork() { - String userName = person.getUserName(); - for (final String tenantDomain : tenants) + public Object doWork() throws Exception { - assertFalse("Unexpected (tenant) user: "+userName, userName.endsWith(tenantDomain)); + List persons = personService.getPeople(null, true, null, new PagingRequest(0, Integer.MAX_VALUE, null)).getPage(); + //assertEquals(2, personRefs.size()); // super-tenant: admin, guest (note: checking for 2 assumes that this test is run in a fresh bootstrap env) + for (PersonInfo person : persons) + { + String userName = person.getUserName(); + for (final String tenantDomain : tenants) + { + assertFalse("Unexpected (tenant) user: "+userName, userName.endsWith(tenantDomain)); + } + } + + return null; } - } + }, AuthenticationUtil.getAdminUserName()); try { - for (final String tenantDomain : tenants) - { - String tenantAdminName = tenantService.getDomainUser(AuthenticationUtil.getAdminUserName(), tenantDomain); - - TenantUtil.runAsPrimaryTenant(new TenantRunAsWork() - { - public Object doWork() throws Exception - { - createUser(TEST_USER1, tenantDomain, TEST_USER1+" "+tenantDomain); - createUser(TEST_USER2, tenantDomain, TEST_USER2+" "+tenantDomain); - - if (tenantDomain.equals(TEST_TENANT_DOMAIN2)) - { - createUser(TEST_USER3, tenantDomain, TEST_USER3+" "+tenantDomain); - } - - return null; - } - }, tenantAdminName); - } - for (final String tenantDomain : tenants) { String tenantAdminName = tenantService.getDomainUser(AuthenticationUtil.getAdminUserName(), tenantDomain); @@ -662,7 +992,7 @@ public class MultiTDemoTest extends TestCase String userName = (String)nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); assertTrue(userName.endsWith(tenantDomain)); - logger.info("Create users: get all people - found user: "+userName); + logger.info("Validate users: get all people - found user: "+userName); NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personRef, ContentModel.PROP_HOMEFOLDER); assertNotNull(homeSpaceRef); @@ -693,6 +1023,7 @@ public class MultiTDemoTest extends TestCase public void test06LoginTenantUsers() throws Throwable { + // use common setup logger.info("Login tenant users"); try @@ -1369,23 +1700,9 @@ public class MultiTDemoTest extends TestCase } } - // TODO fix CLOUD-1348 - // pseudo cleanup - if this test runs last - public void xtest20DeleteAllTenants() - { - logger.info("test delete tenants"); - - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - List allTenants = tenantAdminService.getAllTenants(); - for (final Tenant tenant : allTenants) - { - deleteTenant(tenant.getTenantDomain()); - } - } // TODO pending CLOUD-1350 fix - public void xtest21_ALF_12732() + public void xtest20_ALF_12732() { final String tenantDomain1 = TEST_RUN+".one.alf12732"; @@ -1425,7 +1742,7 @@ public class MultiTDemoTest extends TestCase } } - public void test22_ALF_14354() + public void test21_ALF_14354() { final String tenantDomain1 = TEST_RUN+".one.alf14354"; final String tenantDomain2 = TEST_RUN+".two.alf14354"; @@ -1460,304 +1777,31 @@ public class MultiTDemoTest extends TestCase }, tenantAdminName); } - private void createGroup(String shortName, String parentShortName) + + private int searchForDataDictionary(String tenantAdminName, final String query) { - // create new Group using authority Service - String groupName = this.authorityService.getName(AuthorityType.GROUP, shortName); - if (this.authorityService.authorityExists(groupName) == false) + return AuthenticationUtil.runAs(new RunAsWork() { - String parentGroupName = null; - if (parentShortName != null) - { - parentGroupName = this.authorityService.getName(AuthorityType.GROUP, parentShortName); - if (this.authorityService.authorityExists(parentGroupName) == false) - { - logger.warn("Parent group does not exist: " + parentShortName); - return; - } - } - - this.authorityService.createAuthority(AuthorityType.GROUP, shortName); - - if (parentGroupName != null) - { - addToGroup(parentShortName, groupName); - } + public Integer doWork() throws Exception + { + ResultSet resultSet = searchService.query(SPACES_STORE, SearchService.LANGUAGE_LUCENE, query, null); + return resultSet.length(); + } + }, tenantAdminName); + } + + // pseudo cleanup - if this test runs last + public void test22DeleteAllTenants() + { + logger.info("test delete tenants"); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + List allTenants = tenantAdminService.getAllTenants(); + for (final Tenant tenant : allTenants) + { + deleteTenant(tenant.getTenantDomain()); } - else - { - logger.warn("Group already exists: " + shortName); - } - } - - private void addToGroup(String parentGroupShortName, String authorityName) - { - String parentGroupName = this.authorityService.getName(AuthorityType.GROUP, parentGroupShortName); - authorityService.addAuthority(parentGroupName, authorityName); - } - - private NodeRef createUser(String baseUserName, String tenantDomain, String password) - { - String userName = tenantService.getDomainUser(baseUserName, tenantDomain); - - NodeRef personNodeRef = null; - - if (! this.authenticationService.authenticationExists(userName)) - { - NodeRef baseHomeFolder = getUserHomesNodeRef(SPACES_STORE); - - // Create the users home folder - NodeRef homeFolder = createHomeSpaceFolderNode( - baseHomeFolder, - baseUserName, - userName); - - // Create the authentication - this.authenticationService.createAuthentication(userName, password.toCharArray()); - - // Create the person - Map personProperties = new HashMap(); - personProperties.put(ContentModel.PROP_USERNAME, userName); - personProperties.put(ContentModel.PROP_HOMEFOLDER, homeFolder); - personProperties.put(ContentModel.PROP_FIRSTNAME, baseUserName); - personProperties.put(ContentModel.PROP_LASTNAME, baseUserName+"-"+tenantDomain); // add domain suffix here for demo only - personProperties.put(ContentModel.PROP_EMAIL, userName); - - personNodeRef = this.personService.createPerson(personProperties); - - // ensure the user can access their own Person object - this.permissionService.setPermission(personNodeRef, userName, permissionService.getAllPermission(), true); - - NodeRef checkHomeSpaceRef = (NodeRef)nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); - assertNotNull(checkHomeSpaceRef); - - logger.info("Created user " + userName); - } - else - { - personNodeRef = personService.getPerson(userName); - - logger.info("Found existing user " + userName); - } - - return personNodeRef; - } - - private void loginLogoutUser(String username, String password) - { - // authenticate via the authentication service - authenticationService.authenticate(username, password.toCharArray()); - - // set the user name as stored by the back end - username = authenticationService.getCurrentUserName(); - - NodeRef personRef = personService.getPerson(username); - NodeRef homeSpaceRef = (NodeRef)nodeService.getProperty(personRef, ContentModel.PROP_HOMEFOLDER); - - // check that the home space node exists - else user cannot login - if (nodeService.exists(homeSpaceRef) == false) - { - throw new InvalidNodeRefException(homeSpaceRef); - } - - // logout - authenticationService.clearCurrentSecurityContext(); - } - - private NodeRef getUserHomesNodeRef(StoreRef storeRef) - { - // get the "User Homes" location - return findFolderNodeRef(storeRef, "/app:company_home/app:user_homes"); - } - - private NodeRef getWebClientExtensionNodeRef(StoreRef storeRef) - { - // get the "Web Client Extensions" location - return findFolderNodeRef(storeRef, "/app:company_home/app:dictionary/app:webclient_extension"); - } - - private NodeRef findFolderNodeRef(StoreRef storeRef, String folderXPath) - { - ResultSet rs = this.searchService.query(storeRef, SearchService.LANGUAGE_XPATH, folderXPath); - - NodeRef folderNodeRef = null; - if (rs.length() != 1) - { - throw new AlfrescoRuntimeException("Cannot find folder location: " + folderXPath); - } - else - { - folderNodeRef = rs.getNodeRef(0); - } - rs.close(); - return folderNodeRef; - } - - private NodeRef createFolderNode(NodeRef parentFolderNodeRef, String nameValue) - { - if (nameValue != null) - { - Map folderProps = new HashMap(); - folderProps.put(ContentModel.PROP_NAME, nameValue); - - return this.nodeService.createNode( - parentFolderNodeRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nameValue), - ContentModel.TYPE_FOLDER, - folderProps).getChildRef(); - } - - return null; - } - - private NodeRef createCategory(StoreRef storeRef, NodeRef parentCategoryRef, String name, String description) - { - // create category using categoryservice - NodeRef ref; - if (parentCategoryRef == null) - { - ref = this.categoryService.createRootCategory(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE, name); - } - else - { - ref = categoryService.createCategory(parentCategoryRef, name); - } - - // apply the titled aspect - for description - Map titledProps = new HashMap(1, 1.0f); - titledProps.put(ContentModel.PROP_DESCRIPTION, description); - this.nodeService.addAspect(ref, ContentModel.ASPECT_TITLED, titledProps); - - return ref; - } - - private NodeRef createHomeSpaceFolderNode(NodeRef folderNodeRef, String spaceName, String userName) - { - if (spaceName != null) - { - Map folderProps = new HashMap(); - folderProps.put(ContentModel.PROP_NAME, spaceName); - - NodeRef nodeRef = this.nodeService.createNode( - folderNodeRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, spaceName), - ContentModel.TYPE_FOLDER, - folderProps).getChildRef(); - - // apply the uifacets aspect - icon and title props - Map uiFacetsProps = new HashMap(3); - uiFacetsProps.put(ApplicationModel.PROP_ICON, "space-icon-default"); - uiFacetsProps.put(ContentModel.PROP_TITLE, spaceName); - this.nodeService.addAspect(nodeRef, ApplicationModel.ASPECT_UIFACETS, uiFacetsProps); - - setupHomeSpacePermissions(nodeRef, userName); - - return nodeRef; - } - - return null; - } - - private void setupHomeSpacePermissions(NodeRef homeSpaceRef, String userName) - { - // Admin Authority has full permissions by default (automatic - set in the permission config) - // give full permissions to the new user - this.permissionService.setPermission(homeSpaceRef, userName, permissionService.getAllPermission(), true); - - // by default other users will only have GUEST access to the space contents - String permission = "Consumer"; - - if (permission != null && permission.length() != 0) - { - this.permissionService.setPermission(homeSpaceRef, permissionService.getAllAuthorities(), permission, true); - } - - // the new user is the OWNER of their own space and always has full permissions - this.ownableService.setOwner(homeSpaceRef, userName); - this.permissionService.setPermission(homeSpaceRef, permissionService.getOwnerAuthority(), permissionService.getAllPermission(), true); - - // now detach (if we did this first we could not set any permissions!) - this.permissionService.setInheritParentPermissions(homeSpaceRef, false); - } - - private NodeRef getHomeSpaceFolderNode(String userName) - { - return (NodeRef)this.nodeService.getProperty(personService.getPerson(userName), ContentModel.PROP_HOMEFOLDER); - } - - private NodeRef addContent(NodeRef spaceRef, String name, String textData, String mimeType) - { - Map contentProps = new HashMap(); - contentProps.put(ContentModel.PROP_NAME, name); - - ChildAssociationRef association = nodeService.createNode(spaceRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), - ContentModel.TYPE_CONTENT, - contentProps); - - NodeRef content = association.getChildRef(); - - // add titled aspect (for Web Client display) - Map titledProps = new HashMap(); - titledProps.put(ContentModel.PROP_TITLE, name); - titledProps.put(ContentModel.PROP_DESCRIPTION, name); - this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); - - ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - - writer.setMimetype(mimeType); - writer.setEncoding("UTF-8"); - - writer.putContent(textData); - - return content; - } - - private NodeRef addContent(NodeRef spaceRef, String name, InputStream is, String mimeType) - { - Map contentProps = new HashMap(); - contentProps.put(ContentModel.PROP_NAME, name); - - ChildAssociationRef association = nodeService.createNode(spaceRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name), - ContentModel.TYPE_CONTENT, - contentProps); - - NodeRef content = association.getChildRef(); - - // add titled aspect (for Web Client display) - Map titledProps = new HashMap(); - titledProps.put(ContentModel.PROP_TITLE, name); - titledProps.put(ContentModel.PROP_DESCRIPTION, name); - this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); - - ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - - writer.setMimetype(mimeType); - writer.setEncoding("UTF-8"); - - writer.putContent(is); - - return content; - } - - private SiteInfo createSite(String siteId) - { - SiteInfo siteInfo = siteService.createSite(null, siteId, "title - "+siteId, "description - "+siteId, SiteVisibility.PRIVATE); - - // ensure that the Document Library folder is pre-created so that test code can start creating content straight away. - // At the time of writing V4.1 does not create this folder automatically, but Thor does. - NodeRef result = siteService.getContainer(siteId, SiteService.DOCUMENT_LIBRARY); - if (result == null) - { - result = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null); - } - - return siteInfo; } /* diff --git a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java index 2ff7d3f31e..dcf192cb97 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java +++ b/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptor.java @@ -106,9 +106,9 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep String methodName = invocation.getMethod().getName(); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { - logger.debug("Intercepting method " + methodName); + logger.trace("Intercepting method " + methodName); } Object[] args = invocation.getArguments(); @@ -118,11 +118,11 @@ public class MultiTNodeServiceInterceptor extends DelegatingIntroductionIntercep Object arg = args[i]; Object newArg = convertInboundValue(arg); - if (logger.isDebugEnabled()) + if (logger.isTraceEnabled()) { if (!EqualsHelper.nullSafeEquals(newArg, arg)) { - logger.debug( + logger.trace( "Argument converted: \n" + " Before: " + arg + "\n" + " After: " + newArg); diff --git a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java index 0868af2b44..640540ae6c 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java @@ -712,7 +712,7 @@ public class MultiTServiceImpl implements TenantService Tenant tenant = null; if (tenantEntity != null) { - tenant = new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot()); + tenant = new Tenant(tenantEntity.getTenantDomain(), tenantEntity.getEnabled(), tenantEntity.getContentRoot(), null); } return tenant; } diff --git a/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java b/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java new file mode 100644 index 0000000000..612d520fb0 --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/RunAsTenantInterceptor.java @@ -0,0 +1,78 @@ +/* + * 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.tenant; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * @since 4.2 + */ +public class RunAsTenantInterceptor implements MethodInterceptor +{ + public enum TENANT_TYPE + { + Default, + RealUser + } + + private TENANT_TYPE tenantType; + + public RunAsTenantInterceptor(TENANT_TYPE tenantType) + { + this.tenantType = tenantType; + } + + @Override + public Object invoke(final MethodInvocation mi) throws Throwable + { + TenantRunAsWork runAs = new TenantRunAsWork() + { + public Object doWork() throws Exception + { + try + { + return mi.proceed(); + } + catch(Throwable e) + { + e.printStackTrace(); + + // Re-throw the exception + if (e instanceof RuntimeException) + { + throw (RuntimeException) e; + } + throw new RuntimeException("Failed to execute in RunAsTenant context", e); + } + } + }; + + if (tenantType == TENANT_TYPE.Default) + { + return TenantUtil.runAsDefaultTenant(runAs); + } + else + { + return TenantUtil.runAsPrimaryTenant(runAs, AuthenticationUtil.getFullyAuthenticatedUser()); + } + } +} diff --git a/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java index 079fdddf09..20c9ee22ae 100644 --- a/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/SingleTAdminServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -146,7 +146,12 @@ public class SingleTAdminServiceImpl implements TenantAdminService /** * @throws UnsupportedOperationException always */ - public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir) + public void createTenant(String tenantDomain, char[] adminRawPassword, String contentRoot) + { + throw new UnsupportedOperationException("Single tenant mode is active."); + } + + public void createTenant(String tenantDomain, char[] adminRawPassword, String contentRoot, String dbUrl) { throw new UnsupportedOperationException("Single tenant mode is active."); } diff --git a/source/java/org/alfresco/repo/tenant/TenantAdminService.java b/source/java/org/alfresco/repo/tenant/TenantAdminService.java index 0e1ef25bb2..f4f6d5df6d 100644 --- a/source/java/org/alfresco/repo/tenant/TenantAdminService.java +++ b/source/java/org/alfresco/repo/tenant/TenantAdminService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -63,12 +63,15 @@ public interface TenantAdminService extends TenantUserService */ public void createTenant(String tenantDomain, char[] adminRawPassword); + + public void createTenant(String tenantDomain, char[] adminRawPassword, String contentRoot); - public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir); + // experimental (unsupported) + public void createTenant(String tenantDomain, char[] adminRawPassword, String contentRoot, String dbUrl); public void exportTenant(String tenantDomain, File directoryDestination); - public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir); + public void importTenant(String tenantDomain, File directorySource, String contentRoot); public boolean existsTenant(String tenantDomain); diff --git a/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java b/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java new file mode 100644 index 0000000000..f62adb445a --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/TenantBasicDataSource.java @@ -0,0 +1,49 @@ +/* + * 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.tenant; + +import java.sql.SQLException; + +import org.apache.commons.dbcp.BasicDataSource; + +/** + * Experimental + * + * @author janv + * @since 4.2 + */ +public class TenantBasicDataSource extends BasicDataSource +{ + public TenantBasicDataSource(BasicDataSource bds, String tenantUrl, int tenantMaxActive) throws SQLException + { + // tenant-specific + this.setUrl(tenantUrl); + this.setMaxActive(tenantMaxActive == -1 ? bds.getMaxActive() : tenantMaxActive); + + // defaults + this.setUsername(bds.getUsername()); + this.setPassword(bds.getPassword()); + this.setDriverClassName(bds.getDriverClassName()); + + this.setMaxIdle(bds.getMaxIdle()); + this.setMinIdle(bds.getMinIdle()); + + // TODO other default settings + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/tenant/TenantInterpreter.java b/source/java/org/alfresco/repo/tenant/TenantInterpreter.java index 63d5bf89c5..bd8691f932 100644 --- a/source/java/org/alfresco/repo/tenant/TenantInterpreter.java +++ b/source/java/org/alfresco/repo/tenant/TenantInterpreter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -28,6 +28,7 @@ import java.util.List; import org.alfresco.repo.admin.BaseInterpreter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.MutableAuthenticationService; @@ -142,7 +143,12 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon return executeCommand(line); } }; - return transactionService.getRetryingTransactionHelper().doInTransaction(txnWork); + + // from Thor + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setMaxRetries(1); + + return txnHelper.doInTransaction(txnWork); } }; return AuthenticationUtil.runAs(executeWork, AuthenticationUtil.SYSTEM_USER_NAME); @@ -224,8 +230,8 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon { if (tenant.isEnabled()) { - String rootContentStoreDir = tenant.getRootContentStoreDir(); - out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")"); + String contentRoot = tenant.getRootContentStoreDir(); + out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + contentRoot + ")"); } } @@ -235,8 +241,8 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon { if (! tenant.isEnabled()) { - String rootContentStoreDir = tenant.getRootContentStoreDir(); - out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")"); + String contentRoot = tenant.getRootContentStoreDir(); + out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + contentRoot + ")"); } } } @@ -251,14 +257,14 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon String tenantDomain = new String(command[2]).toLowerCase(); Tenant tenant = tenantAdminService.getTenant(tenantDomain); - String rootContentStoreDir = tenant.getRootContentStoreDir(); + String contentRoot = tenant.getRootContentStoreDir(); if (tenant.isEnabled()) { - out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")"); + out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + contentRoot + ")"); } else { - out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")"); + out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + contentRoot + ")"); } } @@ -270,21 +276,36 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon else if (command[0].equals("create")) { - if ((command.length != 3) && (command.length != 4)) + if ((command.length < 3) || (command.length > 5)) { return "Syntax Error, try 'help'.\n"; } String newTenant = new String(command[1]).toLowerCase(); char[] tenantAdminRawPassword = new String(command[2]).toCharArray(); - String rootContentStoreDir = null; - if (command.length == 4) + String contentRoot = null; + if (command.length >= 4) { - rootContentStoreDir = new String(command[3]); + contentRoot = new String(command[3]); + if ("null".equals(contentRoot)) + { + contentRoot = null; + } } - tenantAdminService.createTenant(newTenant, tenantAdminRawPassword, rootContentStoreDir); + String dbUrl = null; + if (command.length >= 5) + { + // experimental (unsupported) + dbUrl = new String(command[4]); + if ("null".equals(dbUrl)) + { + dbUrl = null; + } + } + tenantAdminService.createTenant(newTenant, tenantAdminRawPassword, contentRoot, dbUrl); + out.println("created tenant: " + newTenant); } @@ -298,13 +319,13 @@ public class TenantInterpreter extends BaseInterpreter implements ApplicationCon String newTenant = new String(command[1]).toLowerCase(); File directorySource = new File(command[2]); - String rootContentStoreDir = null; + String contentRoot = null; if (command.length == 4) { - rootContentStoreDir = new String(command[3]); + contentRoot = new String(command[3]); } - tenantAdminService.importTenant(newTenant, directorySource, rootContentStoreDir); + tenantAdminService.importTenant(newTenant, directorySource, contentRoot); out.println("imported tenant: " + newTenant); } diff --git a/source/java/org/alfresco/repo/tenant/TenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/TenantRoutingContentStore.java index ef6d6f1ee2..cc866f9e43 100644 --- a/source/java/org/alfresco/repo/tenant/TenantRoutingContentStore.java +++ b/source/java/org/alfresco/repo/tenant/TenantRoutingContentStore.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -24,4 +24,5 @@ package org.alfresco.repo.tenant; */ public interface TenantRoutingContentStore extends TenantDeployer { + public String getRootLocation(); } diff --git a/source/java/org/alfresco/repo/tenant/TenantRoutingDataSource.java b/source/java/org/alfresco/repo/tenant/TenantRoutingDataSource.java new file mode 100644 index 0000000000..d8bd8b3656 --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/TenantRoutingDataSource.java @@ -0,0 +1,134 @@ +/* + * 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.tenant; + +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +import javax.sql.DataSource; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.apache.commons.dbcp.BasicDataSource; +import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * Experimental + * + * @author janv + * @since 4.2 + */ +public class TenantRoutingDataSource extends AbstractRoutingDataSource +{ + Map tenantDataSources = new HashMap(); + + private BasicDataSource baseDataSource; + + private TenantService tenantService; + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setBaseDataSource(BasicDataSource baseDataSource) + { + this.baseDataSource = baseDataSource; + } + + @Override + protected Object determineCurrentLookupKey() + { + //return tenantService.getCurrentUserDomain(); // note: this is re-entrant if it checks whether tenant is enabled ! + String runAsUser = AuthenticationUtil.getRunAsUser(); + String tenantDomain = TenantService.DEFAULT_DOMAIN; + if (runAsUser != null) + { + String[] parts = runAsUser.split(TenantService.SEPARATOR); + if (parts.length == 2) + { + tenantDomain = parts[1]; + } + } + return tenantDomain; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void afterPropertiesSet() + { + ParameterCheck.mandatory("baseDataSource", baseDataSource); + ParameterCheck.mandatory("tenantDataSources", tenantDataSources); + + String dbUrl = baseDataSource.getUrl(); + setTargetDataSources((Map)tenantDataSources); + + try + { + // default tenant + DataSource defaultTargetDataSource = new TenantBasicDataSource(baseDataSource, dbUrl, -1); + tenantDataSources.put(TenantService.DEFAULT_DOMAIN, defaultTargetDataSource); + setDefaultTargetDataSource(defaultTargetDataSource); + } + catch (SQLException se) + { + throw new RuntimeException(se); + } + + + + super.afterPropertiesSet(); + } + + public synchronized void addTenantDataSource(String tenantDomain, String dbUrl) throws SQLException + { + String currentTenantDomain = tenantService.getCurrentUserDomain(); + if (! TenantService.DEFAULT_DOMAIN.equals(currentTenantDomain)) + { + throw new RuntimeException("Unexpected - should not be in context of a tenant ["+currentTenantDomain+"]"); + } + + tenantDataSources.put(tenantDomain, new TenantBasicDataSource(baseDataSource, dbUrl, -1)); + + super.afterPropertiesSet(); // to update resolved data sources + } + + public synchronized void removeTenantDataSource(String tenantDomain) throws SQLException + { + String currentTenantDomain = tenantService.getCurrentUserDomain(); + if (! TenantService.DEFAULT_DOMAIN.equals(currentTenantDomain)) + { + throw new RuntimeException("Unexpected - should not be in context of a tenant ["+currentTenantDomain+"]"); + } + + tenantDataSources.remove(tenantDomain); + + super.afterPropertiesSet(); // to update resolved data sources + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException + { + return null; + } +} + diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index 3a0769ca0c..5cf9274989 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -29,13 +29,19 @@ import java.util.concurrent.locks.ReentrantLock; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.usage.UsageDAO; import org.alfresco.repo.domain.usage.UsageDAO.MapHandler; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; 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.repo.transaction.TransactionServiceImpl; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -47,12 +53,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; -import org.alfresco.repo.node.NodeServicePolicies; -import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; -import org.alfresco.repo.policy.JavaBehaviour; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.service.cmr.repository.ChildAssociationRef; - /** * User Usage Tracking Component - to allow user usages to be collapsed or re-calculated * @@ -187,16 +187,16 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements List tenants = tenantAdminService.getAllTenants(); for (Tenant tenant : tenants) { - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() throws Exception { bootstrapInternal(); return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant.getTenantDomain())); + }, tenant.getTenantDomain()); } - } + } } public void bootstrapInternal() @@ -310,7 +310,10 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements String username = (String)result.get("username"); String uuid = (String)result.get("uuid"); - users.put(username, new NodeRef(personStoreRef, uuid)); + if (! username.equalsIgnoreCase(AuthenticationUtil.getSystemUserName())) + { + users.put(username, new NodeRef(personStoreRef, uuid)); + } } }; usageDAO.getUsersWithUsage(tenantService.getName(personStoreRef), userHandler); @@ -406,7 +409,10 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements String username = (String)result.get("username"); String uuid = (String)result.get("uuid"); - users.put(username, new NodeRef(personStoreRef, uuid)); + if (! username.equalsIgnoreCase(AuthenticationUtil.getSystemUserName())) + { + users.put(username, new NodeRef(personStoreRef, uuid)); + } } }; @@ -592,13 +598,13 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements int collapseCount = 0; for (final NodeRef usageNodeRef : usageNodeRefs) { - Boolean collapsed = AuthenticationUtil.runAs(new RunAsWork() + Boolean collapsed = TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Boolean doWork() throws Exception { return collapseUsage(usageNodeRef); } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantService.getDomain(usageNodeRef.getStoreRef().getIdentifier()))); + }, tenantService.getDomain(usageNodeRef.getStoreRef().getIdentifier())); if (collapsed) { diff --git a/source/java/org/alfresco/repo/version/MigrationCleanupJob.java b/source/java/org/alfresco/repo/version/MigrationCleanupJob.java index aebf4ad448..cdc9cc24f0 100644 --- a/source/java/org/alfresco/repo/version/MigrationCleanupJob.java +++ b/source/java/org/alfresco/repo/version/MigrationCleanupJob.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -22,9 +22,10 @@ import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; 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.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.Job; @@ -167,15 +168,14 @@ public class MigrationCleanupJob implements Job List tenants = tenantAdminService.getAllTenants(); for (Tenant tenant : tenants) { - String tenantDomain = tenant.getTenantDomain(); - AuthenticationUtil.runAs(new RunAsWork() + TenantUtil.runAsSystemTenant(new TenantRunAsWork() { public Object doWork() throws Exception { migrationCleanup.executeCleanup(batchSize, threadCount); return null; } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + }, tenant.getTenantDomain()); } } } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index d90f1eca0e..aa033fb7f3 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -426,7 +426,7 @@ public class WorkflowServiceImpl implements WorkflowService String engineId = BPMEngineRegistry.getEngineId(workflowDefinitionId); WorkflowComponent component = getWorkflowComponent(engineId); WorkflowPath path = component.startWorkflow(workflowDefinitionId, parameters); - if(parameters!=null && parameters.containsKey(WorkflowModel.ASSOC_PACKAGE)) + if(path != null && parameters!=null && parameters.containsKey(WorkflowModel.ASSOC_PACKAGE)) { WorkflowInstance instance = path.getInstance(); workflowPackageComponent.setWorkflowForPackage(instance); diff --git a/source/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java b/source/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java index edda0c72cd..510b60062e 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java +++ b/source/java/org/alfresco/repo/workflow/activiti/script/ActivitiScriptBase.java @@ -168,6 +168,7 @@ public class ActivitiScriptBase { userName = AuthenticationUtil.getFullyAuthenticatedUser(); } + // The "System" user is a special case, which has no person object associated with it. if(userName != null && !AuthenticationUtil.SYSTEM_USER_NAME.equals(userName)) { diff --git a/source/java/org/alfresco/service/cmr/module/ModuleService.java b/source/java/org/alfresco/service/cmr/module/ModuleService.java index b5e222c976..582cd15650 100644 --- a/source/java/org/alfresco/service/cmr/module/ModuleService.java +++ b/source/java/org/alfresco/service/cmr/module/ModuleService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -69,4 +69,6 @@ public interface ModuleService * transaction. */ void startModules(); + + void shutdownModules(); } diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java index 5ac684c364..4715b3ec0d 100644 --- a/source/java/org/alfresco/service/cmr/security/PersonService.java +++ b/source/java/org/alfresco/service/cmr/security/PersonService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -233,6 +233,16 @@ public interface PersonService @Auditable(parameters = {"personRef"}) public void deletePerson(NodeRef personRef); + /** + * Delete the person identified by the given ref, and optionally delete + * the associated authentication, if one. + * + * @param personRef + * @param deleteAuthentication + */ + @Auditable(parameters = {"personRef", "deleteAuthentication"}) + public void deletePerson(NodeRef personRef, boolean deleteAuthentication); + /** * Get all the people we know about. *