mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
125788 rmunteanu: Merged 5.1.N (5.1.2) to 5.2.N (5.2.1) 125606 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2) 125515 slanglois: MNT-16155 Update source headers - add new Copyrights for Java and JSP source files + automatic check in the build git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@127810 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
905 lines
36 KiB
Java
905 lines
36 KiB
Java
/*
|
|
* #%L
|
|
* Alfresco Repository
|
|
* %%
|
|
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
|
* %%
|
|
* This file is part of the Alfresco software.
|
|
* If the software was purchased under a paid Alfresco license, the terms of
|
|
* the paid license agreement will prevail. Otherwise, the software is
|
|
* provided under the following open source license terms:
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
* #L%
|
|
*/
|
|
package org.alfresco.repo.admin;
|
|
|
|
import java.io.InputStream;
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ApplicationModel;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.content.MimetypeMap;
|
|
import org.alfresco.repo.dictionary.DictionaryDAO;
|
|
import org.alfresco.repo.dictionary.M2Model;
|
|
import org.alfresco.repo.dictionary.RepositoryLocation;
|
|
import org.alfresco.repo.i18n.MessageService;
|
|
import org.alfresco.repo.i18n.MessageServiceImpl;
|
|
import org.alfresco.repo.usage.RepoUsageComponent;
|
|
import org.alfresco.service.cmr.admin.RepoAdminService;
|
|
import org.alfresco.service.cmr.admin.RepoUsage;
|
|
import org.alfresco.service.cmr.admin.RepoUsage.UsageType;
|
|
import org.alfresco.service.cmr.admin.RepoUsageStatus;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
|
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
import org.alfresco.service.cmr.repository.ContentReader;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.repository.ContentWriter;
|
|
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
|
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
|
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.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.core.io.Resource;
|
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
|
import org.springframework.dao.ConcurrencyFailureException;
|
|
import org.springframework.extensions.surf.util.I18NUtil;
|
|
import org.springframework.extensions.surf.util.ParameterCheck;
|
|
|
|
/**
|
|
* Repository Admin Service Implementation.
|
|
* <p>
|
|
* @see RepoAdminService interface
|
|
*/
|
|
public class RepoAdminServiceImpl implements RepoAdminService
|
|
{
|
|
// Logging support
|
|
private static Log logger = LogFactory.getLog(RepoAdminServiceImpl.class);
|
|
|
|
// dependencies
|
|
private DictionaryDAO dictionaryDAO;
|
|
private SearchService searchService;
|
|
private NodeService nodeService;
|
|
private ContentService contentService;
|
|
private NamespaceService namespaceService;
|
|
private MessageService messageService;
|
|
private RepoUsageComponent repoUsageComponent;
|
|
|
|
private RepositoryLocation repoModelsLocation;
|
|
private RepositoryLocation repoMessagesLocation;
|
|
|
|
public final static String CRITERIA_ALL = "/*"; // immediate children only
|
|
|
|
public final static String defaultSubtypeOfDictionaryModel = "subtypeOf('cm:dictionaryModel')";
|
|
public final static String defaultSubtypeOfContent = "subtypeOf('cm:content')";
|
|
|
|
private static final String MODELS_LOCATION_NOT_FOUND = "repoadmin_service.models_location_not_found";
|
|
private static final String MODELS_LOCATION_MULTIPLE_FOUND = "repoadmin_service.models_location_multiple_found";
|
|
private static final String MODEL_EXISTS = "repoadmin_service.model_exists";
|
|
private static final String MODEL_DEPLOYMENT_FAILED = "repoadmin_service.model_deployment_failed";
|
|
private static final String MODEL_UNDEPLOYMENT_FAILED = "repoadmin_service.model_undeployment_failed";
|
|
private static final String MODEL_ACTIVATION_FAILED = "repoadmin_service.model_activation_failed";
|
|
private static final String MODEL_DEACTIVATION_FAILED = "repoadmin_service.model_deactivation_failed";
|
|
private static final String MODEL_NOT_FOUND = "repoadmin_service.model_not_found";
|
|
private static final String MODELS_MULTIPLE_FOUND = "repoadmin_service.models_multiple_found";
|
|
private static final String MODEL_ALREADY_ACTIVATED = "repoadmin_service.model_already_activated";
|
|
private static final String MODEL_ALREADY_DEACTIVATED = "repoadmin_service.model_already_deactivated";
|
|
private static final String MODEL_NO_LONGER_EXISTS = "repoadmin_service.model_no_longer_exists";
|
|
private static final String MSG_RESOURCES_NOT_FOUND = "repoadmin_service.msg_resource_not_found";
|
|
private static final String MSG_RESOURCES_DEPLOYMENT_FAILED = "repoadmin_service.msg_resource_deployment_failed";
|
|
private static final String MESSAGES_LOCATION_NOT_FOUND = "repoadmin_service.messages_location_not_found";
|
|
private static final String MESSAGES_LOCATION_MULTIPLE_FOUND = "repoadmin_service.messages_location_multiple_found";
|
|
private static final String MSG_RESOURCES_UNDEPLOYMENT_FAILED = "repoadmin_service.msg_resource_undeployment_failed";
|
|
private static final String MSG_RESOURCES_RELOAD_FAILED = "repoadmin_service.msg_resource_reload_failed";
|
|
private static final String MSG_MISSING_BUNDLE_BASE_NAME = "repoadmin_service.msg_missing_bundle_base_name";
|
|
private static final String MSG_BASE_NAME_CONTAIN_UNDERSCORE = "repoadmin_service.msg_base_name_contain_underscore";
|
|
private static final String MSG_BASE_NAME_CONTAIN_PERIOD = "repoadmin_service.msg_base_name_contain_period";
|
|
|
|
public void setDictionaryDAO(DictionaryDAO dictionaryDAO)
|
|
{
|
|
this.dictionaryDAO = dictionaryDAO;
|
|
}
|
|
|
|
public void setSearchService(SearchService searchService)
|
|
{
|
|
this.searchService = searchService;
|
|
}
|
|
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
public void setNamespaceService(NamespaceService namespaceService)
|
|
{
|
|
this.namespaceService = namespaceService;
|
|
}
|
|
|
|
public void setMessageService(MessageService messageService)
|
|
{
|
|
this.messageService = messageService;
|
|
}
|
|
|
|
public void setRepoUsageComponent(RepoUsageComponent repoUsageComponent)
|
|
{
|
|
this.repoUsageComponent = repoUsageComponent;
|
|
}
|
|
|
|
public void setRepositoryModelsLocation(RepositoryLocation repoModelsLocation)
|
|
{
|
|
this.repoModelsLocation = repoModelsLocation;
|
|
}
|
|
|
|
public void setRepositoryMessagesLocation(RepositoryLocation repoMessagesLocation)
|
|
{
|
|
this.repoMessagesLocation = repoMessagesLocation;
|
|
}
|
|
|
|
public List<RepoModelDefinition> getModels()
|
|
{
|
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<RepoModelDefinition> modelsInRepo = new ArrayList<RepoModelDefinition>();
|
|
|
|
Collection<QName> models = dictionaryDAO.getModels(true);
|
|
|
|
List<String> dictionaryModels = new ArrayList<String>();
|
|
for (QName model : models)
|
|
{
|
|
dictionaryModels.add(model.toPrefixString());
|
|
}
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+CRITERIA_ALL+"["+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
|
|
|
if (nodeRefs.size() > 0)
|
|
{
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
String modelFileName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
|
|
String repoVersion = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL);
|
|
|
|
try
|
|
{
|
|
String modelName = null;
|
|
|
|
ContentReader cr = contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
|
|
|
|
if (cr != null)
|
|
{
|
|
InputStream is = cr.getContentInputStream();
|
|
try
|
|
{
|
|
M2Model model = M2Model.createModel(is);
|
|
modelName = model.getName();
|
|
}
|
|
finally
|
|
{
|
|
is.close();
|
|
}
|
|
}
|
|
|
|
// check against models loaded in dictionary and give warning if not found
|
|
if (dictionaryModels.contains(modelName))
|
|
{
|
|
// note: uses dictionary model cache, rather than getting content from repo and re-compiling
|
|
modelsInRepo.add(new RepoModelDefinition(modelFileName, repoVersion, dictionaryDAO.getModel(QName.createQName(modelName, namespaceService)), true));
|
|
}
|
|
else
|
|
{
|
|
modelsInRepo.add(new RepoModelDefinition(modelFileName, repoVersion, null, false));
|
|
}
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
logger.warn("Skip model: "+modelFileName+" ("+t.getMessage()+")");
|
|
}
|
|
}
|
|
}
|
|
|
|
return modelsInRepo;
|
|
}
|
|
|
|
@Override
|
|
public NodeRef deployModel(InputStream modelStream, String modelFileName, boolean activate)
|
|
{
|
|
try
|
|
{
|
|
// Check that all the passed values are not null
|
|
ParameterCheck.mandatory("ModelStream", modelStream);
|
|
ParameterCheck.mandatoryString("ModelFileName", modelFileName);
|
|
|
|
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
|
|
contentProps.put(ContentModel.PROP_NAME, modelFileName);
|
|
|
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath(), null, namespaceService, false);
|
|
|
|
if (nodeRefs.size() == 0)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODELS_LOCATION_NOT_FOUND, new Object[] { repoModelsLocation.getPath() });
|
|
}
|
|
else if (nodeRefs.size() > 1)
|
|
{
|
|
// unexpected: should not find multiple nodes with same name
|
|
throw new AlfrescoRuntimeException(MODELS_LOCATION_MULTIPLE_FOUND, new Object[] { repoModelsLocation.getPath() });
|
|
}
|
|
|
|
NodeRef customModelsSpaceNodeRef = nodeRefs.get(0);
|
|
|
|
nodeRefs = searchService.selectNodes(customModelsSpaceNodeRef, "*[@cm:name='"+modelFileName+"' and "+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
|
|
|
NodeRef modelNodeRef = null;
|
|
|
|
if (nodeRefs.size() == 1)
|
|
{
|
|
// re-deploy existing model to the repository
|
|
|
|
modelNodeRef = nodeRefs.get(0);
|
|
}
|
|
else
|
|
{
|
|
// deploy new model to the repository
|
|
|
|
try
|
|
{
|
|
// note: dictionary model type has associated policies that will be invoked
|
|
ChildAssociationRef association = nodeService.createNode(customModelsSpaceNodeRef,
|
|
ContentModel.ASSOC_CONTAINS,
|
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, modelFileName),
|
|
ContentModel.TYPE_DICTIONARY_MODEL,
|
|
contentProps); // also invokes policies for DictionaryModelType - e.g. onUpdateProperties
|
|
|
|
modelNodeRef = association.getChildRef();
|
|
}
|
|
catch (DuplicateChildNodeNameException dcnne)
|
|
{
|
|
String msg = "Model already exists: "+modelFileName+" - "+dcnne;
|
|
logger.warn(msg);
|
|
|
|
// for now, assume concurrency failure
|
|
throw new ConcurrencyFailureException(getLocalisedMessage(MODEL_EXISTS, modelFileName));
|
|
}
|
|
|
|
// add titled aspect (for Web Client display)
|
|
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
|
|
titledProps.put(ContentModel.PROP_TITLE, modelFileName);
|
|
titledProps.put(ContentModel.PROP_DESCRIPTION, modelFileName);
|
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_TITLED, titledProps);
|
|
|
|
// add versionable aspect (set auto-version)
|
|
Map<QName, Serializable> versionProps = new HashMap<QName, Serializable>();
|
|
versionProps.put(ContentModel.PROP_AUTO_VERSION, true);
|
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_VERSIONABLE, versionProps);
|
|
}
|
|
|
|
ContentWriter writer = contentService.getWriter(modelNodeRef, ContentModel.PROP_CONTENT, true);
|
|
|
|
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
|
|
writer.setEncoding("UTF-8");
|
|
|
|
writer.putContent(modelStream); // also invokes policies for DictionaryModelType - e.g. onContentUpdate
|
|
modelStream.close();
|
|
|
|
// activate the model
|
|
nodeService.setProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE, Boolean.valueOf(activate));
|
|
|
|
// note: model will be loaded as part of DictionaryModelType.beforeCommit()
|
|
|
|
return modelNodeRef;
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_DEPLOYMENT_FAILED, e);
|
|
}
|
|
}
|
|
|
|
public void deployModel(InputStream modelStream, String modelFileName)
|
|
{
|
|
deployModel(modelStream, modelFileName, true);
|
|
}
|
|
|
|
public QName activateModel(String modelFileName)
|
|
{
|
|
try
|
|
{
|
|
return activateOrDeactivate(modelFileName, true);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_ACTIVATION_FAILED, e);
|
|
}
|
|
}
|
|
|
|
public QName deactivateModel(String modelFileName)
|
|
{
|
|
try
|
|
{
|
|
return activateOrDeactivate(modelFileName, false);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_DEACTIVATION_FAILED, e);
|
|
}
|
|
}
|
|
|
|
private QName activateOrDeactivate(String modelFileName, boolean activate)
|
|
{
|
|
// Check that all the passed values are not null
|
|
ParameterCheck.mandatoryString("modelFileName", modelFileName);
|
|
|
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+"//.[@cm:name='"+modelFileName+"' and "+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
|
|
|
if (nodeRefs.size() == 0)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_NOT_FOUND, new Object[] { modelFileName });
|
|
}
|
|
else if (nodeRefs.size() > 1)
|
|
{
|
|
// unexpected: should not find multiple nodes with same name
|
|
throw new AlfrescoRuntimeException(MODELS_MULTIPLE_FOUND, new Object[] { modelFileName });
|
|
}
|
|
|
|
NodeRef modelNodeRef = nodeRefs.get(0);
|
|
|
|
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;
|
|
if (modelQName != null)
|
|
{
|
|
try
|
|
{
|
|
modelDef = dictionaryDAO.getModel(modelQName);
|
|
}
|
|
catch (DictionaryException e)
|
|
{
|
|
logger.warn(e);
|
|
}
|
|
}
|
|
|
|
if (activate)
|
|
{
|
|
// activate
|
|
if (isActive)
|
|
{
|
|
if (modelDef != null)
|
|
{
|
|
// model is already activated
|
|
throw new AlfrescoRuntimeException(MODEL_ALREADY_ACTIVATED, new Object[] { modelQName });
|
|
}
|
|
else
|
|
{
|
|
logger.warn("Model is set to active but not loaded in Dictionary - trying to load...");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (modelDef != null)
|
|
{
|
|
logger.warn("Model is loaded in Dictionary but is not set to active - trying to activate...");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// deactivate
|
|
if (!isActive)
|
|
{
|
|
if (modelDef == null)
|
|
{
|
|
// model is already deactivated
|
|
throw new AlfrescoRuntimeException(MODEL_ALREADY_DEACTIVATED, new Object[] { modelQName });
|
|
}
|
|
else
|
|
{
|
|
logger.warn("Model is set to inactive but loaded in Dictionary - trying to unload...");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (modelDef == null)
|
|
{
|
|
logger.warn("Model is not loaded in Dictionary but is set to active - trying to deactivate...");
|
|
}
|
|
}
|
|
}
|
|
|
|
// activate/deactivate the model
|
|
nodeService.setProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE, new Boolean(activate));
|
|
|
|
// note: model will be loaded/unloaded as part of DictionaryModelType.beforeCommit()
|
|
return modelQName;
|
|
}
|
|
|
|
public QName undeployModel(String modelFileName)
|
|
{
|
|
// Check that all the passed values are not null
|
|
ParameterCheck.mandatory("modelFileName", modelFileName);
|
|
|
|
QName modelQName = null;
|
|
|
|
try
|
|
{
|
|
// find model in repository
|
|
|
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = null;
|
|
try
|
|
{
|
|
nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+"//.[@cm:name='"+modelFileName+"' and "+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
|
}
|
|
catch (InvalidNodeRefException inre)
|
|
{
|
|
String msg = "Model no longer exists: "+modelFileName+" - "+inre;
|
|
logger.warn(msg);
|
|
// for now, assume concurrency failure
|
|
throw new ConcurrencyFailureException(getLocalisedMessage(MODEL_NO_LONGER_EXISTS, modelFileName));
|
|
}
|
|
|
|
if (nodeRefs.size() == 0)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_NOT_FOUND, new Object[] { modelFileName });
|
|
}
|
|
else if (nodeRefs.size() > 1)
|
|
{
|
|
// unexpected: should not find multiple nodes with same name
|
|
throw new AlfrescoRuntimeException(MODELS_MULTIPLE_FOUND, new Object[] { modelFileName });
|
|
}
|
|
|
|
NodeRef modelNodeRef = nodeRefs.get(0);
|
|
|
|
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;
|
|
if (modelQName != null)
|
|
{
|
|
try
|
|
{
|
|
modelDef = dictionaryDAO.getModel(modelQName);
|
|
}
|
|
catch (DictionaryException e)
|
|
{
|
|
logger.warn(e);
|
|
}
|
|
}
|
|
|
|
if (isActive)
|
|
{
|
|
if (modelDef == null)
|
|
{
|
|
logger.warn("Model is set to active but not loaded in Dictionary - trying to undeploy...");
|
|
}
|
|
}
|
|
|
|
// permanently remove model from repository
|
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
|
|
|
try
|
|
{
|
|
nodeService.deleteNode(modelNodeRef);
|
|
}
|
|
catch (DictionaryException de)
|
|
{
|
|
String msg = "Model undeployment failed: "+modelFileName+" - "+de;
|
|
logger.warn(msg);
|
|
// for now, assume concurrency failure
|
|
throw new ConcurrencyFailureException(msg);
|
|
}
|
|
|
|
// note: deleted model will be unloaded as part of DictionaryModelType.beforeCommit()
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MODEL_UNDEPLOYMENT_FAILED, e);
|
|
}
|
|
|
|
return modelQName;
|
|
}
|
|
|
|
public List<String> getMessageBundles()
|
|
{
|
|
StoreRef storeRef = repoMessagesLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
Collection<String> registeredBundles = messageService.getRegisteredBundles();
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoMessagesLocation.getPath()+CRITERIA_ALL+"["+defaultSubtypeOfContent+"]", null, namespaceService, false);
|
|
|
|
List<String> resourceBundlesInRepo = new ArrayList<String>();
|
|
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
String resourceName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
|
|
String resourceBundleBaseName = null;
|
|
int idx1 = resourceName.indexOf("_");
|
|
if (idx1 > 0)
|
|
{
|
|
resourceBundleBaseName = resourceName.substring(0, idx1);
|
|
}
|
|
else
|
|
{
|
|
int idx2 = resourceName.indexOf(".");
|
|
if (idx2 > 0)
|
|
{
|
|
resourceBundleBaseName = resourceName.substring(0, idx2);
|
|
}
|
|
else
|
|
{
|
|
// Unexpected format
|
|
logger.warn("Unexpected message resource name: " + resourceName);
|
|
}
|
|
}
|
|
|
|
if (registeredBundles != null)
|
|
{
|
|
for (String registeredBundlePath : registeredBundles)
|
|
{
|
|
if (registeredBundlePath.endsWith(resourceBundleBaseName) && (! resourceBundlesInRepo.contains(resourceBundleBaseName)))
|
|
{
|
|
resourceBundlesInRepo.add(resourceBundleBaseName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unexpected
|
|
logger.error("Message bundle not registered: " + resourceBundleBaseName);
|
|
}
|
|
}
|
|
|
|
return resourceBundlesInRepo;
|
|
}
|
|
|
|
public String deployMessageBundle(String resourceClasspath)
|
|
{
|
|
// Check that all the passed values are not null
|
|
ParameterCheck.mandatory("ResourceClasspath", resourceClasspath);
|
|
|
|
String bundleBaseName = resourceClasspath;
|
|
|
|
// note: resource path should be in form path1/path2/path3/bundlebasename
|
|
int idx = resourceClasspath.lastIndexOf("/");
|
|
|
|
if (idx != -1)
|
|
{
|
|
if (idx < (resourceClasspath.length()-1))
|
|
{
|
|
bundleBaseName = resourceClasspath.substring(idx+1);
|
|
}
|
|
else
|
|
{
|
|
bundleBaseName = null;
|
|
}
|
|
}
|
|
|
|
checkBundleBaseName(bundleBaseName);
|
|
|
|
String pattern = "classpath*:" + resourceClasspath + "*" + MessageServiceImpl.PROPERTIES_FILE_SUFFIX;
|
|
|
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
|
|
|
try
|
|
{
|
|
Resource[] resources = resolver.getResources(pattern);
|
|
|
|
if ((resources != null) && (resources.length > 0))
|
|
{
|
|
ArrayList<String> names = new ArrayList<String>();
|
|
ArrayList<Resource> filteredResources = new ArrayList<Resource>();
|
|
|
|
for (int i = 0; i < resources.length; i++)
|
|
{
|
|
String filename = resources[i].getFilename();
|
|
if (! names.contains(filename))
|
|
{
|
|
names.add(filename);
|
|
filteredResources.add(resources[i]);
|
|
}
|
|
}
|
|
|
|
for (Resource resource : filteredResources)
|
|
{
|
|
InputStream fileStream = resource.getInputStream();
|
|
String filename = resource.getFilename();
|
|
deployMessageResourceFile(resourceClasspath, filename, fileStream, false);
|
|
}
|
|
|
|
// register bundle
|
|
|
|
StoreRef storeRef = repoMessagesLocation.getStoreRef();
|
|
String repoBundlePath = storeRef.toString() + repoMessagesLocation.getPath() + "/cm:" + bundleBaseName;
|
|
messageService.registerResourceBundle(repoBundlePath);
|
|
|
|
logger.info("Message resource bundle deployed: " + bundleBaseName);
|
|
}
|
|
else
|
|
{
|
|
logger.warn("No message resources found: " + resourceClasspath);
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_NOT_FOUND, new Object[] { resourceClasspath });
|
|
}
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_DEPLOYMENT_FAILED, new Object[] { resourceClasspath }, e);
|
|
}
|
|
|
|
return bundleBaseName;
|
|
}
|
|
|
|
/*
|
|
* Deploy message resource file
|
|
*/
|
|
private void deployMessageResourceFile(String bundleBasePath, String name, InputStream resourceStream, boolean registerResourceBundle)
|
|
{
|
|
// Check that all the passed values are not null
|
|
ParameterCheck.mandatory("BundleBasePath", bundleBasePath);
|
|
ParameterCheck.mandatory("Name", name);
|
|
ParameterCheck.mandatory("ResourceStream", resourceStream);
|
|
|
|
try
|
|
{
|
|
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
|
|
contentProps.put(ContentModel.PROP_NAME, name);
|
|
|
|
StoreRef storeRef = repoMessagesLocation.getStoreRef();
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoMessagesLocation.getPath(), null, namespaceService, false);
|
|
|
|
if (nodeRefs.size() == 0)
|
|
{
|
|
throw new AlfrescoRuntimeException(MESSAGES_LOCATION_NOT_FOUND, new Object[] { repoMessagesLocation.getPath() });
|
|
}
|
|
else if (nodeRefs.size() > 1)
|
|
{
|
|
// unexpected: should not find multiple nodes with same name
|
|
throw new AlfrescoRuntimeException(MESSAGES_LOCATION_MULTIPLE_FOUND, new Object[] { repoMessagesLocation.getPath() });
|
|
}
|
|
|
|
NodeRef customLabelsNodeRef = nodeRefs.get(0);
|
|
|
|
ChildAssociationRef association = nodeService.createNode(customLabelsNodeRef,
|
|
ContentModel.ASSOC_CONTAINS,
|
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, name),
|
|
ContentModel.TYPE_CONTENT,
|
|
contentProps);
|
|
|
|
NodeRef content = association.getChildRef();
|
|
|
|
// add titled aspect (for Web Client display)
|
|
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
|
|
titledProps.put(ContentModel.PROP_TITLE, name);
|
|
titledProps.put(ContentModel.PROP_DESCRIPTION, name);
|
|
nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps);
|
|
|
|
// add inline-editable aspect
|
|
Map<QName, Serializable> editProps = new HashMap<QName, Serializable>(1, 1.0f);
|
|
editProps.put(ApplicationModel.PROP_EDITINLINE, true);
|
|
nodeService.addAspect(content, ApplicationModel.ASPECT_INLINEEDITABLE, editProps);
|
|
|
|
ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true);
|
|
|
|
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
|
|
writer.setEncoding("UTF-8");
|
|
|
|
writer.putContent(resourceStream);
|
|
resourceStream.close();
|
|
|
|
if (registerResourceBundle == true)
|
|
{
|
|
String bundleBaseName = null;
|
|
int idx = bundleBasePath.lastIndexOf("/");
|
|
if ((idx != -1) && (idx != bundleBasePath.length() - 1))
|
|
{
|
|
bundleBaseName = bundleBasePath.substring(idx+1);
|
|
}
|
|
else
|
|
{
|
|
bundleBaseName = bundleBasePath;
|
|
}
|
|
|
|
String repoBundlePath = storeRef.toString() + repoMessagesLocation.getPath() + "/cm:" + bundleBaseName;
|
|
messageService.registerResourceBundle(repoBundlePath);
|
|
}
|
|
|
|
logger.info("Message resource deployed: " + name);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException("Message resource deployment failed", e);
|
|
}
|
|
}
|
|
|
|
public void undeployMessageBundle(String bundleBaseName)
|
|
{
|
|
checkBundleBaseName(bundleBaseName);
|
|
|
|
try
|
|
{
|
|
StoreRef storeRef = repoMessagesLocation.getStoreRef();
|
|
|
|
// unregister bundle
|
|
String repoBundlePath = storeRef.toString() + repoMessagesLocation.getPath() + "/cm:" + bundleBaseName;
|
|
messageService.unregisterResourceBundle(repoBundlePath);
|
|
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoMessagesLocation.getPath()+CRITERIA_ALL, null, namespaceService, false);
|
|
|
|
boolean found = false;
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
String resourceName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
|
|
|
|
if (bundleBaseName.equals(messageService.getBaseBundleName(resourceName)))
|
|
{
|
|
// remove message resource file from the repository
|
|
nodeService.deleteNode(nodeRef);
|
|
found = true; // continue to undeploy any others
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
logger.info("Message resources undeployed: " + bundleBaseName);
|
|
}
|
|
else
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_NOT_FOUND, new Object[] { repoBundlePath });
|
|
}
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_UNDEPLOYMENT_FAILED, t);
|
|
}
|
|
}
|
|
|
|
public void reloadMessageBundle(String bundleBaseName)
|
|
{
|
|
checkBundleBaseName(bundleBaseName);
|
|
|
|
try
|
|
{
|
|
StoreRef storeRef = repoMessagesLocation.getStoreRef();
|
|
|
|
// re-register bundle
|
|
|
|
String repoBundlePath = storeRef.toString() + repoMessagesLocation.getPath() + "/cm:" + bundleBaseName;
|
|
|
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoMessagesLocation.getPath()+CRITERIA_ALL, null, namespaceService, false);
|
|
|
|
boolean found = false;
|
|
for (NodeRef nodeRef : nodeRefs)
|
|
{
|
|
String resourceName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
|
|
|
|
if (bundleBaseName.equals(messageService.getBaseBundleName(resourceName)))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
messageService.unregisterResourceBundle(repoBundlePath);
|
|
messageService.registerResourceBundle(repoBundlePath);
|
|
|
|
logger.info("Message resources re-loaded: " + bundleBaseName);
|
|
}
|
|
else
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_NOT_FOUND, new Object[] { repoBundlePath });
|
|
}
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_RESOURCES_RELOAD_FAILED, e);
|
|
}
|
|
}
|
|
|
|
private void checkBundleBaseName(String bundleBaseName)
|
|
{
|
|
if ((bundleBaseName == null) || (bundleBaseName.equals("")))
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_MISSING_BUNDLE_BASE_NAME);
|
|
}
|
|
|
|
if (bundleBaseName.indexOf("_") != -1)
|
|
{
|
|
// currently limited due to parser in MessageServiceImpl.getBaseBundleName
|
|
throw new AlfrescoRuntimeException(MSG_BASE_NAME_CONTAIN_UNDERSCORE, new Object[] { bundleBaseName });
|
|
}
|
|
|
|
if (bundleBaseName.indexOf(".") != -1)
|
|
{
|
|
throw new AlfrescoRuntimeException(MSG_BASE_NAME_CONTAIN_PERIOD, new Object[] { bundleBaseName });
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public RepoUsage getRestrictions()
|
|
{
|
|
return repoUsageComponent.getRestrictions();
|
|
}
|
|
|
|
@Override
|
|
public RepoUsage getUsage()
|
|
{
|
|
return repoUsageComponent.getUsage();
|
|
}
|
|
|
|
@Override
|
|
public boolean updateUsage(UsageType usageType)
|
|
{
|
|
return repoUsageComponent.updateUsage(usageType);
|
|
}
|
|
|
|
@Override
|
|
public RepoUsageStatus getUsageStatus()
|
|
{
|
|
return repoUsageComponent.getUsageStatus();
|
|
}
|
|
|
|
private String getLocalisedMessage(String msgId, Object... params)
|
|
{
|
|
String localisedMsg = I18NUtil.getMessage(msgId, params);
|
|
if (localisedMsg == null)
|
|
{
|
|
localisedMsg = msgId;
|
|
}
|
|
return localisedMsg;
|
|
}
|
|
}
|