mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
109729: Merged 5.0.2 (5.0.2) to 5.0.N (5.0.3) 109705: MNT-14463: Deadlock during startup DictionaryDAO: Remove side-effect code in: - DictionaryRepositoryBootstrap.register: Now it only registers listeners rathern than destroying and rebuilding the dictionary - DictionaryDAOImpl.removeDictionaryRegistry: Now it only removes the dictionary rather than removes and reinitialized DictionaryRepositoryBootstrap.onBootstrap: - RESETS the dictionary (destroy, reinitialize) - Then registers listeners - Then fires application events The stacks and logs for the deadlock show that the dictioanary bootstrap was triggering a background reload and then firing the application events without checking that the dictionary had loaded. The events will now only be fired once the dictionary has successfully retrieved an instance i.e. no other code will need to wait for the background thread after the reset has finished. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@109804 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
768 lines
28 KiB
Java
768 lines
28 KiB
Java
/*
|
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.repo.dictionary;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.content.EmptyContentReader;
|
|
import org.alfresco.repo.dictionary.DynamicModelPolicies.OnLoadDynamicModel;
|
|
import org.alfresco.repo.i18n.MessageDeployer;
|
|
import org.alfresco.repo.i18n.MessageService;
|
|
import org.alfresco.repo.policy.ClassPolicyDelegate;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.tenant.TenantAdminService;
|
|
import org.alfresco.repo.tenant.TenantDeployer;
|
|
import org.alfresco.repo.tenant.TenantService;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
|
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
|
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.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.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.alfresco.util.PropertyCheck;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationEvent;
|
|
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
|
|
|
/**
|
|
* Bootstrap the dictionary from specified locations within the repository
|
|
*
|
|
* @author Roy Wetherall, JanV, sglover
|
|
*/
|
|
public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean
|
|
implements TenantDeployer, DictionaryListener, /*TenantDictionaryListener, */MessageDeployer
|
|
{
|
|
// Logging support
|
|
private static Log logger = LogFactory.getLog(DictionaryRepositoryBootstrap.class);
|
|
|
|
/** Locations in the repository from which models should be loaded */
|
|
private List<RepositoryLocation> repositoryModelsLocations = new ArrayList<RepositoryLocation>();
|
|
|
|
/** Locations in the repository from which messages should be loaded */
|
|
private List<RepositoryLocation> repositoryMessagesLocations = new ArrayList<RepositoryLocation>();
|
|
|
|
/** Dictionary DAO */
|
|
private DictionaryDAO dictionaryDAO = null;
|
|
|
|
/** The content service */
|
|
private ContentService contentService;
|
|
|
|
/** The node service */
|
|
private NodeService nodeService;
|
|
|
|
/** The tenant admin service */
|
|
private TenantAdminService tenantAdminService;
|
|
|
|
/** The namespace service */
|
|
private NamespaceService namespaceService;
|
|
|
|
/** The message service */
|
|
private MessageService messageService;
|
|
|
|
/** The transaction service */
|
|
private TransactionService transactionService;
|
|
|
|
/** The policy component */
|
|
private PolicyComponent policyComponent;
|
|
|
|
|
|
/**
|
|
* Sets the Dictionary DAO
|
|
*
|
|
* @param dictionaryDAO DictionaryDAO
|
|
*/
|
|
public void setDictionaryDAO(DictionaryDAO dictionaryDAO)
|
|
{
|
|
this.dictionaryDAO = dictionaryDAO;
|
|
}
|
|
|
|
/**
|
|
* Set the content service
|
|
*
|
|
* @param contentService the content service
|
|
*/
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
/**
|
|
* Set the node service
|
|
*
|
|
* @param nodeService the node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
public PolicyComponent getPolicyComponent()
|
|
{
|
|
return policyComponent;
|
|
}
|
|
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* Set the tenant admin service
|
|
*
|
|
* @param tenantAdminService the tenant admin service
|
|
*/
|
|
public void setTenantAdminService(TenantAdminService tenantAdminService)
|
|
{
|
|
this.tenantAdminService = tenantAdminService;
|
|
}
|
|
|
|
/**
|
|
* Set the namespace service
|
|
*
|
|
* @param namespaceService the namespace service
|
|
*/
|
|
public void setNamespaceService(NamespaceService namespaceService)
|
|
{
|
|
this.namespaceService = namespaceService;
|
|
}
|
|
|
|
/**
|
|
* Set the message service
|
|
*
|
|
* @param messageService the message service
|
|
*/
|
|
public void setMessageService(MessageService messageService)
|
|
{
|
|
this.messageService = messageService;
|
|
}
|
|
|
|
/**
|
|
* Set the transaction service
|
|
*
|
|
* @param transactionService the transaction service
|
|
*/
|
|
public void setTransactionService(TransactionService transactionService)
|
|
{
|
|
this.transactionService = transactionService;
|
|
}
|
|
|
|
/**
|
|
* Set the repository models locations
|
|
*
|
|
* @param repositoryLocations list of the repository models locations
|
|
*/ public void setRepositoryModelsLocations(
|
|
List<RepositoryLocation> repositoryLocations)
|
|
{
|
|
this.repositoryModelsLocations = repositoryLocations;
|
|
}
|
|
|
|
/**
|
|
* Set the repository messages (resource bundle) locations
|
|
*
|
|
* @param repositoryLocations
|
|
* list of the repository messages locations
|
|
*/
|
|
public void setRepositoryMessagesLocations(
|
|
List<RepositoryLocation> repositoryLocations)
|
|
{
|
|
this.repositoryMessagesLocations = repositoryLocations;
|
|
}
|
|
|
|
private ClassPolicyDelegate<OnLoadDynamicModel> onLoadDynamicModelDelegate;
|
|
|
|
/**
|
|
* Initialise - after bootstrap of schema and tenant admin service
|
|
*/
|
|
public void init()
|
|
{
|
|
PropertyCheck.mandatory(this, "dictionaryDAO", dictionaryDAO);
|
|
PropertyCheck.mandatory(this, "contentService", contentService);
|
|
PropertyCheck.mandatory(this, "nodeService", nodeService);
|
|
PropertyCheck.mandatory(this, "tenantAdminService", tenantAdminService);
|
|
PropertyCheck.mandatory(this, "namespaceService", namespaceService);
|
|
PropertyCheck.mandatory(this, "messageService", messageService);
|
|
PropertyCheck.mandatory(this, "transactionService", transactionService);
|
|
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
|
|
|
|
if(onLoadDynamicModelDelegate == null)
|
|
{
|
|
onLoadDynamicModelDelegate = policyComponent.registerClassPolicy(DynamicModelPolicies.OnLoadDynamicModel.class);
|
|
}
|
|
|
|
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
|
|
{
|
|
public Object execute() throws Exception
|
|
{
|
|
onDictionaryInit();
|
|
initMessages();
|
|
|
|
return (Object)null;
|
|
}
|
|
}, transactionService.isReadOnly(), false);
|
|
}
|
|
|
|
public void destroy()
|
|
{
|
|
// NOOP - will be destroyed directly via DictionaryComponent
|
|
}
|
|
|
|
/**
|
|
* Perform the actual repository access, checking for the existence of a valid transaction
|
|
*/
|
|
private void onDictionaryInitInTxn()
|
|
{
|
|
if (AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_NONE)
|
|
{
|
|
throw new IllegalStateException("The Repository-based dictionary initialization has to be done in the context of a transaction.");
|
|
}
|
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
String tenantDomain = tenantAdminService.getCurrentUserDomain();
|
|
logger.trace("onDictionaryInit: ["+Thread.currentThread()+"]"+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
|
|
}
|
|
|
|
Collection<QName> modelsBefore = dictionaryDAO.getModels(true); // note: re-entrant
|
|
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
|
|
|
|
List<String> loadedModels = new ArrayList<String>();
|
|
|
|
if (this.repositoryModelsLocations != null)
|
|
{
|
|
// URI to model map
|
|
Map<String, DynamicModelInfo> modelMap = new HashMap<String, DynamicModelInfo>();
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("onDictionaryInit: locations="+this.repositoryModelsLocations);
|
|
}
|
|
|
|
// Register the models found in the repository
|
|
|
|
for (RepositoryLocation repositoryLocation : this.repositoryModelsLocations)
|
|
{
|
|
StoreRef storeRef = repositoryLocation.getStoreRef();
|
|
|
|
if (! nodeService.exists(storeRef))
|
|
{
|
|
logger.info("StoreRef '"+ storeRef+"' does not exist");
|
|
continue; // skip this location
|
|
}
|
|
|
|
List<NodeRef> nodeRefs = null;
|
|
|
|
if (repositoryLocation.getQueryLanguage().equals(RepositoryLocation.LANGUAGE_PATH))
|
|
{
|
|
nodeRefs = getNodes(storeRef, repositoryLocation, ContentModel.TYPE_DICTIONARY_MODEL);
|
|
|
|
if (nodeRefs.size() > 0)
|
|
{
|
|
for (NodeRef dictionaryModel : nodeRefs)
|
|
{
|
|
try
|
|
{
|
|
// Ignore if the node is a working copy or archived, or if its inactive
|
|
if (! (nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_WORKING_COPY) ||
|
|
nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_ARCHIVED)))
|
|
{
|
|
Boolean isActive = (Boolean)nodeService.getProperty(dictionaryModel, ContentModel.PROP_MODEL_ACTIVE);
|
|
|
|
if ((isActive != null) && (isActive.booleanValue() == true))
|
|
{
|
|
M2Model model = createM2Model(dictionaryModel);
|
|
if (model != null)
|
|
{
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("onDictionaryInit: "+model.getName()+" ("+dictionaryModel+")");
|
|
}
|
|
|
|
for (M2Namespace namespace : model.getNamespaces())
|
|
{
|
|
modelMap.put(namespace.getUri(), new DynamicModelInfo(repositoryLocation, model, dictionaryModel));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (InvalidNodeRefException inre)
|
|
{
|
|
// ignore - model no longer exists
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("onDictionaryInit: "+inre+" (assume concurrently deleted)");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.error("Unsupported query language for models location: " + repositoryLocation.getQueryLanguage());
|
|
}
|
|
}
|
|
|
|
// Load the models ensuring that they are loaded in the correct order
|
|
for (Map.Entry<String, DynamicModelInfo> entry : modelMap.entrySet())
|
|
{
|
|
RepositoryLocation importedLocation = entry.getValue().location;
|
|
M2Model importedModel = entry.getValue().model;
|
|
loadModel(modelMap, loadedModels, importedModel, importedLocation);
|
|
notifyDynamicModelLoaded(entry.getValue());
|
|
}
|
|
}
|
|
|
|
Collection<QName> modelsAfter = dictionaryDAO.getModels(true);
|
|
int modelsAfterCnt = (modelsAfter != null ? modelsAfter.size() : 0);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
String tenantDomain = tenantAdminService.getCurrentUserDomain();
|
|
logger.debug("Model count: before="+modelsBeforeCnt+", load/update="+loadedModels.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs ["+Thread.currentThread()+"] "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
|
|
}
|
|
}
|
|
|
|
public void notifyDynamicModelLoaded(DynamicModelInfo entry)
|
|
{
|
|
if(onLoadDynamicModelDelegate == null)
|
|
{
|
|
onLoadDynamicModelDelegate = policyComponent.registerClassPolicy(DynamicModelPolicies.OnLoadDynamicModel.class);
|
|
}
|
|
|
|
DynamicModelPolicies.OnLoadDynamicModel policy = onLoadDynamicModelDelegate.get(ContentModel.TYPE_CONTENT);
|
|
policy.onLoadDynamicModel(entry.model, entry.nodeRef);
|
|
}
|
|
|
|
public void initMessages()
|
|
{
|
|
if (this.repositoryMessagesLocations != null)
|
|
{
|
|
// Register the messages found in the repository
|
|
for (RepositoryLocation repositoryLocation : this.repositoryMessagesLocations)
|
|
{
|
|
StoreRef storeRef = repositoryLocation.getStoreRef();
|
|
|
|
if (! nodeService.exists(storeRef))
|
|
{
|
|
logger.info("StoreRef '"+ storeRef+"' does not exist");
|
|
continue; // skip this location
|
|
}
|
|
|
|
if (repositoryLocation.getQueryLanguage().equals(RepositoryLocation.LANGUAGE_PATH))
|
|
{
|
|
List<NodeRef> nodeRefs = getNodes(storeRef, repositoryLocation, ContentModel.TYPE_CONTENT);
|
|
|
|
if (nodeRefs.size() > 0)
|
|
{
|
|
List<String> resourceBundleBaseNames = new ArrayList<String>();
|
|
|
|
for (NodeRef messageResource : nodeRefs)
|
|
{
|
|
String resourceName = (String) nodeService.getProperty(messageResource, ContentModel.PROP_NAME);
|
|
|
|
String bundleBaseName = messageService.getBaseBundleName(resourceName);
|
|
|
|
if (!resourceBundleBaseNames.contains(bundleBaseName))
|
|
{
|
|
resourceBundleBaseNames.add(bundleBaseName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.error("Unsupported query language for messages location: " + repositoryLocation.getQueryLanguage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// note: active or inactive
|
|
public List<NodeRef> getModelRefs()
|
|
{
|
|
List<NodeRef> modelRefs = new ArrayList<NodeRef>();
|
|
|
|
for (RepositoryLocation repositoryLocation : this.repositoryModelsLocations)
|
|
{
|
|
StoreRef storeRef = repositoryLocation.getStoreRef();
|
|
|
|
if (! nodeService.exists(storeRef))
|
|
{
|
|
logger.info("StoreRef '"+ storeRef+"' does not exist");
|
|
continue; // skip this location
|
|
}
|
|
|
|
if (repositoryLocation.getQueryLanguage().equals(RepositoryLocation.LANGUAGE_PATH))
|
|
{
|
|
List<NodeRef> nodeRefs = getNodes(storeRef, repositoryLocation, ContentModel.TYPE_DICTIONARY_MODEL);
|
|
|
|
if (nodeRefs.size() > 0)
|
|
{
|
|
for (NodeRef dictionaryModel : nodeRefs)
|
|
{
|
|
try
|
|
{
|
|
// Ignore if the node is a working copy or archived
|
|
if (! (nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_WORKING_COPY) || nodeService.hasAspect(dictionaryModel, ContentModel.ASPECT_ARCHIVED)))
|
|
{
|
|
modelRefs.add(dictionaryModel);
|
|
}
|
|
}
|
|
catch (InvalidNodeRefException inre)
|
|
{
|
|
// ignore - model no longer exists
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("getModelRefs: "+inre+" (assume concurrently deleted)");
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.error("Unsupported query language for models location: " + repositoryLocation.getQueryLanguage());
|
|
}
|
|
}
|
|
|
|
return modelRefs;
|
|
}
|
|
|
|
protected List<NodeRef> getNodes(StoreRef storeRef, RepositoryLocation repositoryLocation, QName nodeType)
|
|
{
|
|
List<NodeRef> nodeRefs = new ArrayList<NodeRef>();
|
|
|
|
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
|
|
if(nodeService.exists(rootNodeRef) == false)
|
|
{
|
|
//Tenant is deleted. But cache refresh was called to inform another cluster nodes
|
|
//Should be reworked when MNT-11638 will be implemented
|
|
return nodeRefs;
|
|
}
|
|
|
|
if(repositoryLocation instanceof DynamicCreateRepositoryLocation)
|
|
{
|
|
((DynamicCreateRepositoryLocation)repositoryLocation).checkAndCreate(rootNodeRef);
|
|
}
|
|
|
|
String[] pathElements = repositoryLocation.getPathElements();
|
|
|
|
NodeRef folderNodeRef = rootNodeRef;
|
|
if (pathElements.length > 0)
|
|
{
|
|
folderNodeRef = resolveQNamePath(rootNodeRef, pathElements);
|
|
}
|
|
|
|
if (folderNodeRef != null)
|
|
{
|
|
Set<QName> types = new HashSet<QName>(1);
|
|
types.add(nodeType);
|
|
List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(folderNodeRef, types);
|
|
|
|
if (childAssocRefs.size() > 0)
|
|
{
|
|
nodeRefs = new ArrayList<NodeRef>(childAssocRefs.size());
|
|
for (ChildAssociationRef childAssocRef : childAssocRefs)
|
|
{
|
|
nodeRefs.add(childAssocRef.getChildRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
return nodeRefs;
|
|
}
|
|
|
|
private class DynamicModelInfo
|
|
{
|
|
RepositoryLocation location;
|
|
M2Model model;
|
|
NodeRef nodeRef;
|
|
|
|
|
|
DynamicModelInfo(RepositoryLocation location, M2Model model, NodeRef nodeRef)
|
|
{
|
|
this.location = location;
|
|
this.model = model;
|
|
this.nodeRef = nodeRef;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads a model (and its dependents) if it does not exist in the list of loaded models.
|
|
*
|
|
* @param modelMap a map of the models to be loaded
|
|
* @param loadedModels the list of models already loaded
|
|
* @param model the model to try and load
|
|
*/
|
|
private void loadModel(Map<String, DynamicModelInfo> modelMap, List<String> loadedModels, M2Model model, RepositoryLocation modelLocation)
|
|
{
|
|
String modelName = model.getName();
|
|
if (loadedModels.contains(modelName) == false)
|
|
{
|
|
for (M2Namespace importNamespace : model.getImports())
|
|
{
|
|
DynamicModelInfo entry = modelMap.get(importNamespace.getUri());
|
|
if (entry != null)
|
|
{
|
|
RepositoryLocation importedLocation = entry.location;
|
|
M2Model importedModel = entry.model;
|
|
|
|
// Ensure that the imported model is loaded first
|
|
loadModel(modelMap, loadedModels, importedModel, importedLocation);
|
|
}
|
|
// else we can assume that the imported model is already loaded, if this not the case then
|
|
// an error will be raised during compilation
|
|
}
|
|
|
|
try
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Loading model: " + modelName
|
|
+ " (from ["+ modelLocation.getStoreRef() + "]"+ modelLocation.getPath() + ")");
|
|
}
|
|
|
|
dictionaryDAO.putModel(model);
|
|
|
|
loadedModels.add(modelName);
|
|
}
|
|
catch (AlfrescoRuntimeException e)
|
|
{
|
|
// note: skip with warning - to allow server to start, and hence allow the possibility of fixing the broken model(s)
|
|
logger.warn("Failed to load model '" + modelName + "' : " + e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a M2Model from a dictionary model node
|
|
*
|
|
* @param nodeRef the dictionary model node reference
|
|
* @return the M2Model
|
|
*/
|
|
public M2Model createM2Model(NodeRef nodeRef)
|
|
{
|
|
M2Model model = null;
|
|
ContentReader contentReader = this.contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
|
|
if (contentReader != null)
|
|
{
|
|
if (contentReader instanceof EmptyContentReader)
|
|
{
|
|
// belts-and-braces
|
|
logger.error("Failed to create model (due to EmptyContentReader): "+nodeRef);
|
|
}
|
|
else
|
|
{
|
|
InputStream is = null;
|
|
try
|
|
{
|
|
is = contentReader.getContentInputStream();
|
|
model = M2Model.createModel(is);
|
|
}
|
|
finally
|
|
{
|
|
if (is != null)
|
|
{
|
|
try
|
|
{
|
|
is.close();
|
|
}
|
|
catch (IOException e)
|
|
{
|
|
logger.error("Failed to close input stream for " + nodeRef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// TODO should we inactivate the model node and put the error somewhere??
|
|
return model;
|
|
}
|
|
|
|
@Override
|
|
protected void onBootstrap(ApplicationEvent event)
|
|
{
|
|
// Reset the dictionary (destroy and reload)
|
|
dictionaryDAO.reset();
|
|
|
|
// Register listeners
|
|
register();
|
|
|
|
// The listeners can now know about this
|
|
((ApplicationContext) event.getSource()).publishEvent(new DictionaryRepositoryBootstrappedEvent(this));
|
|
}
|
|
|
|
@Override
|
|
protected void onShutdown(ApplicationEvent event)
|
|
{
|
|
// NOOP
|
|
}
|
|
|
|
public void onEnableTenant()
|
|
{
|
|
init(); // will be called in context of tenant
|
|
}
|
|
|
|
public void onDisableTenant()
|
|
{
|
|
destroy(); // will be called in context of tenant
|
|
}
|
|
|
|
/**
|
|
* Register listeners
|
|
*/
|
|
public void register()
|
|
{
|
|
// register with Dictionary Service to allow (re-)init
|
|
dictionaryDAO.registerListener(this);
|
|
|
|
// register with Message Service to allow (re-)init
|
|
messageService.register(this);
|
|
|
|
if (tenantAdminService.isEnabled())
|
|
{
|
|
// register dictionary repository bootstrap
|
|
tenantAdminService.register(this);
|
|
|
|
// register repository message (I18N) service
|
|
tenantAdminService.register(messageService);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unregister
|
|
*/
|
|
protected void unregister()
|
|
{
|
|
if (tenantAdminService.isEnabled())
|
|
{
|
|
// register dictionary repository bootstrap
|
|
tenantAdminService.unregister(this);
|
|
|
|
// register repository message (I18N) service
|
|
tenantAdminService.unregister(messageService);
|
|
}
|
|
}
|
|
|
|
// TODO refactor (see also MessageServiceImpl)
|
|
protected NodeRef resolveQNamePath(NodeRef rootNodeRef, String[] pathPrefixQNameStrings)
|
|
{
|
|
if (pathPrefixQNameStrings.length == 0)
|
|
{
|
|
throw new IllegalArgumentException("Path array is empty");
|
|
}
|
|
// walk the path
|
|
NodeRef parentNodeRef = rootNodeRef;
|
|
for (int i = 0; i < pathPrefixQNameStrings.length; i++)
|
|
{
|
|
String pathPrefixQNameString = pathPrefixQNameStrings[i];
|
|
|
|
QName pathQName = null;
|
|
if (tenantAdminService.isEnabled())
|
|
{
|
|
String[] parts = QName.splitPrefixedQName(pathPrefixQNameString);
|
|
if ((parts.length == 2) && (parts[0].equals(NamespaceService.APP_MODEL_PREFIX)))
|
|
{
|
|
String pathUriQNameString = new StringBuilder(64).
|
|
append(QName.NAMESPACE_BEGIN).
|
|
append(NamespaceService.APP_MODEL_1_0_URI).
|
|
append(QName.NAMESPACE_END).
|
|
append(parts[1]).toString();
|
|
|
|
pathQName = QName.createQName(pathUriQNameString);
|
|
}
|
|
else
|
|
{
|
|
pathQName = QName.createQName(pathPrefixQNameString, namespaceService);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pathQName = QName.createQName(pathPrefixQNameString, namespaceService);
|
|
}
|
|
|
|
List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(parentNodeRef, RegexQNamePattern.MATCH_ALL, pathQName);
|
|
if (childAssocRefs.size() != 1)
|
|
{
|
|
return null;
|
|
}
|
|
parentNodeRef = childAssocRefs.get(0).getChildRef();
|
|
}
|
|
return parentNodeRef;
|
|
}
|
|
|
|
/**
|
|
* Initialise the dictionary, ensuring that a transaction is available
|
|
*/
|
|
@Override
|
|
public void onDictionaryInit()
|
|
{
|
|
if(onLoadDynamicModelDelegate == null)
|
|
{
|
|
onLoadDynamicModelDelegate = policyComponent.registerClassPolicy(DynamicModelPolicies.OnLoadDynamicModel.class);
|
|
}
|
|
RetryingTransactionCallback<Void> initCallback = new RetryingTransactionCallback<Void>()
|
|
{
|
|
@Override
|
|
public Void execute() throws Throwable
|
|
{
|
|
onDictionaryInitInTxn();
|
|
return null;
|
|
}
|
|
};
|
|
transactionService.getRetryingTransactionHelper().doInTransaction(initCallback, true, false);
|
|
}
|
|
|
|
@Override
|
|
public void afterDictionaryDestroy()
|
|
{
|
|
}
|
|
|
|
@Override
|
|
public void afterDictionaryInit()
|
|
{
|
|
}
|
|
}
|