diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index d515825da3..003794a0eb 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -45,7 +45,7 @@ - + @@ -55,6 +55,7 @@ org.alfresco.repo.content.ContentStore + org.alfresco.repo.content.ContentStoreCaps diff --git a/source/java/org/alfresco/repo/management/subsystems/CryptodocSubsystemProxyFactory.java b/source/java/org/alfresco/repo/management/subsystems/CryptodocSubsystemProxyFactory.java new file mode 100644 index 0000000000..8a49346ecb --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/CryptodocSubsystemProxyFactory.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2012 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.management.subsystems; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.alfresco.repo.tenant.TenantDeployer; +import org.alfresco.repo.tenant.TenantRoutingContentStore; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.support.DefaultPointcutAdvisor; + + +public class CryptodocSubsystemProxyFactory extends SubsystemProxyFactory +{ + private static final long serialVersionUID = 1L; + + public CryptodocSubsystemProxyFactory() + { + super(); + addAdvisor(0, new DefaultPointcutAdvisor(new MethodInterceptor() + { + public Object invoke(MethodInvocation mi) throws Throwable + { + Method method = mi.getMethod(); + try + { + switch (method.getName()) + { + case "getTenantRoutingContentStore": + return getTenantRoutingContentStore(mi); + case "getTenantDeployer": + return getTenantDeployer(mi); + default: + return mi.proceed(); + } + } + catch (InvocationTargetException e) + { + // Unwrap invocation target exceptions + throw e.getTargetException(); + } + } + + private TenantDeployer getTenantDeployer(MethodInvocation mi) + { + Object bean = locateBean(mi); + if (bean instanceof TenantDeployer) + { + return (TenantDeployer) bean; + } + return null; + } + + private TenantRoutingContentStore getTenantRoutingContentStore(MethodInvocation mi) + { + Object bean = locateBean(mi); + if (bean instanceof TenantRoutingContentStore) + { + return (TenantRoutingContentStore) bean; + } + return null; + } + })); + } +} diff --git a/source/java/org/alfresco/repo/management/subsystems/SubsystemProxyFactory.java b/source/java/org/alfresco/repo/management/subsystems/SubsystemProxyFactory.java index 2af9ea058c..7a9428443d 100644 --- a/source/java/org/alfresco/repo/management/subsystems/SubsystemProxyFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/SubsystemProxyFactory.java @@ -162,7 +162,7 @@ public class SubsystemProxyFactory extends ProxyFactoryBean implements Applicati // Bring our cached copies of the source beans in line with the application context factory, using a RW lock to // ensure consistency - private Object locateBean(MethodInvocation mi) + protected Object locateBean(MethodInvocation mi) { boolean haveWriteLock = false; this.lock.readLock().lock(); diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index f8476801a8..65f3686cf0 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -34,6 +34,7 @@ import net.sf.acegisecurity.providers.encoding.PasswordEncoder; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.admin.RepoModelDefinition; import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.content.ContentStoreCaps; import org.alfresco.repo.dictionary.DictionaryComponent; import org.alfresco.repo.domain.tenant.TenantAdminDAO; import org.alfresco.repo.domain.tenant.TenantEntity; @@ -282,11 +283,11 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo // register dictionary - to allow enable/disable tenant callbacks register(dictionaryComponent); - if (tenantFileContentStore instanceof TenantDeployer) + if (isTenantDeployer(tenantFileContentStore)) { // register file store - to allow enable/disable tenant callbacks // note: tenantFileContentStore must be registed before dictionaryRepositoryBootstrap - register((TenantDeployer)tenantFileContentStore, 0); + register(tenantDeployer(tenantFileContentStore), 0); } UserTransaction userTransaction = transactionService.getUserTransaction(); @@ -304,7 +305,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo for (Tenant tenant : tenants) { - if ((! (tenantFileContentStore instanceof TenantRoutingContentStore)) && (! tenantFileContentStore.getRootLocation().equals(tenant.getRootContentStoreDir()))) + if ((! (isTenantRoutingContentStore(tenantFileContentStore))) && (! tenantFileContentStore.getRootLocation().equals(tenant.getRootContentStoreDir()))) { // eg. ALF-14121 - MT will not work with replicating-content-services-context.sample if tenants are not co-mingled throw new AlfrescoRuntimeException("MT: cannot start tenants - TenantRoutingContentStore is not configured AND not all tenants use co-mingled content store"); @@ -438,9 +439,10 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo { dictionaryComponent.init(); - if (tenantFileContentStore instanceof TenantDeployer) + if (isTenantDeployer(tenantFileContentStore)) { - ((TenantDeployer)tenantFileContentStore).init(); + TenantDeployer deployer = tenantDeployer(tenantFileContentStore); + deployer.init(); } // callback @@ -553,9 +555,10 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo dictionaryComponent.init(); - if (tenantFileContentStore instanceof TenantDeployer) + if (isTenantDeployer(tenantFileContentStore)) { - ((TenantDeployer)tenantFileContentStore).init(); + TenantDeployer deployer = tenantDeployer(tenantFileContentStore); + deployer.init(); } // import tenant-specific stores @@ -1262,6 +1265,47 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } } + protected TenantRoutingContentStore tenantRoutingContentStore(ContentStore contentStore) + { + if (contentStore instanceof TenantRoutingContentStore) + { + return (TenantRoutingContentStore) contentStore; + } + else if (contentStore instanceof ContentStoreCaps) + { + ContentStoreCaps capabilities = (ContentStoreCaps) contentStore; + return (TenantRoutingContentStore) capabilities.getTenantRoutingContentStore(); + } + return null; + } + + protected boolean isTenantRoutingContentStore(ContentStore contentStore) + { + boolean router = tenantRoutingContentStore(contentStore) != null; + return router; + } + + protected TenantDeployer tenantDeployer(ContentStore contentStore) + { + if (contentStore instanceof TenantDeployer) + { + return (TenantDeployer) contentStore; + } + else if (contentStore instanceof ContentStoreCaps) + { + ContentStoreCaps capabilities = (ContentStoreCaps) contentStore; + return (TenantDeployer) capabilities.getTenantDeployer(); + } + return null; + } + + protected boolean isTenantDeployer(ContentStore contentStore) + { + boolean deployer = tenantDeployer(contentStore) != null; + return deployer; + } + + private void initTenant(String tenantDomain, String contentRoot, String dbUrl) { validateTenantName(tenantDomain); @@ -1277,7 +1321,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo } else { - if (! (tenantFileContentStore instanceof TenantRoutingContentStore)) + if (! isTenantRoutingContentStore(tenantFileContentStore)) { // eg. ALF-14121 - MT will not work with replicating-content-services-context.sample throw new AlfrescoRuntimeException("MT: cannot initialse tenant - TenantRoutingContentStore is not configured AND tenant is not using co-mingled content store (ie. default root location)"); diff --git a/source/test-java/org/alfresco/repo/tenant/MultiTAdminServiceImplTest.java b/source/test-java/org/alfresco/repo/tenant/MultiTAdminServiceImplTest.java new file mode 100644 index 0000000000..731e0cd2f3 --- /dev/null +++ b/source/test-java/org/alfresco/repo/tenant/MultiTAdminServiceImplTest.java @@ -0,0 +1,180 @@ +package org.alfresco.repo.tenant; + +import static org.junit.Assert.*; + +import org.alfresco.repo.content.AbstractContentStore; +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.content.ContentStoreCaps; +import org.alfresco.service.cmr.repository.ContentReader; +import org.junit.Before; +import org.junit.Test; + +public class MultiTAdminServiceImplTest +{ + private MultiTAdminServiceImpl tenantAdmin; + + @Before + public void setUp() throws Exception + { + tenantAdmin = new MultiTAdminServiceImpl(); + } + + @Test + public void testTenantDeployer() + { + ContentStore contentStore = new ConcreteTenantDeployer(); + TenantDeployer tenantDeployer = tenantAdmin.tenantDeployer(contentStore); + assertNotNull(tenantDeployer); + } + + @Test + public void testTenantDeployerRetrievedByContentStoreCaps() + { + ContentStore contentStore = new FakeSubsystemProxy(false); + TenantDeployer tenantDeployer = tenantAdmin.tenantDeployer(contentStore); + assertNotNull(tenantDeployer); + } + + @Test + public void testTenantDeployerMayBeNullWhenInterfaceNotImplemented() + { + ContentStore contentStore = new BaseStore(); + TenantDeployer tenantDeployer = tenantAdmin.tenantDeployer(contentStore); + assertNull(tenantDeployer); + } + + @Test + public void testTenantDeployerMayBeNullWhenProxyingAndInterfaceNotImplemented() + { + // Represents proxy in front of non-TenantDeployer ContentStore + ContentStore contentStore = new FakeSubsystemProxy(true); + TenantDeployer tenantDeployer = tenantAdmin.tenantDeployer(contentStore); + assertNull(tenantDeployer); + } + + @Test + public void testTenantRoutingContentStore() + { + ContentStore contentStore = new ConcreteTenantRoutingContentStore(); + TenantRoutingContentStore router = tenantAdmin.tenantRoutingContentStore(contentStore); + assertNotNull(router); + } + + @Test + public void testTenantRoutingContentStoreRetrievedByContentStoreCaps() + { + ContentStore contentStore = new FakeSubsystemProxy(false); + TenantRoutingContentStore router = tenantAdmin.tenantRoutingContentStore(contentStore); + assertNotNull(router); + } + + @Test + public void testTenantRoutingContentStoreMayBeNullWhenInterfaceNotImplemented() + { + ContentStore contentStore = new BaseStore(); + TenantRoutingContentStore router = tenantAdmin.tenantRoutingContentStore(contentStore); + assertNull(router); + } + + @Test + public void testTenantRoutingContentStoreMayBeNullWhenProxyingAndInterfaceNotImplemented() + { + // Represents proxy in front of non-TenantRoutingContentStore ContentStore + ContentStore contentStore = new FakeSubsystemProxy(true); + TenantRoutingContentStore router = tenantAdmin.tenantRoutingContentStore(contentStore); + assertNull(router); + } + + + + + + + // This is implemented by the CryptodocSubsystemProxyFactory in real life. + private static class FakeSubsystemProxy extends BaseStore implements ContentStoreCaps + { + private boolean returnNull; + + FakeSubsystemProxy(boolean returnNull) + { + this.returnNull = returnNull; + } + + @Override + public TenantDeployer getTenantRoutingContentStore() + { + // would return the underlying ContentStore bean in real life (or null). + return returnNull ? null : new ConcreteTenantRoutingContentStore(); + } + + @Override + public TenantDeployer getTenantDeployer() + { + // would return the underlying ContentStore bean in real life (or null). + return returnNull ? null : new ConcreteTenantDeployer(); + } + } + + private static class ConcreteTenantDeployer extends BaseStore implements TenantDeployer + { + @Override + public void onEnableTenant() + { + } + + @Override + public void onDisableTenant() + { + } + + @Override + public void init() + { + } + + @Override + public void destroy() + { + } + } + + private static class ConcreteTenantRoutingContentStore extends BaseStore implements TenantRoutingContentStore + { + @Override + public void onEnableTenant() + { + } + + @Override + public void onDisableTenant() + { + } + + @Override + public void init() + { + } + + @Override + public void destroy() + { + } + } + + private static class BaseStore extends AbstractContentStore + { + + @Override + public boolean isWriteSupported() + { + return false; + } + + @Override + public ContentReader getReader(String contentUrl) + { + return null; + } + } + +}