From 990c2d68ec0c186da964e67d69b1bdc6213f2188 Mon Sep 17 00:00:00 2001 From: Jan Vonka Date: Mon, 21 Jan 2008 16:37:27 +0000 Subject: [PATCH] MT admin - add export/import tenant also expose delete tenant (BETA) git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8047 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../extension/mt/mt-admin-context.xml.sample | 8 +- .../messages/tenant-interpreter-help.txt | 37 ++- config/alfresco/workflow-context.xml | 9 + .../repo/dictionary/DictionaryModelType.java | 8 +- .../repo/importer/ImporterBootstrap.java | 49 +-- .../repo/tenant/MultiTAdminServiceImpl.java | 309 ++++++++++++++++-- .../repo/tenant/TenantAdminService.java | 5 + .../repo/tenant/TenantInterpreter.java | 46 ++- .../repo/workflow/WorkflowDefinitionType.java | 2 +- 9 files changed, 401 insertions(+), 72 deletions(-) diff --git a/config/alfresco/extension/mt/mt-admin-context.xml.sample b/config/alfresco/extension/mt/mt-admin-context.xml.sample index 5163eeeac8..41e5e34a90 100644 --- a/config/alfresco/extension/mt/mt-admin-context.xml.sample +++ b/config/alfresco/extension/mt/mt-admin-context.xml.sample @@ -21,7 +21,13 @@ - + + + + + + + diff --git a/config/alfresco/messages/tenant-interpreter-help.txt b/config/alfresco/messages/tenant-interpreter-help.txt index ba6946d92f..7160bc8a91 100755 --- a/config/alfresco/messages/tenant-interpreter-help.txt +++ b/config/alfresco/messages/tenant-interpreter-help.txt @@ -32,7 +32,7 @@ ok> show tenant ok> create [] - Create tenant. By default the tenant will be enabled. It will have an admin + Create empty tenant. By default the tenant will be enabled. It will have an admin user called "admin@" with supplied admin password. All users that the admin creates, will login using "@". The root of the contentstore directory can be optionally specified, otherwise @@ -42,17 +42,7 @@ ok> create [] Examples: create zzz.com l3tm31n /usr/tenantstores/zzz create yyy.zzz.com g00dby3 /usr/tenantstores/yyy.zzz create myorg h3ll0 - -ok> createWithoutWorkflows [] - Same as create, except the default workflows will not be bootstrapped. - -ok> bootstrapWorkflows - - Bootstrap the default workflows. - - Examples: bootstrapWorkflows yyy.zzz.com - ok> changeAdminPassword Useful if the tenant's admin (admin@) has forgotten their password. @@ -70,6 +60,31 @@ ok> disable Disable tenant so that is inactive. Existing logins will fail on next usage. Example: enable yyy.zzz.com + +ok> delete + + BETA - Delete tenant. + + Note: This currently requires a server restart to clear the index threads. Also + tenant index directories should be deleted manually. + + Example: delete yyy.zzz.com + +ok> export + + Export tenant to given destination directory. Export filenames will be suffixed with '_'. + + Example: export yyy.zzz.com /usr/exportdir + +ok> import [] + + BETA - create tenant by importing the tenant files from the given source directory. The import filenames must + be suffixed with '_'. + + Note: If importing into a previously deleted tenant then the server must be stopped after the delete + (and tenant indexes manually deleted) before restarting and performing the import. + + Example: import yyy.zzz.com /usr/exportdir /usr/tenantstores/yyy.zzz ## ## end diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index 6a7e9d07e3..2a195a15da 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -109,6 +109,15 @@ + + + + + + /app:company_home/app:dictionary/app:workflow_defs + + + diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java index 9a0ddba465..4003b8aee6 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryModelType.java @@ -473,7 +473,13 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda if ((existingValue != null) && (existingValue.booleanValue() == true)) { String name = (String)nodeService.getProperty(existingNodeRef, ContentModel.PROP_NAME); - throw new AlfrescoRuntimeException("Cannot activate '"+modelDefinition.getName()+"' - existing active model: " + name); + + // for MT import, model may have been activated by DictionaryRepositoryBootstrap + if (logger.isDebugEnabled()) + { + logger.debug("Re-activating '"+modelDefinition.getName()+"' - existing active model: " + name); + } + //throw new AlfrescoRuntimeException("Cannot activate '"+modelDefinition.getName()+"' - existing active model: " + name); } } } diff --git a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java index a483a855e5..2d0d074185 100644 --- a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java +++ b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java @@ -498,29 +498,40 @@ public class ImporterBootstrap extends AbstractLifecycleBean */ private File getFile(String view) { - // Get input stream - InputStream viewStream = getClass().getClassLoader().getResourceAsStream(view); - if (viewStream == null) + // Try as a file location + File file = new File(view); + if ((file != null) && (file.exists())) { - throw new ImporterException("Could not find view file " + view); + return file; } - - // Create output stream - File tempFile = TempFileProvider.createTempFile("acpImport", ".tmp"); - try + else { - FileOutputStream os = new FileOutputStream(tempFile); - FileCopyUtils.copy(viewStream, os); + // Try as a classpath location + + // Get input stream + InputStream viewStream = getClass().getClassLoader().getResourceAsStream(view); + if (viewStream == null) + { + throw new ImporterException("Could not find view file " + view); + } + + // Create output stream + File tempFile = TempFileProvider.createTempFile("acpImport", ".tmp"); + try + { + FileOutputStream os = new FileOutputStream(tempFile); + FileCopyUtils.copy(viewStream, os); + } + catch (FileNotFoundException e) + { + throw new ImporterException("Could not import view " + view, e); + } + catch (IOException e) + { + throw new ImporterException("Could not import view " + view, e); + } + return tempFile; } - catch (FileNotFoundException e) - { - throw new ImporterException("Could not import view " + view, e); - } - catch (IOException e) - { - throw new ImporterException("Could not import view " + view, e); - } - return tempFile; } diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index 552cd0d5e0..506dd9dcda 100755 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.tenant; +import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -45,17 +46,23 @@ 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.dictionary.RepositoryLocation; 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.WorkflowDefinitionType; 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.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.view.RepositoryExporterService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.ParameterCheck; @@ -85,6 +92,11 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten private PasswordEncoder passwordEncoder; private TenantRoutingFileContentStore tenantFileContentStore; private WorkflowService workflowService; + private RepositoryExporterService repositoryExporterService; + private NamespaceService namespaceService; + private SearchService searchService; + private RepositoryLocation repoWorkflowDefsLocation; + private WorkflowDefinitionType workflowDefinitionType; 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 @@ -139,6 +151,32 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten this.workflowService = workflowService; } + public void setRepositoryExporterService(RepositoryExporterService repositoryExporterService) + { + this.repositoryExporterService = repositoryExporterService; + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setRepositoryWorkflowDefsLocations(RepositoryLocation repoWorkflowDefsLocation) + { + this.repoWorkflowDefsLocation = repoWorkflowDefsLocation; + } + + public void setWorkflowDefinitionType(WorkflowDefinitionType workflowDefinitionType) + { + this.workflowDefinitionType = workflowDefinitionType; + } + + public static final String PROTOCOL_STORE_USER = "user"; public static final String PROTOCOL_STORE_WORKSPACE = "workspace"; public static final String PROTOCOL_STORE_SYSTEM = "system"; @@ -154,6 +192,9 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten private static final String TENANT_ROOT_CONTENT_STORE_DIR = "rootContentStoreDir"; private static final String ADMIN_BASENAME = TenantService.ADMIN_BASENAME; + + public final static String CRITERIA_ALL = "/*"; // immediate children only + public final static String defaultSubtypeOfWorkflowDefinitionType = "subtypeOf('bpm:workflowDefinition')"; private List tenantDeployers = new ArrayList(); @@ -275,11 +316,20 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten tenantFileContentStore.init(); // create tenant-specific stores - bootstrapUserTenantStore(tenantDomain, tenantAdminRawPassword); - bootstrapSystemTenantStore(tenantDomain); - bootstrapVersionTenantStore(tenantDomain); - bootstrapSpacesArchiveTenantStore(tenantDomain); - bootstrapSpacesTenantStore(tenantDomain); + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("userBootstrap"); + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, tenantAdminRawPassword); + + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("systemBootstrap"); + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); + + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("versionBootstrap"); + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesArchiveImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesArchiveBootstrap"); + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesBootstrap"); + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); // notify listeners that tenant has been created & hence enabled for (TenantDeployer tenantDeployer : tenantDeployers) @@ -295,6 +345,82 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten logger.info("Tenant created: " + tenantDomain); } + /** + * Export tenant - equivalent to the tenant admin running a 'complete repo' export from the Web Client Admin + */ + public void exportTenant(final String tenantDomain, final File directoryDestination) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + repositoryExporterService.export(directoryDestination, tenantDomain); + return null; + } + }, getTenantAdminUser(tenantDomain)); + + logger.info("Tenant exported: " + tenantDomain); + } + + /** + * Create tenant by restoring from a complete repository export. This is equivalent to a bootstrap import using restore-context.xml. + */ + public void importTenant(final String tenantDomain, final File directorySource, String rootContentStoreDir) + { + // Check that all the passed values are not null + ParameterCheck.mandatory("tenantDomain", tenantDomain); + + 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() + { + public Object doWork() + { + dictionaryComponent.init(); + tenantFileContentStore.init(); + + // import tenant-specific stores + importBootstrapUserTenantStore(tenantDomain, directorySource); + importBootstrapSystemTenantStore(tenantDomain, directorySource); + importBootstrapVersionTenantStore(tenantDomain, directorySource); + importBootstrapSpacesArchiveTenantStore(tenantDomain, directorySource); + importBootstrapSpacesModelsTenantStore(tenantDomain, directorySource); + importBootstrapSpacesTenantStore(tenantDomain, directorySource); + + // notify listeners that tenant has been created & hence enabled + for (TenantDeployer tenantDeployer : tenantDeployers) + { + tenantDeployer.onEnableTenant(); + } + + return null; + } + }, getTenantAdminUser(tenantDomain)); + } + + logger.info("Tenant imported: " + tenantDomain); + } + public boolean existsTenant(String tenantDomain) { // Check that all the passed values are not null @@ -477,6 +603,21 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten throw new AlfrescoRuntimeException("Failed to find workflow process def: " + resourceClasspath); } + // in case of import, also deploy any custom workflow definitions defined in the repo + // TODO refactor/review repository bootstrap order of undeployed workflow definitions + + StoreRef storeRef = repoWorkflowDefsLocation.getStoreRef(); + NodeRef rootNode = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(rootNode, repoWorkflowDefsLocation.getPath()+CRITERIA_ALL+"["+defaultSubtypeOfWorkflowDefinitionType+"]", null, namespaceService, false); + + if (nodeRefs.size() > 0) + { + for (NodeRef nodeRef : nodeRefs) + { + workflowDefinitionType.deploy(nodeRef); + } + } + logger.info("Tenant workflows bootstrapped: " + tenantService.getCurrentUserDomain()); } @@ -585,33 +726,28 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten return tenants; // list of tenants or empty list } - - private void bootstrapUserTenantStore(String tenantDomain, char[] tenantAdminRawPassword) + + private void importBootstrapSystemTenantStore(String tenantDomain, File directorySource) { - // Bootstrap Tenant-Specific User Store - StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_USER, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)); + // Import Bootstrap (restore) Tenant-Specific Version Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_system.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); - 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)); - - userImporterBootstrap.bootstrap(); - - logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + ImporterBootstrap systemImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("systemBootstrap"); + systemImporterBootstrap.setBootstrapViews(bootstrapViews); + systemImporterBootstrap.setLog(true); + + bootstrapSystemTenantStore(systemImporterBootstrap, tenantDomain); } - private void bootstrapSystemTenantStore(String tenantDomain) + private void bootstrapSystemTenantStore(ImporterBootstrap systemImporterBootstrap, 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) @@ -624,12 +760,65 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); } - private void bootstrapVersionTenantStore(String tenantDomain) + private void importBootstrapUserTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific User Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_users.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap userImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("userBootstrap"); + userImporterBootstrap.setBootstrapViews(bootstrapViews); + userImporterBootstrap.setLog(true); + + bootstrapUserTenantStore(userImporterBootstrap, tenantDomain, null); + } + + private void bootstrapUserTenantStore(ImporterBootstrap userImporterBootstrap, String tenantDomain, char[] tenantAdminRawPassword) + { + // Bootstrap Tenant-Specific User Store + StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_USER, tenantService.getName(STORE_BASE_ID_USER, tenantDomain)); + userImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + + // override admin username property + 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)); + } + + userImporterBootstrap.bootstrap(); + + logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); + } + + 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+"_versions.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap versionImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("versionBootstrap"); + versionImporterBootstrap.setBootstrapViews(bootstrapViews); + versionImporterBootstrap.setLog(true); + + bootstrapVersionTenantStore(versionImporterBootstrap, tenantDomain); + } + + private void bootstrapVersionTenantStore(ImporterBootstrap versionImporterBootstrap, 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(); @@ -637,12 +826,27 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten 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)); + private void importBootstrapSpacesArchiveTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Archive Store + 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)getApplicationContext().getBean("spacesArchiveBootstrap"); + spacesArchiveImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesArchiveImporterBootstrap.setLog(true); + + bootstrapSpacesArchiveTenantStore(spacesArchiveImporterBootstrap, tenantDomain); + } + + private void bootstrapSpacesArchiveTenantStore(ImporterBootstrap spacesArchiveImporterBootstrap, String tenantDomain) + { + // Bootstrap Tenant-Specific Spaces Archive Store + StoreRef bootstrapStoreRef = new StoreRef(PROTOCOL_STORE_ARCHIVE, tenantService.getName(STORE_BASE_ID_SPACES, tenantDomain)); spacesArchiveImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); // override default property (archive://SpacesStore) @@ -655,12 +859,47 @@ public class MultiTAdminServiceImpl extends AbstractLifecycleBean implements Ten logger.debug("Bootstrapped store: " + tenantService.getBaseName(bootstrapStoreRef)); } - private void bootstrapSpacesTenantStore(String tenantDomain) + private void importBootstrapSpacesModelsTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_models.acp"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesBootstrap"); + spacesImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesImporterBootstrap.setLog(true); + + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + } + + private void importBootstrapSpacesTenantStore(String tenantDomain, File directorySource) + { + // Import Bootstrap (restore) Tenant-Specific Spaces Store + Properties bootstrapView = new Properties(); + bootstrapView.put("path", "/"); + bootstrapView.put("location", directorySource.getPath()+"/"+tenantDomain+"_spaces.acp"); + bootstrapView.put("uuidBinding", "UPDATE_EXISTING"); + + List bootstrapViews = new ArrayList(1); + bootstrapViews.add(bootstrapView); + + ImporterBootstrap spacesImporterBootstrap = (ImporterBootstrap)getApplicationContext().getBean("spacesBootstrap"); + spacesImporterBootstrap.setBootstrapViews(bootstrapViews); + spacesImporterBootstrap.setLog(true); + + spacesImporterBootstrap.setUseExistingStore(true); + + bootstrapSpacesTenantStore(spacesImporterBootstrap, tenantDomain); + } + + private void bootstrapSpacesTenantStore(ImporterBootstrap spacesImporterBootstrap, 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 diff --git a/source/java/org/alfresco/repo/tenant/TenantAdminService.java b/source/java/org/alfresco/repo/tenant/TenantAdminService.java index 0178847525..55c449c1d2 100755 --- a/source/java/org/alfresco/repo/tenant/TenantAdminService.java +++ b/source/java/org/alfresco/repo/tenant/TenantAdminService.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.tenant; +import java.io.File; import java.util.List; @@ -40,6 +41,10 @@ public interface TenantAdminService extends TenantDeployerService public void createTenant(String tenantDomain, char[] adminRawPassword, String rootContentStoreDir); + public void exportTenant(String tenantDomain, File directoryDestination); + + public void importTenant(String tenantDomain, File directorySource, String rootContentStoreDir); + public boolean existsTenant(String tenantDomain); public void bootstrapWorkflows(); diff --git a/source/java/org/alfresco/repo/tenant/TenantInterpreter.java b/source/java/org/alfresco/repo/tenant/TenantInterpreter.java index b26fa6a94a..8367779b92 100755 --- a/source/java/org/alfresco/repo/tenant/TenantInterpreter.java +++ b/source/java/org/alfresco/repo/tenant/TenantInterpreter.java @@ -25,6 +25,7 @@ package org.alfresco.repo.tenant; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; @@ -208,8 +209,8 @@ public class TenantInterpreter extends BaseInterpreter out.print(executeCommand("createWithoutWorkflows " + createTenantArgs)); out.print(executeCommand("bootstrapWorkflows " + newTenant)); - } - + } + else if (command[0].equals("createWithoutWorkflows")) { if ((command.length != 3) && (command.length != 4)) @@ -228,8 +229,8 @@ public class TenantInterpreter extends BaseInterpreter tenantAdminService.createTenant(newTenant, tenantAdminRawPassword, rootContentStoreDir); out.println("created tenant: " + newTenant); - } - + } + else if (command[0].equals("bootstrapWorkflows")) { if (command.length != 2) @@ -252,6 +253,43 @@ public class TenantInterpreter extends BaseInterpreter out.println("bootstrap workflows deployed for tenant: " + newTenant); } + else if (command[0].equals("import")) + { + if ((command.length != 3) && (command.length != 4)) + { + return "Syntax Error, try 'help'.\n"; + } + + String newTenant = new String(command[1]).toLowerCase(); + File directorySource = new File(command[2]); + + String rootContentStoreDir = null; + if (command.length == 4) + { + rootContentStoreDir = new String(command[3]); + } + + tenantAdminService.importTenant(newTenant, directorySource, rootContentStoreDir); + + out.println("imported tenant: " + newTenant); + out.print(executeCommand("bootstrapWorkflows " + newTenant)); + } + + else if (command[0].equals("export")) + { + if (command.length != 3) + { + return "Syntax Error, try 'help'.\n"; + } + + String tenant = new String(command[1]).toLowerCase(); + File directoryDestination = new File(command[2]); + + tenantAdminService.exportTenant(tenant, directoryDestination); + + out.println("exported tenant: " + tenant); + } + // TODO - not fully working yet else if (command[0].equals("delete")) { diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java index db6817f31a..b3cd6b1c2b 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDefinitionType.java @@ -171,7 +171,7 @@ public class WorkflowDefinitionType implements ContentServicePolicies.OnContentU undeploy(nodeRef); } - private void deploy(NodeRef nodeRef) + public void deploy(NodeRef nodeRef) { // Ignore if the node is a working copy if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == false)