mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-22 15:12:38 +00:00 
			
		
		
		
	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/BRANCHES/DEV/5.1.N/root@125606 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
		
			
				
	
	
		
			782 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			782 lines
		
	
	
		
			30 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.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) in order to ensure that we have a basic version of
 | |
|         // the dictionary (static models) loaded at least
 | |
|         dictionaryDAO.reset();
 | |
| 
 | |
|         // Register listeners, which will be called when the dictionary is next reloaded
 | |
|         register();
 | |
|         
 | |
|         // Trigger a reload.  The callbacks will occur immediately on the current thread, however,
 | |
|         // the model created in reset() will still be available for the basic necessities
 | |
|         dictionaryDAO.init();
 | |
|         
 | |
|         // The listeners can now know about this
 | |
|         // However, the listeners will be needing to access the dictionary themselves, hence the earlier 'reset'
 | |
|         // to ensure that there is no deadlock waiting for a new dictionary
 | |
|         ((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()
 | |
|     {
 | |
|     }
 | |
| }
 |