mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Dynamic model activation / deactivation - via WebClient UI or RepoAdmin console - also verified in MT env
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6712 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -614,24 +614,6 @@
|
|||||||
|
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="dictionaryModelType" class="org.alfresco.repo.dictionary.DictionaryModelType" init-method="init">
|
|
||||||
<property name="dictionaryDAO">
|
|
||||||
<ref bean="dictionaryDAO" />
|
|
||||||
</property>
|
|
||||||
<property name="namespaceDAO">
|
|
||||||
<ref bean="namespaceDAO" />
|
|
||||||
</property>
|
|
||||||
<property name="nodeService">
|
|
||||||
<ref bean="nodeService"/>
|
|
||||||
</property>
|
|
||||||
<property name="contentService">
|
|
||||||
<ref bean="contentService"/>
|
|
||||||
</property>
|
|
||||||
<property name="policyComponent">
|
|
||||||
<ref bean="policyComponent"/>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="dictionaryDAO" class="org.alfresco.repo.dictionary.DictionaryDAOImpl">
|
<bean id="dictionaryDAO" class="org.alfresco.repo.dictionary.DictionaryDAOImpl">
|
||||||
<constructor-arg index="0">
|
<constructor-arg index="0">
|
||||||
<ref bean="namespaceDAO" />
|
<ref bean="namespaceDAO" />
|
||||||
|
@@ -49,22 +49,30 @@ ok> show models
|
|||||||
|
|
||||||
ok> deploy model <fileClassPath>
|
ok> deploy model <fileClassPath>
|
||||||
|
|
||||||
Upload model to repository and into runtime data dictionary. This will also
|
Upload model to repository and load into runtime data dictionary. This will also
|
||||||
set the model as active.
|
set the repository model as active.
|
||||||
|
|
||||||
|
If a model is already deployed then it will be updated and re-deployed.
|
||||||
|
|
||||||
e.g. deploy model alfresco/extension/exampleModel.xml
|
e.g. deploy model alfresco/extension/exampleModel.xml
|
||||||
|
|
||||||
ok> undeploy model <modelFileName>
|
ok> undeploy model <modelFileName>
|
||||||
|
|
||||||
Permanently delete model from repository (all versions) and from runtime data dictionary.
|
Permanently delete model from repository (all versions) and unload from runtime data dictionary.
|
||||||
|
|
||||||
e.g. undeploy model exampleModel.xml
|
e.g. undeploy model exampleModel.xml
|
||||||
|
|
||||||
ok> reload model <modelFileName>
|
ok> activate model <modelFileName>
|
||||||
|
|
||||||
Reload (or load for first time) from repository into runtime data dictionary.
|
Set repository model to active and load into runtime data dictionary.
|
||||||
|
|
||||||
e.g. reload model exampleModel.xml
|
e.g. activate model exampleModel.xml
|
||||||
|
|
||||||
|
ok> deactivate model <modelFileName>
|
||||||
|
|
||||||
|
Set repository model to inactive and unload from runtime data dictionary.
|
||||||
|
|
||||||
|
e.g. deactivate model exampleModel.xml
|
||||||
|
|
||||||
##
|
##
|
||||||
## Message Admin Commands
|
## Message Admin Commands
|
||||||
|
@@ -11,18 +11,10 @@
|
|||||||
<property name="contentService" ref="ContentService"/>
|
<property name="contentService" ref="ContentService"/>
|
||||||
<property name="namespaceService" ref="namespaceService"/>
|
<property name="namespaceService" ref="namespaceService"/>
|
||||||
<property name="messageService" ref="messageService"/>
|
<property name="messageService" ref="messageService"/>
|
||||||
<property name="workflowService" ref="WorkflowService"/>
|
|
||||||
|
|
||||||
<property name="repositoryModelsLocation" ref="customModelsRepositoryLocation"/>
|
<property name="repositoryModelsLocation" ref="customModelsRepositoryLocation"/>
|
||||||
<property name="repositoryMessagesLocation" ref="customMessagesRepositoryLocation"/>
|
<property name="repositoryMessagesLocation" ref="customMessagesRepositoryLocation"/>
|
||||||
|
|
||||||
<property name="storeUrls">
|
|
||||||
<list>
|
|
||||||
<value>${spaces.store}</value>
|
|
||||||
<value>${spaces.archive.store}</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="repoAdminInterpreter" class="org.alfresco.repo.admin.RepoAdminInterpreter">
|
<bean id="repoAdminInterpreter" class="org.alfresco.repo.admin.RepoAdminInterpreter">
|
||||||
@@ -63,24 +55,14 @@
|
|||||||
|
|
||||||
<bean id="dictionaryRepositoryBootstrap" class="org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap">
|
<bean id="dictionaryRepositoryBootstrap" class="org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap">
|
||||||
|
|
||||||
<property name="dictionaryDAO">
|
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
||||||
<ref bean="dictionaryDAO"/>
|
<property name="contentService" ref="ContentService"/>
|
||||||
</property>
|
<property name="searchService" ref="SearchService"/>
|
||||||
<property name="contentService">
|
<property name="transactionService" ref="transactionComponent"/>
|
||||||
<ref bean="contentService"/>
|
<property name="namespaceService" ref="namespaceService"/>
|
||||||
</property>
|
<property name="nodeService" ref="NodeService"/>
|
||||||
<property name="searchService">
|
<property name="messageService" ref="messageService"/>
|
||||||
<ref bean="searchService"/>
|
<property name="tenantService" ref="tenantService"/>
|
||||||
</property>
|
|
||||||
<property name="transactionService">
|
|
||||||
<ref bean="transactionComponent"/>
|
|
||||||
</property>
|
|
||||||
|
|
||||||
<property name="namespaceService"><ref bean="namespaceService"/></property>
|
|
||||||
<property name="nodeService"><ref bean="NodeService"/></property>
|
|
||||||
<property name="messageService"><ref bean="messageService"/></property>
|
|
||||||
|
|
||||||
<property name="tenantService"><ref bean="tenantService"/></property>
|
|
||||||
<property name="tenantDeployerService" ref="tenantAdminService"/>
|
<property name="tenantDeployerService" ref="tenantAdminService"/>
|
||||||
|
|
||||||
<property name="repositoryModelsLocations">
|
<property name="repositoryModelsLocations">
|
||||||
@@ -97,4 +79,23 @@
|
|||||||
|
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="dictionaryModelType" class="org.alfresco.repo.dictionary.DictionaryModelType" init-method="init">
|
||||||
|
<property name="dictionaryDAO" ref="dictionaryDAO"/>
|
||||||
|
<property name="namespaceDAO" ref="namespaceDAO"/>
|
||||||
|
<property name="nodeService" ref="NodeService"/>
|
||||||
|
<property name="contentService" ref="contentService"/>
|
||||||
|
<property name="policyComponent" ref="policyComponent"/>
|
||||||
|
<property name="workflowService" ref="WorkflowService"/>
|
||||||
|
<property name="searchService" ref="SearchService"/>
|
||||||
|
<property name="namespaceService" ref="namespaceService"/>
|
||||||
|
<property name="tenantService" ref="tenantService"/>
|
||||||
|
|
||||||
|
<property name="storeUrls">
|
||||||
|
<list>
|
||||||
|
<value>${spaces.store}</value>
|
||||||
|
<value>${spaces.archive.store}</value>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
@@ -255,8 +255,8 @@ public class RepoAdminInterpreter extends BaseInterpreter
|
|||||||
InputStream fileStream = file.getInputStream();
|
InputStream fileStream = file.getInputStream();
|
||||||
|
|
||||||
String modelFileName = file.getFilename();
|
String modelFileName = file.getFilename();
|
||||||
QName modelQName = repoAdminService.deployModel(fileStream, modelFileName);
|
repoAdminService.deployModel(fileStream, modelFileName);
|
||||||
out.println("Model deployed: " + modelFileName + " [" + modelQName + "]");
|
out.println("Model deployed: " + modelFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command[1].equals("messages"))
|
else if (command[1].equals("messages"))
|
||||||
@@ -272,7 +272,7 @@ public class RepoAdminInterpreter extends BaseInterpreter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command[0].equals("reload"))
|
else if (command[0].equals("activate"))
|
||||||
{
|
{
|
||||||
if (command.length != 3)
|
if (command.length != 3)
|
||||||
{
|
{
|
||||||
@@ -282,15 +282,38 @@ public class RepoAdminInterpreter extends BaseInterpreter
|
|||||||
else if (command[1].equals("model"))
|
else if (command[1].equals("model"))
|
||||||
{
|
{
|
||||||
String modelFileName = command[2];
|
String modelFileName = command[2];
|
||||||
QName modelQName = repoAdminService.reloadModel(modelFileName);
|
QName modelQName = repoAdminService.activateModel(modelFileName);
|
||||||
out.println("Model (re-)loaded: " + modelFileName + " [" + modelQName + "]");
|
out.println("Model activated: " + modelFileName + " [" + modelQName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (command[0].equals("deactivate"))
|
||||||
|
{
|
||||||
|
if (command.length != 3)
|
||||||
|
{
|
||||||
|
return "Syntax Error.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (command[1].equals("model"))
|
||||||
|
{
|
||||||
|
String modelFileName = command[2];
|
||||||
|
QName modelQName = repoAdminService.deactivateModel(modelFileName);
|
||||||
|
out.println("Model deactivated: " + modelFileName + " [" + modelQName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (command[0].equals("reload"))
|
||||||
|
{
|
||||||
|
if (command.length != 3)
|
||||||
|
{
|
||||||
|
return "Syntax Error.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (command[1].equals("messages"))
|
else if (command[1].equals("messages"))
|
||||||
{
|
{
|
||||||
String bundleBaseName = command[2];
|
String bundleBaseName = command[2];
|
||||||
repoAdminService.reloadMessageBundle(bundleBaseName);
|
repoAdminService.reloadMessageBundle(bundleBaseName);
|
||||||
out.println("Message resource bundle (re-)loaded: " + bundleBaseName);
|
out.println("Message resource bundle reloaded: " + bundleBaseName);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
@@ -27,6 +27,7 @@ package org.alfresco.repo.admin;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.service.Auditable;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
|
|
||||||
|
|
||||||
@@ -47,11 +48,13 @@ public interface RepoAdminService
|
|||||||
|
|
||||||
public List<RepoModelDefinition> getModels();
|
public List<RepoModelDefinition> getModels();
|
||||||
|
|
||||||
public QName deployModel(InputStream modelStream, String modelFileName);
|
public void deployModel(InputStream modelStream, String modelFileName);
|
||||||
|
|
||||||
public QName undeployModel(String modelFileName);
|
public QName undeployModel(String modelFileName);
|
||||||
|
|
||||||
public QName reloadModel(String modelFileName);
|
public QName activateModel(String modelFileName);
|
||||||
|
|
||||||
|
public QName deactivateModel(String modelFileName);
|
||||||
|
|
||||||
/* Custom message/resource bundles managed in the repository */
|
/* Custom message/resource bundles managed in the repository */
|
||||||
|
|
||||||
|
@@ -24,10 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.repo.admin;
|
package org.alfresco.repo.admin;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -43,12 +40,9 @@ import org.alfresco.repo.dictionary.DictionaryDAO;
|
|||||||
import org.alfresco.repo.dictionary.M2Model;
|
import org.alfresco.repo.dictionary.M2Model;
|
||||||
import org.alfresco.repo.dictionary.RepositoryLocation;
|
import org.alfresco.repo.dictionary.RepositoryLocation;
|
||||||
import org.alfresco.repo.i18n.MessageService;
|
import org.alfresco.repo.i18n.MessageService;
|
||||||
import org.alfresco.repo.workflow.BPMEngineRegistry;
|
|
||||||
import org.alfresco.service.cmr.admin.RepoAdminService;
|
import org.alfresco.service.cmr.admin.RepoAdminService;
|
||||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||||
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
||||||
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
|
|
||||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
|
||||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
@@ -56,11 +50,7 @@ import org.alfresco.service.cmr.repository.ContentWriter;
|
|||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
import org.alfresco.service.cmr.repository.StoreRef;
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
import org.alfresco.service.cmr.search.ResultSet;
|
|
||||||
import org.alfresco.service.cmr.search.SearchService;
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
|
|
||||||
import org.alfresco.service.cmr.workflow.WorkflowService;
|
|
||||||
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
|
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.ParameterCheck;
|
import org.alfresco.util.ParameterCheck;
|
||||||
@@ -88,13 +78,10 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
private ContentService contentService;
|
private ContentService contentService;
|
||||||
private NamespaceService namespaceService;
|
private NamespaceService namespaceService;
|
||||||
private MessageService messageService;
|
private MessageService messageService;
|
||||||
private WorkflowService workflowService;
|
|
||||||
|
|
||||||
private RepositoryLocation repoModelsLocation;
|
private RepositoryLocation repoModelsLocation;
|
||||||
private RepositoryLocation repoMessagesLocation;
|
private RepositoryLocation repoMessagesLocation;
|
||||||
|
|
||||||
private List<String> storeUrls; // stores against which model changes (updates/deletes) should be validated
|
|
||||||
|
|
||||||
public final static String CRITERIA_ALL = "/*"; // immediate children only
|
public final static String CRITERIA_ALL = "/*"; // immediate children only
|
||||||
|
|
||||||
public final static String defaultSubtypeOfDictionaryModel = "subtypeOf('cm:dictionaryModel')";
|
public final static String defaultSubtypeOfDictionaryModel = "subtypeOf('cm:dictionaryModel')";
|
||||||
@@ -131,10 +118,6 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
this.messageService = messageService;
|
this.messageService = messageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWorkflowService(WorkflowService workflowService)
|
|
||||||
{
|
|
||||||
this.workflowService = workflowService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setRepositoryModelsLocation(RepositoryLocation repoModelsLocation)
|
public void setRepositoryModelsLocation(RepositoryLocation repoModelsLocation)
|
||||||
@@ -147,11 +130,6 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
this.repoMessagesLocation = repoMessagesLocation;
|
this.repoMessagesLocation = repoMessagesLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStoreUrls(List<String> storeUrls)
|
|
||||||
{
|
|
||||||
this.storeUrls = storeUrls;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
@@ -162,6 +140,10 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
||||||
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
||||||
|
|
||||||
|
List<RepoModelDefinition> modelsInRepo = new ArrayList<RepoModelDefinition>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
Collection<QName> models = dictionaryDAO.getModels();
|
Collection<QName> models = dictionaryDAO.getModels();
|
||||||
|
|
||||||
List<String> dictionaryModels = new ArrayList<String>();
|
List<String> dictionaryModels = new ArrayList<String>();
|
||||||
@@ -172,8 +154,6 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
|
|
||||||
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+CRITERIA_ALL+"["+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+CRITERIA_ALL+"["+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
||||||
|
|
||||||
List<RepoModelDefinition> modelsInRepo = new ArrayList<RepoModelDefinition>();
|
|
||||||
|
|
||||||
if (nodeRefs.size() > 0)
|
if (nodeRefs.size() > 0)
|
||||||
{
|
{
|
||||||
for (NodeRef nodeRef : nodeRefs)
|
for (NodeRef nodeRef : nodeRefs)
|
||||||
@@ -183,8 +163,7 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
|
|
||||||
String modelName = null;
|
String modelName = null;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ContentReader cr = contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
|
ContentReader cr = contentService.getReader(nodeRef, ContentModel.TYPE_CONTENT);
|
||||||
InputStream is = cr.getContentInputStream();
|
InputStream is = cr.getContentInputStream();
|
||||||
|
|
||||||
@@ -192,11 +171,7 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
modelName = model.getName();
|
modelName = model.getName();
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to getModels " + t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check against models loaded in dictionary and give warning if not found
|
// check against models loaded in dictionary and give warning if not found
|
||||||
if (dictionaryModels.contains(modelName))
|
if (dictionaryModels.contains(modelName))
|
||||||
@@ -210,6 +185,11 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Failed to get models " + t);
|
||||||
|
}
|
||||||
|
|
||||||
return modelsInRepo;
|
return modelsInRepo;
|
||||||
}
|
}
|
||||||
@@ -218,29 +198,14 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.alfresco.service.cmr.admin.RepoAdminService#deployModel(java.io.InputStream, java.lang.String)
|
* @see org.alfresco.service.cmr.admin.RepoAdminService#deployModel(java.io.InputStream, java.lang.String)
|
||||||
*/
|
*/
|
||||||
public QName deployModel(InputStream modelStream, String modelFileName)
|
public void deployModel(InputStream modelStream, String modelFileName)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
// Check that all the passed values are not null
|
// Check that all the passed values are not null
|
||||||
ParameterCheck.mandatory("ModelStream", modelStream);
|
ParameterCheck.mandatory("ModelStream", modelStream);
|
||||||
ParameterCheck.mandatoryString("ModelFileName", modelFileName);
|
ParameterCheck.mandatoryString("ModelFileName", modelFileName);
|
||||||
|
|
||||||
QName modelQName = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// TODO workaround due to issue with model.toXML() - see below
|
|
||||||
BufferedReader in = new BufferedReader(new InputStreamReader(modelStream));
|
|
||||||
StringBuffer buffer = new StringBuffer();
|
|
||||||
String line = null;
|
|
||||||
while ((line = in.readLine()) != null) {
|
|
||||||
buffer.append(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream is = new ByteArrayInputStream(buffer.toString().getBytes());
|
|
||||||
|
|
||||||
M2Model model = M2Model.createModel(is);
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> contentProps = new HashMap<QName, Serializable>();
|
||||||
contentProps.put(ContentModel.PROP_NAME, modelFileName);
|
contentProps.put(ContentModel.PROP_NAME, modelFileName);
|
||||||
|
|
||||||
@@ -259,106 +224,99 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
throw new AlfrescoRuntimeException("Found multiple custom models location " + repoModelsLocation.getPath());
|
throw new AlfrescoRuntimeException("Found multiple custom models location " + repoModelsLocation.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeRef customModelsNodeRef = nodeRefs.get(0);
|
NodeRef customModelsSpaceNodeRef = nodeRefs.get(0);
|
||||||
|
|
||||||
nodeRefs = searchService.selectNodes(customModelsNodeRef, "*[@cm:name='"+modelFileName+"' and "+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
nodeRefs = searchService.selectNodes(customModelsSpaceNodeRef, "*[@cm:name='"+modelFileName+"' and "+defaultSubtypeOfDictionaryModel+"]", null, namespaceService, false);
|
||||||
|
|
||||||
|
NodeRef modelNodeRef = null;
|
||||||
|
|
||||||
if (nodeRefs.size() == 1)
|
if (nodeRefs.size() == 1)
|
||||||
{
|
{
|
||||||
// re-deploy existing model to the repository
|
// re-deploy existing model to the repository
|
||||||
|
|
||||||
NodeRef modelNodeRef = nodeRefs.get(0);
|
modelNodeRef = nodeRefs.get(0);
|
||||||
|
|
||||||
ContentWriter writer = contentService.getWriter(modelNodeRef, ContentModel.PROP_CONTENT, true);
|
|
||||||
|
|
||||||
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
|
|
||||||
writer.setEncoding("UTF-8");
|
|
||||||
|
|
||||||
is = new ByteArrayInputStream(buffer.toString().getBytes());
|
|
||||||
writer.putContent(is); // also invokes policies for DictionaryModelType - e.g. onContentUpdate
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
/* TODO
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
model.toXML(out); // fails with NPE in JIBX - see also: http://issues.alfresco.com/browse/AR-1304
|
|
||||||
writer.putContent(out.toString("UTF-8"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// validate model against dictionary - could be new, unchanged or updated
|
|
||||||
dictionaryDAO.validateModel(model);
|
|
||||||
|
|
||||||
// parse and update model in the dictionary
|
|
||||||
modelQName = dictionaryDAO.putModel(model);
|
|
||||||
|
|
||||||
logger.info("Model re-deployed: " + modelQName);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// deploy new model to the repository
|
// deploy new model to the repository
|
||||||
|
|
||||||
// note: dictionary model type has associated policies that will be invoked
|
// note: dictionary model type has associated policies that will be invoked
|
||||||
ChildAssociationRef association = nodeService.createNode(customModelsNodeRef,
|
ChildAssociationRef association = nodeService.createNode(customModelsSpaceNodeRef,
|
||||||
ContentModel.ASSOC_CONTAINS,
|
ContentModel.ASSOC_CONTAINS,
|
||||||
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, modelFileName),
|
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, modelFileName),
|
||||||
ContentModel.TYPE_DICTIONARY_MODEL,
|
ContentModel.TYPE_DICTIONARY_MODEL,
|
||||||
contentProps); // also invokes policies for DictionaryModelType - e.g. onUpdateProperties
|
contentProps); // also invokes policies for DictionaryModelType - e.g. onUpdateProperties
|
||||||
|
|
||||||
NodeRef content = association.getChildRef();
|
modelNodeRef = association.getChildRef();
|
||||||
|
|
||||||
// add titled aspect (for Web Client display)
|
// add titled aspect (for Web Client display)
|
||||||
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> titledProps = new HashMap<QName, Serializable>();
|
||||||
titledProps.put(ContentModel.PROP_TITLE, modelFileName);
|
titledProps.put(ContentModel.PROP_TITLE, modelFileName);
|
||||||
titledProps.put(ContentModel.PROP_DESCRIPTION, modelFileName);
|
titledProps.put(ContentModel.PROP_DESCRIPTION, modelFileName);
|
||||||
nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps);
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_TITLED, titledProps);
|
||||||
|
|
||||||
// add versionable aspect (set auto-version)
|
// add versionable aspect (set auto-version)
|
||||||
Map<QName, Serializable> versionProps = new HashMap<QName, Serializable>();
|
Map<QName, Serializable> versionProps = new HashMap<QName, Serializable>();
|
||||||
versionProps.put(ContentModel.PROP_AUTO_VERSION, true);
|
versionProps.put(ContentModel.PROP_AUTO_VERSION, true);
|
||||||
nodeService.addAspect(content, ContentModel.ASPECT_VERSIONABLE, versionProps);
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_VERSIONABLE, versionProps);
|
||||||
|
}
|
||||||
|
|
||||||
ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true);
|
ContentWriter writer = contentService.getWriter(modelNodeRef, ContentModel.PROP_CONTENT, true);
|
||||||
|
|
||||||
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
|
writer.setMimetype(MimetypeMap.MIMETYPE_XML);
|
||||||
writer.setEncoding("UTF-8");
|
writer.setEncoding("UTF-8");
|
||||||
|
|
||||||
is = new ByteArrayInputStream(buffer.toString().getBytes());
|
writer.putContent(modelStream); // also invokes policies for DictionaryModelType - e.g. onContentUpdate
|
||||||
writer.putContent(is); // also invokes policies for DictionaryModelType - e.g. onContentUpdate
|
modelStream.close();
|
||||||
is.close();
|
|
||||||
|
|
||||||
/* TODO
|
// activate the model
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
nodeService.setProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE, new Boolean(true));
|
||||||
model.toXML(out); // fails with NPE in JIBX - see also: http://issues.alfresco.com/browse/AR-1304
|
|
||||||
writer.putContent(out.toString("UTF-8"));
|
|
||||||
*/
|
|
||||||
|
|
||||||
// validate model against dictionary - could be new, unchanged or updated
|
// note: model will be loaded as part of DictionaryModelType.beforeCommit()
|
||||||
dictionaryDAO.validateModel(model);
|
|
||||||
|
|
||||||
// parse and add model to dictionary
|
|
||||||
modelQName = dictionaryDAO.putModel(model);
|
|
||||||
|
|
||||||
logger.info("Model deployed: " + modelQName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Model deployment failed ", e);
|
throw new AlfrescoRuntimeException("Model deployment failed ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modelQName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.alfresco.service.cmr.admin.RepoAdminService#reloadModel(java.lang.String)
|
* @see org.alfresco.service.cmr.admin.RepoAdminService#activateModel(java.lang.String)
|
||||||
*/
|
*/
|
||||||
public QName reloadModel(String modelFileName)
|
public QName activateModel(String modelFileName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return activateOrDeactivate(modelFileName, true);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Model activation failed ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see org.alfresco.service.cmr.admin.RepoAdminService#deactivateModel(java.lang.String)
|
||||||
|
*/
|
||||||
|
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
|
// Check that all the passed values are not null
|
||||||
ParameterCheck.mandatoryString("modelFileName", modelFileName);
|
ParameterCheck.mandatoryString("modelFileName", modelFileName);
|
||||||
|
|
||||||
QName modelQName = null;
|
|
||||||
|
|
||||||
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
StoreRef storeRef = repoModelsLocation.getStoreRef();
|
||||||
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
NodeRef rootNode = nodeService.getRootNode(storeRef);
|
||||||
|
|
||||||
@@ -376,31 +334,73 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
|
|
||||||
NodeRef modelNodeRef = nodeRefs.get(0);
|
NodeRef modelNodeRef = nodeRefs.get(0);
|
||||||
|
|
||||||
|
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
|
||||||
|
QName modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
|
||||||
|
|
||||||
|
ModelDefinition modelDef = null;
|
||||||
|
if (modelQName != null)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ContentReader cr = contentService.getReader(modelNodeRef, ContentModel.TYPE_CONTENT);
|
modelDef = dictionaryDAO.getModel(modelQName);
|
||||||
InputStream is = cr.getContentInputStream();
|
}
|
||||||
|
catch (DictionaryException e)
|
||||||
// create model
|
|
||||||
M2Model model = M2Model.createModel(is);
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
if (model != null)
|
|
||||||
{
|
{
|
||||||
// validate model against dictionary - could be new, unchanged or updated
|
logger.warn(e);
|
||||||
dictionaryDAO.validateModel(model);
|
|
||||||
|
|
||||||
// parse and update model in the dictionary
|
|
||||||
modelQName = dictionaryDAO.putModel(model);
|
|
||||||
|
|
||||||
logger.info("Model loaded: " + modelQName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
|
||||||
|
if (activate)
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Model deployment failed", e);
|
// activate
|
||||||
|
if (isActive)
|
||||||
|
{
|
||||||
|
if (modelDef != null)
|
||||||
|
{
|
||||||
|
// model is already activated
|
||||||
|
throw new AlfrescoRuntimeException("Model activation failed - model '" + modelQName + "' is already activated");
|
||||||
|
}
|
||||||
|
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 deactivation failed - model '" + modelQName + "' is already deactivated");
|
||||||
|
}
|
||||||
|
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;
|
return modelQName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,35 +436,35 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
|
|
||||||
NodeRef modelNodeRef = nodeRefs.get(0);
|
NodeRef modelNodeRef = nodeRefs.get(0);
|
||||||
|
|
||||||
String modelName = null;
|
boolean isActive = ((Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
|
||||||
|
modelQName = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
|
||||||
|
|
||||||
|
ModelDefinition modelDef = null;
|
||||||
|
if (modelQName != null)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ContentReader cr = contentService.getReader(modelNodeRef, ContentModel.TYPE_CONTENT);
|
modelDef = dictionaryDAO.getModel(modelQName);
|
||||||
InputStream is = cr.getContentInputStream();
|
|
||||||
|
|
||||||
M2Model model = M2Model.createModel(is);
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
modelName = model.getName();
|
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (DictionaryException e)
|
||||||
{
|
{
|
||||||
throw new AlfrescoRuntimeException("Failed to get model " + t);
|
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
|
// permanently remove model from repository
|
||||||
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
nodeService.addAspect(modelNodeRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||||
nodeService.deleteNode(modelNodeRef);
|
nodeService.deleteNode(modelNodeRef);
|
||||||
|
|
||||||
modelQName = QName.createQName(modelName, namespaceService);
|
// note: deleted model will be unloaded as part of DictionaryModelType.beforeCommit()
|
||||||
|
|
||||||
// simple check for usages
|
|
||||||
validateModelDelete(modelQName);
|
|
||||||
|
|
||||||
dictionaryDAO.removeModel(modelQName);
|
|
||||||
|
|
||||||
logger.info("Model undeployed: " + modelQName);
|
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
@@ -770,74 +770,4 @@ public class RepoAdminServiceImpl implements RepoAdminService
|
|||||||
throw new AlfrescoRuntimeException("Message resource re-load failed", e);
|
throw new AlfrescoRuntimeException("Message resource re-load failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* validate against repository contents / workflows (e.g. when deleting an existing model)
|
|
||||||
*
|
|
||||||
* @param modelName
|
|
||||||
*/
|
|
||||||
private void validateModelDelete(QName modelName)
|
|
||||||
{
|
|
||||||
// TODO add model locking during delete (would need to be tenant-aware & cluster-aware) to avoid potential
|
|
||||||
// for concurrent addition of new content/workflow as model is being deleted
|
|
||||||
|
|
||||||
for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
|
|
||||||
{
|
|
||||||
String workflowDefName = workflowDef.getName();
|
|
||||||
String workflowNamespaceURI = QName.createQName(BPMEngineRegistry.getLocalId(workflowDefName), namespaceService).getNamespaceURI();
|
|
||||||
for (NamespaceDefinition namespace : dictionaryDAO.getNamespaces(modelName))
|
|
||||||
{
|
|
||||||
if (workflowNamespaceURI.equals(namespace.getUri()))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found workflow process definition " + workflowDefName + " using model namespace '" + namespace.getUri() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (TypeDefinition type : dictionaryDAO.getTypes(modelName))
|
|
||||||
{
|
|
||||||
validateClass(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AspectDefinition aspect : dictionaryDAO.getAspects(modelName))
|
|
||||||
{
|
|
||||||
validateClass(aspect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateClass(ClassDefinition classDef)
|
|
||||||
{
|
|
||||||
QName className = classDef.getName();
|
|
||||||
|
|
||||||
String classType = "TYPE";
|
|
||||||
if (classDef instanceof AspectDefinition)
|
|
||||||
{
|
|
||||||
classType = "ASPECT";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String storeUrl : this.storeUrls)
|
|
||||||
{
|
|
||||||
StoreRef store = new StoreRef(storeUrl);
|
|
||||||
|
|
||||||
// search for TYPE or ASPECT - TODO - alternative would be to extract QName and search by namespace ...
|
|
||||||
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_LUCENE, classType+":\""+className+"\"");
|
|
||||||
if (rs.length() > 0)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found " + rs.length() + " nodes in store " + store + " with " + classType + " '" + className + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check against workflow usage
|
|
||||||
for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
|
|
||||||
{
|
|
||||||
for (WorkflowTaskDefinition workflowTaskDef : workflowService.getTaskDefinitions(workflowDef.getId()))
|
|
||||||
{
|
|
||||||
TypeDefinition workflowTypeDef = workflowTaskDef.metadata;
|
|
||||||
if (workflowTypeDef.getName().toString().equals(className))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Failed to validate model delete - found task definition in workflow " + workflowDef.getName() + " with " + classType + " '" + className + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -26,24 +26,43 @@ package org.alfresco.repo.dictionary;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.content.ContentServicePolicies;
|
import org.alfresco.repo.content.ContentServicePolicies;
|
||||||
import org.alfresco.repo.node.NodeServicePolicies;
|
import org.alfresco.repo.node.NodeServicePolicies;
|
||||||
import org.alfresco.repo.policy.JavaBehaviour;
|
import org.alfresco.repo.policy.JavaBehaviour;
|
||||||
import org.alfresco.repo.policy.PolicyComponent;
|
import org.alfresco.repo.policy.PolicyComponent;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.repo.tenant.TenantService;
|
||||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
import org.alfresco.repo.transaction.TransactionListener;
|
import org.alfresco.repo.transaction.TransactionListener;
|
||||||
|
import org.alfresco.repo.workflow.BPMEngineRegistry;
|
||||||
|
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.ClassDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||||
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
import org.alfresco.service.cmr.dictionary.ModelDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.service.cmr.repository.StoreRef;
|
||||||
|
import org.alfresco.service.cmr.search.ResultSet;
|
||||||
|
import org.alfresco.service.cmr.search.SearchService;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowService;
|
||||||
|
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
|
||||||
import org.alfresco.service.namespace.NamespaceService;
|
import org.alfresco.service.namespace.NamespaceService;
|
||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dictionary model type behaviour.
|
* Dictionary model type behaviour.
|
||||||
@@ -54,6 +73,9 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
NodeServicePolicies.OnUpdatePropertiesPolicy,
|
NodeServicePolicies.OnUpdatePropertiesPolicy,
|
||||||
NodeServicePolicies.BeforeDeleteNodePolicy
|
NodeServicePolicies.BeforeDeleteNodePolicy
|
||||||
{
|
{
|
||||||
|
// Logger
|
||||||
|
private static Log logger = LogFactory.getLog(DictionaryModelType.class);
|
||||||
|
|
||||||
/** Key to the pending models */
|
/** Key to the pending models */
|
||||||
private static final String KEY_PENDING_MODELS = "dictionaryModelType.pendingModels";
|
private static final String KEY_PENDING_MODELS = "dictionaryModelType.pendingModels";
|
||||||
|
|
||||||
@@ -72,9 +94,24 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
/** The policy component */
|
/** The policy component */
|
||||||
private PolicyComponent policyComponent;
|
private PolicyComponent policyComponent;
|
||||||
|
|
||||||
|
/** The workflow service */
|
||||||
|
private WorkflowService workflowService;
|
||||||
|
|
||||||
|
/** The search service */
|
||||||
|
private SearchService searchService;
|
||||||
|
|
||||||
|
/** The namespace service */
|
||||||
|
private NamespaceService namespaceService;
|
||||||
|
|
||||||
|
/** The tenant service */
|
||||||
|
private TenantService tenantService;
|
||||||
|
|
||||||
/** Transaction listener */
|
/** Transaction listener */
|
||||||
private DictionaryModelTypeTransactionListener transactionListener;
|
private DictionaryModelTypeTransactionListener transactionListener;
|
||||||
|
|
||||||
|
private List<String> storeUrls; // stores against which model deletes should be validated
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the dictionary DAO
|
* Set the dictionary DAO
|
||||||
*
|
*
|
||||||
@@ -125,6 +162,53 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
this.policyComponent = policyComponent;
|
this.policyComponent = policyComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the workflow service
|
||||||
|
*
|
||||||
|
* @param workflowService the workflow service
|
||||||
|
*/
|
||||||
|
public void setWorkflowService(WorkflowService workflowService)
|
||||||
|
{
|
||||||
|
this.workflowService = workflowService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the search service
|
||||||
|
*
|
||||||
|
* @param searchService the search service
|
||||||
|
*/
|
||||||
|
public void setSearchService(SearchService searchService)
|
||||||
|
{
|
||||||
|
this.searchService = searchService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the namespace service
|
||||||
|
*
|
||||||
|
* @param namespaceService the namespace service
|
||||||
|
*/
|
||||||
|
public void setNamespaceService(NamespaceService namespaceService)
|
||||||
|
{
|
||||||
|
this.namespaceService = namespaceService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tenant service
|
||||||
|
*
|
||||||
|
* @param tenantService the tenant service
|
||||||
|
*/
|
||||||
|
public void setTenantService(TenantService tenantService)
|
||||||
|
{
|
||||||
|
this.tenantService = tenantService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setStoreUrls(List<String> storeUrls)
|
||||||
|
{
|
||||||
|
this.storeUrls = storeUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The initialise method
|
* The initialise method
|
||||||
*/
|
*/
|
||||||
@@ -215,10 +299,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
|
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
|
||||||
if (modelName != null)
|
if (modelName != null)
|
||||||
{
|
{
|
||||||
|
// Validate model delete against usages - content and/or workflows
|
||||||
|
validateModelDelete(modelName);
|
||||||
|
|
||||||
// Remove the model from the dictionary
|
// Remove the model from the dictionary
|
||||||
dictionaryDAO.removeModel(modelName);
|
dictionaryDAO.removeModel(modelName);
|
||||||
|
|
||||||
// TODO how can we make this transactional ??
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,7 +344,14 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
|
|
||||||
if (pendingModels != null)
|
if (pendingModels != null)
|
||||||
{
|
{
|
||||||
for (NodeRef nodeRef : pendingModels)
|
for (final NodeRef nodeRef : pendingModels)
|
||||||
|
{
|
||||||
|
String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier());
|
||||||
|
String tenantAdminUserName = tenantService.getDomainUser(TenantService.ADMIN_BASENAME, tenantDomain);
|
||||||
|
|
||||||
|
AuthenticationUtil.runAs(new RunAsWork<Object>()
|
||||||
|
{
|
||||||
|
public Object doWork()
|
||||||
{
|
{
|
||||||
// Find out whether the model is active (by default it is)
|
// Find out whether the model is active (by default it is)
|
||||||
boolean isActive = false;
|
boolean isActive = false;
|
||||||
@@ -277,27 +369,26 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
// 1. Compile the model and update the details on the node
|
// 1. Compile the model and update the details on the node
|
||||||
// 2. Re-put the model
|
// 2. Re-put the model
|
||||||
|
|
||||||
ContentReader contentReader = this.contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
|
ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
|
||||||
if (contentReader != null)
|
if (contentReader != null)
|
||||||
{
|
{
|
||||||
// Create a model from the current content
|
// Create a model from the current content
|
||||||
M2Model m2Model = M2Model.createModel(contentReader.getContentInputStream());
|
M2Model m2Model = M2Model.createModel(contentReader.getContentInputStream());
|
||||||
// TODO what do we do if we don't have a model??
|
|
||||||
|
|
||||||
// Try and compile the model
|
// Try and compile the model
|
||||||
ModelDefinition modelDefintion = m2Model.compile(dictionaryDAO, namespaceDAO).getModelDefinition();
|
ModelDefinition modelDefintion = m2Model.compile(dictionaryDAO, namespaceDAO).getModelDefinition();
|
||||||
// TODO what do we do if the model does not compile
|
|
||||||
|
|
||||||
// Update the meta data for the model
|
// Update the meta data for the model
|
||||||
Map<QName, Serializable> props = this.nodeService.getProperties(nodeRef);
|
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
|
||||||
props.put(ContentModel.PROP_MODEL_NAME, modelDefintion.getName());
|
props.put(ContentModel.PROP_MODEL_NAME, modelDefintion.getName());
|
||||||
props.put(ContentModel.PROP_MODEL_DESCRIPTION, modelDefintion.getDescription());
|
props.put(ContentModel.PROP_MODEL_DESCRIPTION, modelDefintion.getDescription());
|
||||||
props.put(ContentModel.PROP_MODEL_AUTHOR, modelDefintion.getAuthor());
|
props.put(ContentModel.PROP_MODEL_AUTHOR, modelDefintion.getAuthor());
|
||||||
props.put(ContentModel.PROP_MODEL_PUBLISHED_DATE, modelDefintion.getPublishedDate());
|
props.put(ContentModel.PROP_MODEL_PUBLISHED_DATE, modelDefintion.getPublishedDate());
|
||||||
props.put(ContentModel.PROP_MODEL_VERSION, modelDefintion.getVersion());
|
props.put(ContentModel.PROP_MODEL_VERSION, modelDefintion.getVersion());
|
||||||
this.nodeService.setProperties(nodeRef, props);
|
nodeService.setProperties(nodeRef, props);
|
||||||
|
|
||||||
// TODO how do we get the dependancies for this model ??
|
// Validate model against dictionary - could be new, unchanged or updated
|
||||||
|
dictionaryDAO.validateModel(m2Model);
|
||||||
|
|
||||||
// Put the model
|
// Put the model
|
||||||
dictionaryDAO.putModel(m2Model);
|
dictionaryDAO.putModel(m2Model);
|
||||||
@@ -305,14 +396,21 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
|
QName modelName = (QName)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
|
||||||
if (modelName != null)
|
if (modelName != null)
|
||||||
{
|
{
|
||||||
|
// Validate model delete against usages - content and/or workflows
|
||||||
|
validateModelDelete(modelName);
|
||||||
|
|
||||||
// Remove the model from the dictionary
|
// Remove the model from the dictionary
|
||||||
dictionaryDAO.removeModel(modelName);
|
dictionaryDAO.removeModel(modelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, tenantAdminUserName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,4 +458,87 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* validate against repository contents / workflows (e.g. when deleting an existing model)
|
||||||
|
*
|
||||||
|
* @param modelName
|
||||||
|
*/
|
||||||
|
private void validateModelDelete(QName modelName)
|
||||||
|
{
|
||||||
|
// TODO add model locking during delete (would need to be tenant-aware & cluster-aware) to avoid potential
|
||||||
|
// for concurrent addition of new content/workflow as model is being deleted
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dictionaryDAO.getModel(modelName); // ignore returned model definition
|
||||||
|
}
|
||||||
|
catch (DictionaryException e)
|
||||||
|
{
|
||||||
|
logger.warn("Model ' + modelName + ' does not exist ... skip delete validation : " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check workflow namespace usage
|
||||||
|
for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
|
||||||
|
{
|
||||||
|
String workflowDefName = workflowDef.getName();
|
||||||
|
String workflowNamespaceURI = QName.createQName(BPMEngineRegistry.getLocalId(workflowDefName), namespaceService).getNamespaceURI();
|
||||||
|
for (NamespaceDefinition namespace : dictionaryDAO.getNamespaces(modelName))
|
||||||
|
{
|
||||||
|
if (workflowNamespaceURI.equals(namespace.getUri()))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Failed to validate model delete - found workflow process definition " + workflowDefName + " using model namespace '" + namespace.getUri() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for type usages
|
||||||
|
for (TypeDefinition type : dictionaryDAO.getTypes(modelName))
|
||||||
|
{
|
||||||
|
validateClass(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for aspect usages
|
||||||
|
for (AspectDefinition aspect : dictionaryDAO.getAspects(modelName))
|
||||||
|
{
|
||||||
|
validateClass(aspect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateClass(ClassDefinition classDef)
|
||||||
|
{
|
||||||
|
QName className = classDef.getName();
|
||||||
|
|
||||||
|
String classType = "TYPE";
|
||||||
|
if (classDef instanceof AspectDefinition)
|
||||||
|
{
|
||||||
|
classType = "ASPECT";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String storeUrl : this.storeUrls)
|
||||||
|
{
|
||||||
|
StoreRef store = new StoreRef(storeUrl);
|
||||||
|
|
||||||
|
// search for TYPE or ASPECT - TODO - alternative would be to extract QName and search by namespace ...
|
||||||
|
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_LUCENE, classType+":\""+className+"\"");
|
||||||
|
if (rs.length() > 0)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Failed to validate model delete - found " + rs.length() + " nodes in store " + store + " with " + classType + " '" + className + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check against workflow task usage
|
||||||
|
for (WorkflowDefinition workflowDef : workflowService.getDefinitions())
|
||||||
|
{
|
||||||
|
for (WorkflowTaskDefinition workflowTaskDef : workflowService.getTaskDefinitions(workflowDef.getId()))
|
||||||
|
{
|
||||||
|
TypeDefinition workflowTypeDef = workflowTaskDef.metadata;
|
||||||
|
if (workflowTypeDef.getName().toString().equals(className))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Failed to validate model delete - found task definition in workflow " + workflowDef.getName() + " with " + classType + " '" + className + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.repo.i18n.MessageDeployer;
|
import org.alfresco.repo.i18n.MessageDeployer;
|
||||||
import org.alfresco.repo.i18n.MessageService;
|
import org.alfresco.repo.i18n.MessageService;
|
||||||
@@ -261,7 +262,9 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
|
|||||||
|
|
||||||
for (NodeRef dictionaryModel : nodeRefs)
|
for (NodeRef dictionaryModel : nodeRefs)
|
||||||
{
|
{
|
||||||
// TODO - should validate in case of re-deploy - e.g. update or delete
|
boolean isActive = ((Boolean)nodeService.getProperty(dictionaryModel, ContentModel.PROP_MODEL_ACTIVE)).booleanValue();
|
||||||
|
if (isActive)
|
||||||
|
{
|
||||||
M2Model model = createM2Model(dictionaryModel);
|
M2Model model = createM2Model(dictionaryModel);
|
||||||
if (model != null)
|
if (model != null)
|
||||||
{
|
{
|
||||||
@@ -273,6 +276,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load the models ensuring that they are loaded in the correct order
|
// Load the models ensuring that they are loaded in the correct order
|
||||||
List<String> loadedModels = new ArrayList<String>();
|
List<String> loadedModels = new ArrayList<String>();
|
||||||
@@ -377,9 +381,17 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
|
|||||||
// an error will be raised during compilation
|
// an error will be raised during compilation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
dictionaryDAO.putModel(model);
|
dictionaryDAO.putModel(model);
|
||||||
loadedModels.add(modelName);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -64,7 +64,7 @@ public interface RepoAdminService
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Auditable(parameters = {"modelStream, modelFileName"})
|
@Auditable(parameters = {"modelStream, modelFileName"})
|
||||||
public QName deployModel(InputStream modelStream, String modelFileName);
|
public void deployModel(InputStream modelStream, String modelFileName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undeploy custom model
|
* Undeploy custom model
|
||||||
@@ -78,10 +78,16 @@ public interface RepoAdminService
|
|||||||
public QName undeployModel(String modelFileName);
|
public QName undeployModel(String modelFileName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload custom model
|
* Activate custom model
|
||||||
*/
|
*/
|
||||||
@Auditable(parameters = {"modelFileName"})
|
@Auditable(parameters = {"modelFileName"})
|
||||||
public QName reloadModel(String modelFileName);
|
public QName activateModel(String modelFileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivate custom model
|
||||||
|
*/
|
||||||
|
@Auditable(parameters = {"modelFileName"})
|
||||||
|
public QName deactivateModel(String modelFileName);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Custom Message Management
|
// Custom Message Management
|
||||||
|
Reference in New Issue
Block a user