MT extension

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6785 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jan Vonka
2007-09-14 11:20:02 +00:00
parent 6a83f91a6b
commit d9590d3677
25 changed files with 2414 additions and 67 deletions

View File

@@ -334,7 +334,13 @@ public class RepoAdminServiceImpl implements RepoAdminService
NodeRef modelNodeRef = nodeRefs.get(0);
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
boolean isActive = false;
Boolean value = (Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE);
if (value != null)
{
isActive = value.booleanValue();
}
QName modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
ModelDefinition modelDef = null;
@@ -436,7 +442,13 @@ public class RepoAdminServiceImpl implements RepoAdminService
NodeRef modelNodeRef = nodeRefs.get(0);
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
boolean isActive = false;
Boolean value = (Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE);
if (value != null)
{
isActive = value.booleanValue();
}
modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
ModelDefinition modelDef = null;

View File

@@ -83,7 +83,7 @@ public class GenericWorkflowPatch extends AbstractPatch implements ApplicationCo
props.put(WorkflowDeployer.REDEPLOY, "true");
}
deployer.setWorkflowDefinitions(workflowDefinitions);
deployer.deploy();
deployer.init();
// done
return I18NUtil.getMessage(MSG_DEPLOYED, workflowDefinitions.size());

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.content;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.Tenant;
import org.alfresco.repo.tenant.TenantDeployer;
import org.alfresco.repo.tenant.TenantService;
/**
* Content Store that supports tenant routing, if multi-tenancy is enabled.
*
* Note: Need to initialise before the dictionary service, in the case that models are dynamically loaded for the tenant.
*/
public class TenantRoutingFileContentStore extends AbstractRoutingContentStore implements TenantDeployer
{
Map<String, FileContentStore> tenantFileStores = new HashMap<String, FileContentStore>();
private String defaultRootDirectory;
private TenantService tenantService;
public void setDefaultRootDir(String defaultRootDirectory)
{
this.defaultRootDirectory = defaultRootDirectory;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
protected ContentStore selectWriteStore(ContentContext ctx)
{
return getTenantFileStore(tenantService.getCurrentUserDomain());
}
public List<ContentStore> getAllStores()
{
if (tenantService.isEnabled())
{
String currentUser = AuthenticationUtil.getCurrentUserName();
if ((currentUser == null) || (currentUser.equals(AuthenticationUtil.getSystemUserName())))
{
// return enabled stores across all tenants, if running as system/null user, for example, ContentStoreCleaner scheduled job
List<ContentStore> allEnabledStores = new ArrayList<ContentStore>();
for (String tenantDomain : tenantFileStores.keySet())
{
allEnabledStores.add(tenantFileStores.get(tenantDomain)); // note: cache should only contain enabled stores
}
return allEnabledStores;
}
}
return Arrays.asList(getTenantFileStore(tenantService.getCurrentUserDomain()));
}
private ContentStore getTenantFileStore(String tenantDomain)
{
return tenantFileStores.get(tenantDomain);
}
private void putTenantFileStore(String tenantDomain, FileContentStore fileStore)
{
tenantFileStores.put(tenantDomain, fileStore);
}
private void removeTenantFileStore(String tenantDomain)
{
tenantFileStores.remove(tenantDomain);
}
public void init()
{
String tenantDomain = "";
String rootDir = defaultRootDirectory;
Tenant tenant = tenantService.getTenant(tenantService.getCurrentUserDomain());
if (tenant != null)
{
if (tenant.getRootContentStoreDir() != null)
{
rootDir = tenant.getRootContentStoreDir();
}
tenantDomain = tenant.getTenantDomain();
}
putTenantFileStore(tenantDomain, new FileContentStore(new File(rootDir)));
}
public void destroy()
{
removeTenantFileStore(tenantService.getCurrentUserDomain());
}
public void onEnableTenant()
{
init();
}
public void onDisableTenant()
{
destroy();
}
public String getDefaultRootDir()
{
return this.defaultRootDirectory;
}
}

View File

@@ -175,8 +175,12 @@ public class MessageServiceImpl implements MessageService
try
{
writeLock.lock();
tenantResourceBundleBaseNames.add(resBundlePath);
writeLock.lock();
if (! tenantResourceBundleBaseNames.contains(resBundlePath))
{
tenantResourceBundleBaseNames.add(resBundlePath);
}
logger.info("Registered message bundle '" + resBundlePath + "'");
@@ -333,7 +337,10 @@ public class MessageServiceImpl implements MessageService
try
{
resourcebundle = new PropertyResourceBundle(resBundleStream);
if (resBundleStream != null)
{
resourcebundle = new PropertyResourceBundle(resBundleStream);
}
}
catch (IOException ioe)
{
@@ -346,12 +353,15 @@ public class MessageServiceImpl implements MessageService
resourcebundle = ResourceBundle.getBundle(resBundlePath, locale);
}
// unload from the cached messages
Enumeration<String> enumKeys = resourcebundle.getKeys();
while (enumKeys.hasMoreElements() == true)
if (resourcebundle != null)
{
String key = enumKeys.nextElement();
props.remove(key);
// unload from the cached messages
Enumeration<String> enumKeys = resourcebundle.getKeys();
while (enumKeys.hasMoreElements() == true)
{
String key = enumKeys.nextElement();
props.remove(key);
}
}
loadedBundles.remove(resBundlePath);
@@ -480,7 +490,10 @@ public class MessageServiceImpl implements MessageService
try
{
resourcebundle = new PropertyResourceBundle(resBundleStream);
if (resBundleStream != null)
{
resourcebundle = new PropertyResourceBundle(resBundleStream);
}
}
catch (IOException ioe)
{
@@ -493,15 +506,18 @@ public class MessageServiceImpl implements MessageService
resourcebundle = ResourceBundle.getBundle(resBundlePath, locale);
}
Enumeration<String> enumKeys = resourcebundle.getKeys();
while (enumKeys.hasMoreElements() == true)
if (resourcebundle != null)
{
String key = enumKeys.nextElement();
props.put(key, resourcebundle.getString(key));
Enumeration<String> enumKeys = resourcebundle.getKeys();
while (enumKeys.hasMoreElements() == true)
{
String key = enumKeys.nextElement();
props.put(key, resourcebundle.getString(key));
}
loadedBundles.add(resBundlePath);
count++;
}
loadedBundles.add(resBundlePath);
count++;
}
}
@@ -543,7 +559,8 @@ public class MessageServiceImpl implements MessageService
if ((nodeRefs == null) || (nodeRefs.size() == 0))
{
throw new RuntimeException("Could not find message resource bundle " + storeRef + path);
logger.debug("Could not find message resource bundle " + storeRef + "/" + path);
return null;
}
}

View File

@@ -323,8 +323,12 @@ public class ImporterBootstrap extends AbstractLifecycleBean
UserTransaction userTransaction = transactionService.getUserTransaction();
Authentication authentication = authenticationComponent.getCurrentAuthentication();
authenticationComponent.setSystemUserAsCurrentUser();
if (authenticationComponent.getCurrentUserName() == null)
{
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
}
try
{
userTransaction.begin();

View File

@@ -0,0 +1,882 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.tenant;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;
import javax.transaction.UserTransaction;
import net.sf.acegisecurity.providers.encoding.PasswordEncoder;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.admin.RepoModelDefinition;
import org.alfresco.repo.attributes.BooleanAttributeValue;
import org.alfresco.repo.attributes.MapAttribute;
import org.alfresco.repo.attributes.MapAttributeValue;
import org.alfresco.repo.attributes.StringAttributeValue;
import org.alfresco.repo.content.TenantRoutingFileContentStore;
import org.alfresco.repo.dictionary.DictionaryComponent;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.node.db.DbNodeServiceImpl;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.attributes.AttributeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.AbstractLifecycleBean;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.core.io.ClassPathResource;
/**
* MT Admin Service Implementation.
*
*/
public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements TenantAdminService, TenantDeployerService
{
// Logger
private static Log logger = LogFactory.getLog(MultiTAdminServiceImpl.class);
// Dependencies
private DbNodeServiceImpl nodeService; // TODO - replace with NodeService, when deleteStore is exposed via public API
private DictionaryComponent dictionaryComponent;
private RepoAdminService repoAdminService;
private AuthenticationComponent authenticationComponent;
private TransactionService transactionService;
private MultiTServiceImpl tenantService;
private AttributeService attributeService;
private PasswordEncoder passwordEncoder;
private TenantRoutingFileContentStore tenantFileContentStore;
private WorkflowService workflowService;
protected final static String REGEX_VALID_TENANT_NAME = "^[a-zA-Z0-9]([a-zA-Z0-9]|.[a-zA-Z0-9])*$"; // note: must also be a valid filename
public void setNodeService(DbNodeServiceImpl dbNodeService)
{
this.nodeService = dbNodeService;
}
public void setDictionaryComponent(DictionaryComponent dictionaryComponent)
{
this.dictionaryComponent = dictionaryComponent;
}
public void setRepoAdminService(RepoAdminService repoAdminService)
{
this.repoAdminService = repoAdminService;
}
public void setAuthenticationComponent(AuthenticationComponent authenticationComponent)
{
this.authenticationComponent = authenticationComponent;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setTenantService(MultiTServiceImpl tenantService)
{
this.tenantService = tenantService;
}
public void setAttributeService(AttributeService attributeService)
{
this.attributeService = attributeService;
}
public void setPasswordEncoder(PasswordEncoder passwordEncoder)
{
this.passwordEncoder = passwordEncoder;
}
public void setTenantFileContentStore(TenantRoutingFileContentStore tenantFileContentStore)
{
this.tenantFileContentStore = tenantFileContentStore;
}
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
}
public static final String PROTOCOL_STORE_USER = "user";
public static final String PROTOCOL_STORE_WORKSPACE = "workspace";
public static final String PROTOCOL_STORE_SYSTEM = "system";
public static final String PROTOCOL_STORE_ARCHIVE = "archive";
public static final String STORE_BASE_ID_USER = "alfrescoUserStore";
public static final String STORE_BASE_ID_SYSTEM = "system";
public static final String STORE_BASE_ID_VERSION = "lightWeightVersionStore";
public static final String STORE_BASE_ID_SPACES = "SpacesStore";
private static final String TENANTS_ATTRIBUTE_PATH = "alfresco-tenants";
private static final String TENANT_ATTRIBUTE_ENABLED = "enabled";
private static final String TENANT_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir";
private static final String ADMIN_BASENAME = TenantService.ADMIN_BASENAME;
private List<TenantDeployer> tenantDeployers = new ArrayList<TenantDeployer>();
@Override
protected void onBootstrap(ApplicationEvent event)
{
// initialise the tenant admin service and status of tenants (using attribute service)
// note: this requires that the repository schema has already been initialised
// register dictionary - to allow enable/disable tenant callbacks
register(dictionaryComponent);
// register file store - to allow enable/disable tenant callbacks
register(tenantFileContentStore);
UserTransaction userTransaction = transactionService.getUserTransaction();
authenticationComponent.setSystemUserAsCurrentUser();
try
{
userTransaction.begin();
// bootstrap Tenant Service internal cache
List<Tenant> tenants = getAllTenants();
if (tenants != null)
{
for (Tenant tenant : tenants)
{
if (tenant.isEnabled())
{
// this will also call tenant deployers registered so far ...
enableTenant(tenant.getTenantDomain(), true);
}
else
{
// explicitly disable, without calling disableTenant callback
disableTenant(tenant.getTenantDomain(), false);
}
}
tenantService.register(this); // callback to refresh tenantStatus cache
}
userTransaction.commit();
}
catch(Throwable e)
{
// rollback the transaction
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
throw new AlfrescoRuntimeException("Failed to bootstrap tenants", e);
}
}
@Override
protected void onShutdown(ApplicationEvent event)
{
tenantDeployers.clear();
tenantDeployers = null;
}
/**
* @see TenantAdminService.createTenant()
*/
public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword)
{
createTenant(tenantDomain, tenantAdminRawPassword, null);
}
/**
* @see TenantAdminService.createTenant()
*/
public void createTenant(final String tenantDomain, final char[] tenantAdminRawPassword, String rootContentStoreDir)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("tenantDomain", tenantDomain);
ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword);
if (! Pattern.matches(REGEX_VALID_TENANT_NAME, tenantDomain))
{
throw new IllegalArgumentException(tenantDomain + " is not a valid tenant name (must match " + REGEX_VALID_TENANT_NAME + ")");
}
if (existsTenant(tenantDomain))
{
throw new AlfrescoRuntimeException("Tenant already exists: " + tenantDomain);
}
else
{
authenticationComponent.setSystemUserAsCurrentUser();
if (rootContentStoreDir == null)
{
rootContentStoreDir = tenantFileContentStore.getDefaultRootDir();
}
// init - need to enable tenant (including tenant service) before stores bootstrap
Tenant tenant = new Tenant(tenantDomain, true, rootContentStoreDir);
putTenantAttributes(tenantDomain, tenant);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
dictionaryComponent.init();
tenantFileContentStore.init();
// create tenant-specific stores
bootstrapUserTenantStore(tenantDomain, tenantAdminRawPassword);
bootstrapSystemTenantStore(tenantDomain);
bootstrapVersionTenantStore(tenantDomain);
bootstrapSpacesArchiveTenantStore(tenantDomain);
bootstrapSpacesTenantStore(tenantDomain);
// notify listeners that tenant has been created & hence enabled
for (TenantDeployer tenantDeployer : tenantDeployers)
{
tenantDeployer.onEnableTenant();
}
return null;
}
}, getTenantAdminUser(tenantDomain));
}
logger.info("Tenant created: " + tenantDomain);
}
public boolean existsTenant(String tenantDomain)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("tenantDomain", tenantDomain);
return (getTenantAttributes(tenantDomain) != null);
}
private void putTenantAttributes(String tenantDomain, Tenant tenant)
{
if (! attributeService.exists(TENANTS_ATTRIBUTE_PATH))
{
// bootstrap
attributeService.setAttribute("", TENANTS_ATTRIBUTE_PATH, new MapAttributeValue());
}
MapAttribute tenantProps = new MapAttributeValue();
tenantProps.put(TENANT_ATTRIBUTE_ENABLED, new BooleanAttributeValue(tenant.isEnabled()));
tenantProps.put(TENANT_ROOT_CONTENT_STORE_DIR, new StringAttributeValue(tenant.getRootContentStoreDir()));
attributeService.setAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain, tenantProps);
// update tenant status cache
((MultiTServiceImpl)tenantService).putTenant(tenantDomain, tenant);
}
private Tenant getTenantAttributes(String tenantDomain)
{
if (attributeService.exists(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain))
{
MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH+"/"+tenantDomain);
if (map != null)
{
return new Tenant(tenantDomain,
map.get(TENANT_ATTRIBUTE_ENABLED).getBooleanValue(),
map.get(TENANT_ROOT_CONTENT_STORE_DIR).getStringValue());
}
}
return null;
}
public void enableTenant(String tenantDomain)
{
if (isEnabledTenant(tenantDomain))
{
logger.warn("Tenant already enabled: " + tenantDomain);
}
enableTenant(tenantDomain, true);
}
private void enableTenant(String tenantDomain, boolean notifyTenantDeployers)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("tenantDomain", tenantDomain);
Tenant tenant = getTenantAttributes(tenantDomain);
tenant = new Tenant(tenantDomain, true, tenant.getRootContentStoreDir()); // enable
putTenantAttributes(tenantDomain, tenant);
if (notifyTenantDeployers)
{
// notify listeners that tenant has been enabled
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
for (TenantDeployer tenantDeployer : tenantDeployers)
{
tenantDeployer.onEnableTenant();
}
return null;
}
}, getTenantAdminUser(tenantDomain));
}
logger.info("Tenant enabled: " + tenantDomain);
}
public void disableTenant(String tenantDomain)
{
if (! isEnabledTenant(tenantDomain))
{
logger.warn("Tenant already disabled: " + tenantDomain);
}
disableTenant(tenantDomain, true);
}
public void disableTenant(String tenantDomain, boolean notifyTenantDeployers)
{
if (notifyTenantDeployers)
{
// notify listeners that tenant has been disabled
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
for (TenantDeployer tenantDeployer : tenantDeployers)
{
tenantDeployer.onDisableTenant();
}
return null;
}
}, getTenantAdminUser(tenantDomain));
}
// update tenant attributes / tenant cache - need to disable after notifying listeners (else they cannot disable)
Tenant tenant = getTenantAttributes(tenantDomain);
tenant = new Tenant(tenantDomain, false, tenant.getRootContentStoreDir()); // disable
putTenantAttributes(tenantDomain, tenant);
logger.info("Tenant disabled: " + tenantDomain);
}
public boolean isEnabledTenant(String tenantDomain)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("tenantDomain", 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);
Tenant tenant = getTenantAttributes(tenantDomain);
if (tenant != null)
{
return tenant.getRootContentStoreDir();
}
return null;
}
protected void putRootContentStoreDir(String tenantDomain, String rootContentStoreDir)
{
Tenant tenant = getTenantAttributes(tenantDomain);
tenant = new Tenant(tenantDomain, tenant.isEnabled(), rootContentStoreDir);
putTenantAttributes(tenantDomain, tenant);
}
public Tenant getTenant(String tenantDomain)
{
return new Tenant(tenantDomain, isEnabledTenant(tenantDomain), getRootContentStoreDir(tenantDomain));
}
public void bootstrapWorkflows()
{
// use this to deploy standard workflow process defs to the JBPM engine
WorkflowDeployer workflowBootstrap = (WorkflowDeployer)getApplicationContext().getBean("workflowBootstrap");
String resourceClasspath = null;
// Workflow process definitions
try
{
List<Properties> workflowDefs = workflowBootstrap.getWorkflowDefinitions();
if (workflowDefs != null)
{
for (Properties workflowDefProps : workflowDefs)
{
resourceClasspath = workflowDefProps.getProperty(WorkflowDeployer.LOCATION);
ClassPathResource resource = new ClassPathResource(resourceClasspath);
workflowService.deployDefinition(workflowDefProps.getProperty(WorkflowDeployer.ENGINE_ID), resource.getInputStream(), workflowDefProps.getProperty(WorkflowDeployer.MIMETYPE));
}
}
}
catch (IOException ioe)
{
throw new AlfrescoRuntimeException("Failed to find workflow process def: " + resourceClasspath);
}
logger.info("Tenant workflows bootstrapped: " + tenantService.getCurrentUserDomain());
}
/**
* @see TenantAdminService.deleteTenant()
*/
public void deleteTenant(String tenantDomain)
{
if (! existsTenant(tenantDomain))
{
throw new RuntimeException("Tenant does not exist: " + tenantDomain);
}
else
{
try
{
final String tenantAdminUser = getTenantAdminUser(tenantDomain);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
List<WorkflowDefinition> workflowDefs = workflowService.getDefinitions();
if (workflowDefs != null)
{
for (WorkflowDefinition workflowDef : workflowDefs)
{
workflowService.undeployDefinition(workflowDef.getId());
}
}
List<String> messageResourceBundles = repoAdminService.getMessageBundles();
if (messageResourceBundles != null)
{
for (String messageResourceBundle : messageResourceBundles)
{
repoAdminService.undeployMessageBundle(messageResourceBundle);
}
}
List<RepoModelDefinition> models = repoAdminService.getModels();
if (models != null)
{
for (RepoModelDefinition model : models)
{
repoAdminService.undeployModel(model.getRepoName());
}
}
return null;
}
}, tenantAdminUser);
// delete tenant-specific stores
nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_SPACES)));
nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_ARCHIVE, STORE_BASE_ID_SPACES)));
nodeService.deleteStore(tenantService.getName(tenantAdminUser, new StoreRef(PROTOCOL_STORE_WORKSPACE, STORE_BASE_ID_VERSION)));
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<Object>()
{
public Object doWork()
{
for (TenantDeployer tenantDeployer : tenantDeployers)
{
tenantDeployer.onDisableTenant();
}
return null;
}
}, tenantAdminUser);
// remove tenant
attributeService.removeAttribute(TENANTS_ATTRIBUTE_PATH, tenantDomain);
}
catch (Throwable t)
{
throw new AlfrescoRuntimeException("Failed to delete tenant: " + tenantDomain, t);
}
}
}
/**
* @see TenantAdminService.getAllTenants()
*/
public List<Tenant> getAllTenants()
{
MapAttribute map = (MapAttribute)attributeService.getAttribute(TENANTS_ATTRIBUTE_PATH);
List<Tenant> tenants = new ArrayList<Tenant>();
if (map != null)
{
// note: getAllTenants is called first, by TenantDeployer - hence need to initialise the TenantService status cache
Set<String> tenantDomains = map.keySet();
for (String tenantDomain : tenantDomains)
{
Tenant tenant = getTenantAttributes(tenantDomain);
tenants.add(new Tenant(tenantDomain, tenant.isEnabled(), tenant.getRootContentStoreDir()));
}
}
return tenants; // list of tenants or empty list
}
private void bootstrapUserTenantStore(String tenantDomain, char[] tenantAdminRawPassword)
{
// Bootstrap Tenant-Specific User Store
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_USER, tenantService.getName(STORE_BASE_ID_USER, tenantDomain));
ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("userBootstrap");
userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
// override admin username property
String salt = null; // GUID.generate();
Properties props = userImporterBootstrap.getConfiguration();
props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));
props.put("alfresco_user_store.adminpassword", passwordEncoder.encodePassword(new String(tenantAdminRawPassword), salt));
// override guest username property
props.put("alfresco_user_store.guestusername", getTenantGuestUser(tenantDomain));
userImporterBootstrap.bootstrap();
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
}
private void bootstrapSystemTenantStore(String tenantDomain)
{
// Bootstrap Tenant-Specific System Store
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_SYSTEM, tenantService.getName(STORE_BASE_ID_SYSTEM, tenantDomain));
ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("systemBootstrap");
systemImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
// override default property (workspace://SpacesStore)
List<String> mustNotExistStoreUrls = new ArrayList<String>();
mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)).toString());
systemImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);
systemImporterBootstrap.bootstrap();
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
}
private void bootstrapVersionTenantStore(String tenantDomain)
{
// Bootstrap Tenant-Specific Version Store
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_VERSION, tenantDomain));
ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("versionBootstrap");
versionImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
versionImporterBootstrap.bootstrap();
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
}
private void bootstrapSpacesArchiveTenantStore(String tenantDomain)
{
// Bootstrap Tenant-Specific Spaces Store
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));
ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesArchiveBootstrap");
spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
// override default property (archive://SpacesStore)
List<String> mustNotExistStoreUrls = new ArrayList<String>();
mustNotExistStoreUrls.add(new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain)).toString());
spacesArchiveImporterBootstrap.setMustNotExistStoreUrls(mustNotExistStoreUrls);
spacesArchiveImporterBootstrap.bootstrap();
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
}
private void bootstrapSpacesTenantStore(String tenantDomain)
{
// Bootstrap Tenant-Specific Spaces Store
StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_WORKSPACE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain));
final ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesBootstrap");
spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString());
// override admin username property
Properties props = spacesImporterBootstrap.getConfiguration();
props.put("alfresco_user_store.adminusername", getTenantAdminUser(tenantDomain));
spacesImporterBootstrap.bootstrap();
logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef));
}
public void deployTenants(final TenantDeployer deployer, Log logger)
{
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();
authenticationComponent.setSystemUserAsCurrentUser();
List<Tenant> tenants = null;
try
{
userTransaction.begin();
tenants = getAllTenants();
userTransaction.commit();
}
catch(Throwable e)
{
// rollback the transaction
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
throw new AlfrescoRuntimeException("Failed to get tenants", e);
}
String currentUser = AuthenticationUtil.getCurrentUserName();
if (tenants != null)
{
try
{
for (Tenant tenant : tenants)
{
if (tenant.isEnabled())
{
try
{
// switch to admin in order to deploy within context of tenant domain
// assumes each tenant has default "admin" user
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
// init the service within tenant context
deployer.init();
return null;
}
}, getTenantAdminUser(tenant.getTenantDomain()));
}
catch (Throwable e)
{
logger.error("Deployment failed" + e);
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
logger.error(stringWriter.toString());
// tenant deploy failure should not necessarily affect other tenants
}
}
}
}
finally
{
if (currentUser != null) { AuthenticationUtil.setCurrentUser(currentUser); }
}
}
}
}
public void undeployTenants(final TenantDeployer deployer, Log logger)
{
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();
authenticationComponent.setSystemUserAsCurrentUser();
List<Tenant> tenants = null;
try
{
userTransaction.begin();
tenants = getAllTenants();
userTransaction.commit();
}
catch(Throwable e)
{
// rollback the transaction
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
throw new AlfrescoRuntimeException("Failed to get tenants", e);
}
String currentUser = AuthenticationUtil.getCurrentUserName();
if (tenants != null)
{
try
{
for (Tenant tenant : tenants)
{
if (tenant.isEnabled())
{
try
{
// switch to admin in order to deploy within context of tenant domain
// assumes each tenant has default "admin" user
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
// destroy the service within tenant context
deployer.destroy();
return null;
}
}, getTenantAdminUser(tenant.getTenantDomain()));
}
catch (Throwable e)
{
logger.error("Undeployment failed" + e);
StringWriter stringWriter = new StringWriter();
e.printStackTrace(new PrintWriter(stringWriter));
logger.error(stringWriter.toString());
// tenant undeploy failure should not necessarily affect other tenants
}
}
}
}
finally
{
if (currentUser != null) { AuthenticationUtil.setCurrentUser(currentUser); }
}
}
}
}
public void register(TenantDeployer deployer)
{
if (deployer == null)
{
throw new AlfrescoRuntimeException("Deployer must be provided");
}
if (! tenantDeployers.contains(deployer))
{
tenantDeployers.add(deployer);
}
}
public void unregister(TenantDeployer deployer)
{
if (deployer == null)
{
throw new AlfrescoRuntimeException("Deployer must be provided");
}
if (tenantDeployers != null)
{
tenantDeployers.remove(deployer);
}
}
public boolean isEnabled()
{
return tenantService.isEnabled();
}
public void resetCache(String tenantDomain)
{
if (existsTenant(tenantDomain))
{
if (isEnabledTenant(tenantDomain))
{
enableTenant(tenantDomain);
}
else
{
disableTenant(tenantDomain);
}
}
else
{
throw new AlfrescoRuntimeException("No such tenant " + tenantDomain);
}
}
// local helper
private String getTenantAdminUser(String tenantDomain)
{
return tenantService.getDomainUser(ADMIN_BASENAME, tenantDomain);
}
// local helper
private String getTenantGuestUser(String tenantDomain)
{
return tenantService.getDomainUser(authenticationComponent.getGuestUserName(), tenantDomain);
}
}

View File

@@ -0,0 +1,568 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.tenant;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/*
* MT Service implementation
*
* Adapts names to be tenant specific or vice-versa.
*/
public class MultiTServiceImpl implements TenantService
{
private static Log logger = LogFactory.getLog(MultiTServiceImpl.class);
// clusterable cache of enabled/disabled tenants - managed via TenantAdmin Service
private SimpleCache<String, Tenant> tenantsCache;
private MultiTAdminServiceImpl tenantAdminService = null; // registered (rather than injected) - to avoid circular dependency
public void setTenantsCache(SimpleCache<String, Tenant> tenantsCache)
{
this.tenantsCache = tenantsCache;
}
public NodeRef getName(NodeRef nodeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("NodeRef", nodeRef);
return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId());
}
public NodeRef getName(NodeRef inNodeRef, NodeRef nodeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("InNodeRef", inNodeRef);
ParameterCheck.mandatory("NodeRef", nodeRef);
int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR);
if (idx != -1)
{
String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx);
return new NodeRef(nodeRef.getStoreRef().getProtocol(), getName(nodeRef.getStoreRef().getIdentifier(), tenantDomain), nodeRef.getId());
}
return nodeRef;
}
public StoreRef getName(StoreRef storeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("StoreRef", storeRef);
return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier()));
}
public StoreRef getName(String username, StoreRef storeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("StoreRef", storeRef);
if (username != null) {
int idx = username.lastIndexOf(SEPARATOR);
if ((idx > 0) && (idx < (username.length()-1)))
{
String tenantDomain = username.substring(idx+1);
return new StoreRef(storeRef.getProtocol(), getName(storeRef.getIdentifier(), tenantDomain));
}
}
return storeRef;
}
protected String getName(String name, String tenantDomain)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("name", name);
ParameterCheck.mandatory("tenantDomain", tenantDomain);
checkTenantEnabled(tenantDomain);
int idx1 = name.indexOf(SEPARATOR);
if (idx1 != 0)
{
// no domain, so add it as a prefix (between two domain separators)
name = SEPARATOR + tenantDomain + SEPARATOR + name;
}
else
{
int idx2 = name.indexOf(SEPARATOR, 1);
String nameDomain = name.substring(1, idx2);
if (! tenantDomain.equals(nameDomain))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
}
return name;
}
public QName getName(NodeRef inNodeRef, QName name)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("InNodeRef", inNodeRef);
ParameterCheck.mandatory("Name", name);
int idx = inNodeRef.getStoreRef().getIdentifier().lastIndexOf(SEPARATOR);
if (idx != -1)
{
String tenantDomain = inNodeRef.getStoreRef().getIdentifier().substring(1, idx);
checkTenantEnabled(tenantDomain);
return getName(name, tenantDomain);
}
return name;
}
private QName getName(QName name, String tenantDomain)
{
String namespace = name.getNamespaceURI();
int idx1 = namespace.indexOf(SEPARATOR);
if (idx1 == -1)
{
// no domain, so add it as a prefix (between two domain separators)
namespace = SEPARATOR + tenantDomain + SEPARATOR + namespace;
name = QName.createQName(namespace, name.getLocalName());
}
else
{
int idx2 = namespace.indexOf(SEPARATOR, 1);
String nameDomain = namespace.substring(1, idx2);
if (! tenantDomain.equals(nameDomain))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
}
return name;
}
public String getName(String name)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("name", name);
String tenantDomain = getCurrentUserDomain();
if (! tenantDomain.equals(""))
{
int idx1 = name.indexOf(SEPARATOR);
if (idx1 != 0)
{
// no tenant domain prefix, so add it
name = SEPARATOR + tenantDomain + SEPARATOR + name;
}
else
{
int idx2 = name.indexOf(SEPARATOR, 1);
String nameDomain = name.substring(1, idx2);
if (! tenantDomain.equals(nameDomain))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
}
}
return name;
}
public QName getBaseName(QName name, boolean forceForNonTenant)
{
String baseNamespaceURI = getBaseName(name.getNamespaceURI(), forceForNonTenant);
return QName.createQName(baseNamespaceURI, name.getLocalName());
}
public NodeRef getBaseName(NodeRef nodeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("NodeRef", nodeRef);
return new NodeRef(nodeRef.getStoreRef().getProtocol(), getBaseName(nodeRef.getStoreRef().getIdentifier()), nodeRef.getId());
}
public StoreRef getBaseName(StoreRef storeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("StoreRef", storeRef);
return new StoreRef(storeRef.getProtocol(), getBaseName(storeRef.getIdentifier()));
}
public String getBaseName(String name)
{
// get base name, but don't force for non-tenant user (e.g. super admin)
return getBaseName(name, false);
}
public String getBaseName(String name, boolean forceForNonTenant)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("name", name);
String tenantDomain = getCurrentUserDomain();
int idx1 = name.indexOf(SEPARATOR);
if (idx1 == 0)
{
int idx2 = name.indexOf(SEPARATOR, 1);
String nameDomain = name.substring(1, idx2);
if ((! tenantDomain.equals("")) && (! tenantDomain.equals(nameDomain)))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
if ((! tenantDomain.equals("")) || (forceForNonTenant))
{
// remove tenant domain
name = name.substring(idx2+1);
}
}
return name;
}
public String getBaseNameUser(String name)
{
// can be null (e.g. for System user / during app ctx init)
if (name != null)
{
int idx = name.lastIndexOf(SEPARATOR);
if (idx != -1)
{
return name.substring(0, idx);
}
}
return name;
}
public void checkDomainUser(String username)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("Username", username);
String tenantDomain = getCurrentUserDomain();
if (! tenantDomain.equals(""))
{
int idx2 = username.lastIndexOf(SEPARATOR);
if ((idx2 > 0) && (idx2 < (username.length()-1)))
{
String tenantUserDomain = username.substring(idx2+1);
if ((tenantUserDomain == null) || (! tenantDomain.equals(tenantUserDomain)))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + tenantUserDomain);
}
}
else
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = <none>");
}
}
}
public void checkDomain(String name)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("Name", name);
String nameDomain = null;
int idx1 = name.indexOf(SEPARATOR);
if (idx1 == 0)
{
int idx2 = name.indexOf(SEPARATOR, 1);
nameDomain = name.substring(1, idx2);
}
String tenantDomain = getCurrentUserDomain();
if (((nameDomain == null) && (! tenantDomain.equals(""))) ||
((nameDomain != null) && (! nameDomain.equals(tenantDomain))))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
}
public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("NodeService", nodeService);
ParameterCheck.mandatory("SearchService", searchService);
ParameterCheck.mandatory("NamespaceService", namespaceService);
ParameterCheck.mandatory("RootPath", rootPath);
ParameterCheck.mandatory("RootNodeRef", rootNodeRef);
String username = AuthenticationUtil.getCurrentUserName();
StoreRef storeRef = getName(username, rootNodeRef.getStoreRef());
AuthenticationUtil.RunAsWork<NodeRef> action = new GetRootNode(nodeService, searchService, namespaceService, rootPath, rootNodeRef, storeRef);
return getBaseName(AuthenticationUtil.runAs(action, AuthenticationUtil.getSystemUserName()));
}
private class GetRootNode implements AuthenticationUtil.RunAsWork<NodeRef>
{
NodeService nodeService;
SearchService searchService;
NamespaceService namespaceService;
String rootPath;
NodeRef rootNodeRef;
StoreRef storeRef;
GetRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef, StoreRef storeRef)
{
this.nodeService = nodeService;
this.searchService = searchService;
this.namespaceService = namespaceService;
this.rootPath = rootPath;
this.rootNodeRef = rootNodeRef;
this.storeRef = storeRef;
}
public NodeRef doWork() throws Exception
{
// Get company home / root for the tenant domain
// Do this as the System user in case the tenant user does not have permission
// Connect to the repo and ensure that the store exists
if (! nodeService.exists(storeRef))
{
throw new AlfrescoRuntimeException("Store not created prior to application startup: " + storeRef);
}
NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
// Find the root node for this device
List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService, false);
if (nodeRefs.size() > 1)
{
throw new AlfrescoRuntimeException("Multiple possible roots for device: \n" +
" root path: " + rootPath + "\n" +
" results: " + nodeRefs);
}
else if (nodeRefs.size() == 0)
{
// nothing found
throw new AlfrescoRuntimeException("No root found for device: \n" +
" root path: " + rootPath);
}
else
{
// we found a node
rootNodeRef = nodeRefs.get(0);
}
return rootNodeRef;
}
}
public boolean isTenantUser()
{
return isTenantUser(AuthenticationUtil.getCurrentUserName());
}
public boolean isTenantUser(String username)
{
// can be null (e.g. for System user / during app ctx init)
if (username != null) {
int idx = username.lastIndexOf(SEPARATOR);
if ((idx > 0) && (idx < (username.length()-1)))
{
return true;
}
}
return false;
}
public boolean isTenantName(String name)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("name", name);
int idx1 = name.indexOf(SEPARATOR);
if (idx1 == 0)
{
int idx2 = name.indexOf(SEPARATOR, 1);
if (idx2 != -1)
{
return true;
}
}
return false;
}
public String getCurrentUserDomain()
{
String user = AuthenticationUtil.getCurrentUserName();
// can be null (e.g. for System user / during app ctx init)
if (user != null)
{
int idx = user.lastIndexOf(SEPARATOR);
if ((idx > 0) && (idx < (user.length()-1)))
{
String tenantDomain = user.substring(idx+1);
checkTenantEnabled(tenantDomain);
return tenantDomain;
}
}
return ""; // default domain - non-tenant user
}
public String getDomain(String name)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("name", name);
String tenantDomain = getCurrentUserDomain();
String nameDomain = "";
int idx1 = name.indexOf(SEPARATOR);
if (idx1 == 0)
{
int idx2 = name.indexOf(SEPARATOR, 1);
nameDomain = name.substring(1, idx2);
if ((! tenantDomain.equals("")) && (! tenantDomain.equals(nameDomain)))
{
throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain);
}
}
return nameDomain;
}
public String getDomainUser(String baseUsername, String tenantDomain)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("baseUsername", baseUsername);
ParameterCheck.mandatory("tenantDomain", tenantDomain);
if (! tenantDomain.equals(""))
{
if (baseUsername.contains(SEPARATOR))
{
throw new AlfrescoRuntimeException("Invalid base username: " + baseUsername);
}
if (tenantDomain.contains(SEPARATOR))
{
throw new AlfrescoRuntimeException("Invalid tenant domain: " + tenantDomain);
}
return baseUsername + SEPARATOR + tenantDomain;
}
else
{
return baseUsername;
}
}
protected void checkTenantEnabled(String tenantDomain)
{
if (getTenant(tenantDomain).isEnabled() == false)
{
throw new AlfrescoRuntimeException("Tenant is not enabled: " + tenantDomain);
}
}
public Tenant getTenant(String tenantDomain)
{
Tenant tenant = tenantsCache.get(tenantDomain);
if (tenant == null)
{
// backed by TenantAdminService - update this cache, e.g. could have been invalidated and/or expired
if (tenantAdminService != null)
{
tenant = tenantAdminService.getTenant(tenantDomain);
if (tenant == null)
{
throw new AlfrescoRuntimeException("No such tenant " + tenantDomain);
}
else
{
putTenant(tenantDomain, tenant);
}
}
}
return tenant;
}
public boolean isEnabled()
{
return true;
}
// should only be called by Tenant Admin Service
protected void register(MultiTAdminServiceImpl tenantAdminService)
{
this.tenantAdminService = tenantAdminService;
}
// should only be called by Tenant Admin Service
protected void putTenant(String tenantDomain, Tenant tenant)
{
if (logger.isDebugEnabled())
{
logger.debug("putTenant " + tenantDomain);
}
tenantsCache.put(tenantDomain, tenant);
}
// should only be called by Tenant Admin Service
protected void removeTenant(String tenantDomain)
{
if (logger.isDebugEnabled())
{
logger.debug("removeTenant " + tenantDomain);
}
tenantsCache.remove(tenantDomain);
}
}

View File

@@ -112,11 +112,6 @@ public class SingleTServiceImpl implements TenantService
{
return rootNodeRef;
}
public NodeRef getCompanyHomeNode(NodeService nodeService, String username, StoreRef storeRef)
{
return null;
}
public boolean isTenantUser()
{

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.tenant;
import java.util.List;
/**
* Tenant Admin Service interface.
* <p>
* This interface provides administrative methods to provision and administer tenants.
*
*/
public interface TenantAdminService extends TenantDeployerService
{
public void createTenant(String tenantDomain, char[] adminRawPassword);
public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir);
public boolean existsTenant(String tenantDomain);
public void bootstrapWorkflows();
public void deleteTenant(String tenantDomain);
public List<Tenant> getAllTenants();
public void enableTenant(String tenantDomain);
public void disableTenant(String tenantDomain);
public Tenant getTenant(String tenantDomain);
public boolean isEnabledTenant(String tenantDomain);
public boolean isEnabled();
}

View File

@@ -0,0 +1,332 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.tenant;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;
import org.alfresco.i18n.I18NUtil;
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.RetryingTransactionCallback;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
/**
* An interactive console for Tenants.
*
*/
public class TenantInterpreter extends BaseInterpreter
{
// Service dependencies
private TenantAdminService tenantAdminService;
private AuthenticationService authenticationService;
public void setTenantAdminService(TenantAdminService tenantAdminService)
{
this.tenantAdminService = tenantAdminService;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
public static BaseInterpreter getConsoleBean(ApplicationContext context)
{
return (TenantInterpreter)context.getBean("tenantInterpreter");
}
protected boolean hasAuthority(String username)
{
// must be super "admin" for tenant administrator
return ((username != null) && (username.equals(BaseInterpreter.DEFAULT_ADMIN)));
}
/**
* Execute a single command using the BufferedReader passed in for any data needed.
*
* TODO: Use decent parser!
*
* @param line The unparsed command
* @return The textual output of the command.
*/
public String executeCommand(String line)
throws IOException
{
String[] command = line.split(" ");
if (command.length == 0)
{
command = new String[1];
command[0] = line;
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bout);
// repeat last command?
if (command[0].equals("r"))
{
if (lastCommand == null)
{
return "No command entered yet.";
}
return "repeating command " + lastCommand + "\n\n" + executeCommand(lastCommand);
}
// remember last command
lastCommand = line;
// execute command
if (command[0].equals("help"))
{
String helpFile = I18NUtil.getMessage("tenant_console.help");
ClassPathResource helpResource = new ClassPathResource(helpFile);
byte[] helpBytes = new byte[500];
InputStream helpStream = helpResource.getInputStream();
try
{
int read = helpStream.read(helpBytes);
while (read != -1)
{
bout.write(helpBytes, 0, read);
read = helpStream.read(helpBytes);
}
}
finally
{
helpStream.close();
}
}
else if (command[0].equals("show"))
{
if (command.length < 2)
{
return "Syntax Error, try 'help'.\n";
}
else if (command[1].equals("tenants"))
{
List<Tenant> tenants = tenantAdminService.getAllTenants();
for (Tenant tenant : tenants)
{
if (tenant.isEnabled())
{
String rootContentStoreDir = tenant.getRootContentStoreDir();
out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
}
}
out.println("");
for (Tenant tenant : tenants)
{
if (! tenant.isEnabled())
{
String rootContentStoreDir = tenant.getRootContentStoreDir();
out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
}
}
}
else if (command[1].equals("tenant"))
{
if (command.length != 3)
{
return "Syntax Error, try 'help'.\n";
}
String tenantDomain = new String(command[2]).toLowerCase();
Tenant tenant = tenantAdminService.getTenant(tenantDomain);
String rootContentStoreDir = tenant.getRootContentStoreDir();
if (tenant.isEnabled())
{
out.println("Enabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
}
else
{
out.println("Disabled - Tenant: " + tenant.getTenantDomain() + " (" + rootContentStoreDir + ")");
}
}
else
{
return "No such sub-command, try 'help'.\n";
}
}
else if (command[0].equals("create"))
{
if ((command.length != 3) && (command.length != 4))
{
return "Syntax Error, try 'help'.\n";
}
String newTenant = new String(command[1]).toLowerCase();
String tenantAdminRawPassword = new String(command[2]);
String createTenantArgs = newTenant + " " + tenantAdminRawPassword;
if (command.length == 4)
{
createTenantArgs = createTenantArgs + " " + new String(command[3]);
}
out.print(executeCommand("createWithoutWorkflows " + createTenantArgs));
out.print(executeCommand("bootstrapWorkflows " + newTenant));
}
else if (command[0].equals("createWithoutWorkflows"))
{
if ((command.length != 3) && (command.length != 4))
{
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)
{
rootContentStoreDir = new String(command[3]);
}
tenantAdminService.createTenant(newTenant, tenantAdminRawPassword, rootContentStoreDir);
out.println("created tenant: " + newTenant);
}
else if (command[0].equals("bootstrapWorkflows"))
{
if (command.length != 2)
{
return "Syntax Error, try 'help'.\n";
}
String newTenant = new String(command[1]).toLowerCase();
String tenantAdminUsername = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, newTenant);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
tenantAdminService.bootstrapWorkflows();
return null;
}
}, tenantAdminUsername);
out.println("bootstrap workflows deployed for tenant: " + newTenant);
}
// TODO - not fully working yet
else if (command[0].equals("delete"))
{
if (command.length != 2)
{
return "Syntax Error, try 'help'.\n";
}
String tenantDomain = new String(command[1]).toLowerCase();
tenantAdminService.deleteTenant(tenantDomain);
out.println("Deleted tenant: " + tenantDomain);
}
else if (command[0].equals("enable"))
{
if (command.length != 2)
{
return "Syntax Error, try 'help'.\n";
}
String tenantDomain = new String(command[1]).toLowerCase();
tenantAdminService.enableTenant(tenantDomain);
out.println("Enabled tenant: " + tenantDomain);
}
else if (command[0].equals("disable"))
{
if (command.length != 2)
{
return "Syntax Error, try 'help'.\n";
}
String tenantDomain = new String(command[1]).toLowerCase();
tenantAdminService.disableTenant(tenantDomain);
out.println("Disabled tenant: " + tenantDomain);
}
else if (command[0].equals("changeAdminPassword"))
{
if (command.length != 3)
{
return "Syntax Error, try 'help'.\n";
}
String tenantDomain = new String(command[1]).toLowerCase();
final String newPassword = new String(command[2]);
final String tenantAdminUsername = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, tenantDomain);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
RetryingTransactionCallback<Object> txnWork = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
authenticationService.setAuthentication(tenantAdminUsername, newPassword.toCharArray());
return null;
}
};
return transactionService.getRetryingTransactionHelper().doInTransaction(txnWork);
}
}, tenantAdminUsername);
}
else
{
return "No such command, try 'help'.\n";
}
out.flush();
String retVal = new String(bout.toByteArray());
out.close();
return retVal;
}
}

View File

@@ -35,7 +35,7 @@ import org.alfresco.service.namespace.QName;
/**
* Tenant Service interface.
* <p>
* This interface provides methods to support either Single-Tenant or Multi-Tenant implementations.
* This interface provides methods to support either ST or MT implementations.
*
*/
public interface TenantService
@@ -74,8 +74,6 @@ public interface TenantService
public NodeRef getRootNode(NodeService nodeService, SearchService searchService, NamespaceService namespaceService, String rootPath, NodeRef rootNodeRef);
public NodeRef getCompanyHomeNode(NodeService nodeService, String username, StoreRef storeRef);
public boolean isTenantUser();
public boolean isTenantUser(String username);

View File

@@ -34,6 +34,9 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.dictionary.DictionaryBootstrap;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.view.ImporterException;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowException;
@@ -71,6 +74,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean
private List<Properties> workflowDefinitions;
private List<String> models = new ArrayList<String>();
private List<String> resourceBundles = new ArrayList<String>();
private TenantService tenantService;
/**
@@ -123,6 +127,16 @@ public class WorkflowDeployer extends AbstractLifecycleBean
this.dictionaryDAO = dictionaryDAO;
}
/**
* Sets the tenant service
*
* @param tenantService the tenant service
*/
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
/**
* Sets the Workflow Definitions
*
@@ -144,7 +158,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean
}
/**
* Sets the initial list of Workflow reosurce bundles to bootstrap with
* Sets the initial list of Workflow resource bundles to bootstrap with
*
* @param modelResources the model names
*/
@@ -152,11 +166,17 @@ public class WorkflowDeployer extends AbstractLifecycleBean
{
this.resourceBundles = labels;
}
// used by TenantAdminService when creating a new tenant and bootstrapping the pre-defined workflows
public List<Properties> getWorkflowDefinitions()
{
return this.workflowDefinitions;
}
/**
* Deploy the Workflow Definitions
*/
public void deploy()
public void init()
{
if (transactionService == null)
{
@@ -171,24 +191,30 @@ public class WorkflowDeployer extends AbstractLifecycleBean
throw new ImporterException("Workflow Service must be provided");
}
String currentUser = authenticationComponent.getCurrentUserName();
if (currentUser == null)
{
authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
}
UserTransaction userTransaction = transactionService.getUserTransaction();
authenticationComponent.setSystemUserAsCurrentUser();
try
{
userTransaction.begin();
// bootstrap the workflow models and labels
// bootstrap the workflow models and static labels (from classpath)
if (models != null && resourceBundles != null)
{
DictionaryBootstrap dictionaryBootstrap = new DictionaryBootstrap();
dictionaryBootstrap.setDictionaryDAO(dictionaryDAO);
dictionaryBootstrap.setTenantService(tenantService);
dictionaryBootstrap.setModels(models);
dictionaryBootstrap.setLabels(resourceBundles);
dictionaryBootstrap.bootstrap();
dictionaryBootstrap.bootstrap(); // also registers with dictionary
}
// bootstrap the workflow definitions
// bootstrap the workflow definitions (from classpath)
if (workflowDefinitions != null)
{
for (Properties workflowDefinition : workflowDefinitions)
@@ -239,19 +265,29 @@ public class WorkflowDeployer extends AbstractLifecycleBean
{
// rollback the transaction
try { if (userTransaction != null) {userTransaction.rollback();} } catch (Exception ex) {}
try {authenticationComponent.clearCurrentSecurityContext(); } catch (Exception ex) {}
throw new AlfrescoRuntimeException("Workflow deployment failed", e);
}
finally
{
authenticationComponent.clearCurrentSecurityContext();
if (currentUser == null)
{
authenticationComponent.clearCurrentSecurityContext();
}
}
}
@Override
protected void onBootstrap(ApplicationEvent event)
{
deploy();
// run as System on bootstrap
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
init();
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
@Override