Merged V3.2 to HEAD

18023: RM: groundwork for custom metadata delete
    18071: MT - fix ETHREEOH-3730 (reject invitation shows "Invitation not found" instead of Yes / No options)
    18078: MT - fix ETHREEOH-3892 (it is not possible to create or manage any Tenants once the DOD5015 RM AMP is installed)
    18903: RM Caveats - fix ALF-1894 (improvement for ESC - remove hardcoded "rmc" model/namespace)
    19046: Improve dictionary debug logging (ALF-587)
    19096: Merging PATCHES/V3.2.r to V3.2
        19075: Merging DEV/BELARUS/V3.2-2010_02_24 to PATCHES/V3.2.r
            18881: ALF-587: MT Upgrades to 3.2r fail unable to find Alfresco content types
        19085: ALF-587 - test/build fix (follow on for r18881->r19075)
    19145: Dynamic Models - follow-on for ALF-587 (& ALFCOM-2977) + additional unit tests
    19176: Remove Java 6'ism
    19198: Build/test fix (follow-on to r19145 - do not validate model delete of version nodes)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19260 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jan Vonka
2010-03-12 17:06:36 +00:00
parent e96af8d097
commit 8f0ad2d96f
17 changed files with 1703 additions and 323 deletions

View File

@@ -64,7 +64,8 @@
<property name="namespaceService" ref="namespaceService"/>
<property name="tenantService" ref="tenantService"/>
<property name="tenantAdminService" ref="tenantAdminService"/>
<property name="transactionService" ref="TransactionService"/>
<property name="storeUrls">
<list>
<value>${spaces.store}</value>

View File

@@ -0,0 +1,430 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.admin;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
/**
* @see RepoAdminServiceImpl
*
* @author janv
*/
public class RepoAdminServiceImplTest extends TestCase
{
private static Log logger = LogFactory.getLog(RepoAdminServiceImplTest.class);
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private RepoAdminService repoAdminService;
private DictionaryService dictionaryService;
private TransactionService transactionService;
final String modelPrefix = "model-";
final static String MKR = "{MKR}";
public static final String MODEL_MKR_XML =
"<model name='test"+MKR+":testModel"+MKR+"' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
" <description>Test model "+MKR+"</description>" +
" <author>Alfresco</author>" +
" <published>2005-05-30</published>" +
" <version>1.0</version>" +
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel"+MKR+"/1.0' prefix='test"+MKR+"'/>" +
" </namespaces>" +
" <types>" +
" <type name='test"+MKR+":base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test"+MKR+":prop1'>" +
" <type>d:text</type>" +
" </property>" +
" </properties>" +
" </type>" +
" </types>" +
"</model>";
@Override
protected void setUp() throws Exception
{
super.setUp();
repoAdminService = (RepoAdminService) ctx.getBean("RepoAdminService");
dictionaryService = (DictionaryService) ctx.getBean("DictionaryService");
transactionService = (TransactionService) ctx.getBean("TransactionService");
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
}
@Override
protected void tearDown() throws Exception
{
super.tearDown();
}
public void testSetup() throws Exception
{
// NOOP
}
//
// Test custom model management
//
public void testSimpleDynamicModel() throws Exception
{
final int X = 0;
final String modelFileName = modelPrefix+X+".xml";
final QName typeName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}base");
final QName modelName = QName.createQName("{http://www.alfresco.org/test/testmodel"+X+"/1.0}testModel"+X);
try
{
if (isModelDeployed(modelFileName))
{
// undeploy model
repoAdminService.undeployModel(modelFileName);
}
assertFalse(isModelDeployed(modelFileName));
assertNull(dictionaryService.getClass(typeName));
final int defaultModelCnt = dictionaryService.getAllModels().size();
// deploy custom model
String model = MODEL_MKR_XML.replace(MKR, X+"");
InputStream modelStream = new ByteArrayInputStream(model.getBytes("UTF-8"));
repoAdminService.deployModel(modelStream, modelFileName);
assertTrue(isModelDeployed(modelFileName));
assertEquals(defaultModelCnt+1, dictionaryService.getAllModels().size());
ClassDefinition myType = dictionaryService.getClass(typeName);
assertNotNull(myType);
assertEquals(modelName, myType.getModel().getName());
// can re-deploy a deployed model (note: re-deploying the same model is a valid incremental update)
modelStream = new ByteArrayInputStream(model.getBytes("UTF-8"));
repoAdminService.deployModel(modelStream, modelFileName);
// deactivate model
repoAdminService.deactivateModel(modelFileName);
assertTrue(isModelDeployed(modelFileName)); // still deployed, although not active
assertEquals(defaultModelCnt, dictionaryService.getAllModels().size());
assertNull(dictionaryService.getClass(typeName));
// try to deactivate an already deactivated model
try
{
repoAdminService.deactivateModel(modelFileName);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Model deactivation failed"));
}
// re-activate model
repoAdminService.activateModel(modelFileName);
assertTrue(isModelDeployed(modelFileName));
assertEquals(defaultModelCnt+1, dictionaryService.getAllModels().size());
myType = dictionaryService.getClass(typeName);
assertNotNull(myType);
assertEquals(modelName, myType.getModel().getName());
// try to activate an already activated model
try
{
repoAdminService.activateModel(modelFileName);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Model activation failed"));
}
// undeploy model
repoAdminService.undeployModel(modelFileName);
assertFalse(isModelDeployed(modelFileName));
assertEquals(defaultModelCnt, dictionaryService.getAllModels().size());
assertNull(dictionaryService.getClass(typeName));
// try to undeploy an already undeployed (or non-existant) model
try
{
repoAdminService.undeployModel(modelFileName);
fail();
}
catch (AlfrescoRuntimeException are)
{
// expected
assertTrue(are.getMessage().contains("Model undeployment failed"));
}
}
finally
{
if (isModelDeployed(modelFileName))
{
// undeploy model
repoAdminService.undeployModel(modelFileName);
}
assertNull(dictionaryService.getClass(typeName));
}
}
private boolean isModelDeployed(String modelFileName)
{
for (RepoModelDefinition modelDef : repoAdminService.getModels())
{
if (modelDef.getRepoName().equals(modelFileName))
{
return true;
}
}
return false;
}
private void undeployModels(int modelCnt)
{
for (int i = 1; i <= modelCnt; i++)
{
if (isModelDeployed(modelPrefix+i))
{
repoAdminService.undeployModel(modelPrefix+i);
}
}
}
private void deployModels(int modelCnt) throws UnsupportedEncodingException
{
for (int i = 1; i <= modelCnt; i++)
{
if (! isModelDeployed(modelPrefix+i))
{
String model = MODEL_MKR_XML.replace(MKR, i+"");
InputStream modelStream = new ByteArrayInputStream(model.getBytes("UTF-8"));
repoAdminService.deployModel(modelStream, modelPrefix+i);
}
}
}
public void testConcurrentDynamicModelCreate() throws Exception
{
final int n = 5;
undeployModels(n);
int deployedModelCount = repoAdminService.getModels().size();
logger.info("Existing deployed custom model count: "+deployedModelCount);
int dictModelCount = getModelCount();
logger.info("Existing dictionary model count: "+dictModelCount);
// concurrently deploy N models
runConcurrentOps(n, 1);
assertEquals(deployedModelCount+n, repoAdminService.getModels().size());
for (int i = 1; i <= n; i++)
{
assertTrue(isModelDeployed(modelPrefix+i));
}
assertEquals(dictModelCount+n, getModelCount());
undeployModels(n);
}
public void testConcurrentDynamicModelDelete() throws Exception
{
final int n = 5;
undeployModels(n);
int deployedModelCount = repoAdminService.getModels().size();
logger.info("Existing deployed custom model count: "+deployedModelCount);
int dictModelCount = getModelCount();
logger.info("Existing dictionary model count: "+dictModelCount);
deployModels(n);
assertEquals(deployedModelCount+n, repoAdminService.getModels().size());
for (int i = 1; i <= n; i++)
{
assertTrue(isModelDeployed(modelPrefix+i));
}
assertEquals(dictModelCount+n, getModelCount());
// concurrently undeploy N models
runConcurrentOps(n, 2);
assertEquals(deployedModelCount, repoAdminService.getModels().size());
for (int i = 1; i <= n; i++)
{
assertFalse(isModelDeployed(modelPrefix+i));
}
assertEquals(dictModelCount, getModelCount());
}
private int getModelCount()
{
return dictionaryService.getAllModels().size();
}
private void runConcurrentOps(int threadCnt, int opType) throws InterruptedException
{
Thread[] threads = new Thread[threadCnt];
Tester[] testers = new Tester[threadCnt];
for (int i = 0; i < threadCnt; i++)
{
Tester tester = new Tester(i+1, opType);
testers[i] = tester;
threads[i] = new Thread(tester);
threads[i].start();
}
for (int i = 0; i < threadCnt; i++)
{
threads[i].join();
if (testers[i].getErrorStackTrace() != null)
{
fail(testers[i].getErrorStackTrace());
}
}
}
private class Tester implements Runnable
{
private int i;
private int opType;
private String errorStackTrace = null;
public Tester(int i, int opType)
{
this.i = i;
this.opType = opType;
}
public String getErrorStackTrace()
{
return errorStackTrace;
}
public void run()
{
try
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
if (opType == 1)
{
// Deploy model
String model = MODEL_MKR_XML.replace(MKR, i+"");
InputStream modelStream = new ByteArrayInputStream(model.getBytes("UTF-8"));
repoAdminService.deployModel(modelStream, modelPrefix+i);
logger.info("["+i+"] Deploying test model: "+modelPrefix+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
else if (opType == 2)
{
// Undeploy model
repoAdminService.undeployModel(modelPrefix+i);
logger.info("["+i+"] Undeployed test model: "+modelPrefix+i+" ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
return null;
}
});
}
catch (Throwable t)
{
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
errorStackTrace = sw.toString();
logger.error("["+i+"] Failed to deploy test model: "+t);
}
}
}
//
// TODO - Test custom message management
//
}

View File

@@ -20,11 +20,13 @@ package org.alfresco.repo.dictionary;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,7 +34,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Bootstrap Dictionary DAO with pre-defined models & message resources (from classpath)
*
* @author David Caruana
* @author David Caruana, janv
*
*/
public class DictionaryBootstrap implements DictionaryListener
@@ -50,7 +52,7 @@ public class DictionaryBootstrap implements DictionaryListener
private TenantService tenantService;
// Logger
private static Log logger = LogFactory.getLog(DictionaryDAO.class);
private static Log logger = LogFactory.getLog(DictionaryBootstrap.class);
/**
@@ -110,8 +112,7 @@ public class DictionaryBootstrap implements DictionaryListener
*/
public void register()
{
dictionaryDAO.destroy(); // deployer - force reload on next get
dictionaryDAO.register(this);
dictionaryDAO.register(this);
}
/*
@@ -120,6 +121,11 @@ public class DictionaryBootstrap implements DictionaryListener
*/
public void onDictionaryInit()
{
long startTime = System.currentTimeMillis();
Collection<QName> modelsBefore = dictionaryDAO.getModels(); // note: on first bootstrap will init empty dictionary
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
if ((tenantService == null) || (! tenantService.isTenantUser()))
{
// register models
@@ -132,12 +138,13 @@ public class DictionaryBootstrap implements DictionaryListener
}
try
{
M2Model model = M2Model.createModel(modelStream);
if (logger.isDebugEnabled())
{
logger.debug("Loading model from " + bootstrapModel);
logger.debug("Loading model: "+model.getName()+" (from "+bootstrapModel+")");
}
M2Model model = M2Model.createModel(modelStream);
dictionaryDAO.putModel(model);
}
catch(DictionaryException e)
@@ -145,6 +152,14 @@ public class DictionaryBootstrap implements DictionaryListener
throw new DictionaryException("Could not import bootstrap model " + bootstrapModel, e);
}
}
Collection<QName> modelsAfter = dictionaryDAO.getModels();
int modelsAfterCnt = (modelsAfter != null ? modelsAfter.size() : 0);
if (logger.isDebugEnabled())
{
logger.debug("Model count: before="+modelsBeforeCnt+", load="+models.size()+", after="+modelsAfterCnt+" in "+(System.currentTimeMillis()-startTime)+" msecs");
}
}
}

View File

@@ -45,8 +45,7 @@ import org.springframework.extensions.surf.util.ParameterCheck;
public class DictionaryComponent implements DictionaryService, TenantDeployer
{
private DictionaryDAO dictionaryDAO;
// TODO: Check passed arguments are valid
/**
@@ -58,7 +57,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
this.dictionaryDAO = dictionaryDAO;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllModels()
@@ -67,7 +65,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getModels();
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getModel(org.alfresco.repo.ref.QName)
@@ -76,7 +73,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getModel(model);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllPropertyTypes()
*/
@@ -89,7 +86,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return propertyTypes;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getPropertyTypes(org.alfresco.repo.ref.QName)
@@ -104,7 +100,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return qnames;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllTypes()
@@ -128,7 +123,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getSubTypes(superType, follow);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getTypes(org.alfresco.repo.ref.QName)
*/
@@ -142,7 +136,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return qnames;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllAspects()
@@ -169,7 +162,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return associations;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getSubAspects(org.alfresco.service.namespace.QName, boolean)
@@ -207,8 +199,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return qnames;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#isSubClass(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName)
*/
@@ -247,7 +238,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return subClassOf;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getPropertyType(org.alfresco.repo.ref.QName)
@@ -256,7 +246,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getDataType(name);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getDataType(java.lang.Class)
@@ -265,8 +254,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getDataType(javaClass);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getType(org.alfresco.repo.ref.QName)
*/
@@ -274,7 +262,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getType(name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAspect(org.alfresco.repo.ref.QName)
@@ -283,7 +270,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getAspect(name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getClass(org.alfresco.repo.ref.QName)
@@ -330,7 +316,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getProperty(org.alfresco.repo.ref.QName)
@@ -339,7 +324,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getProperty(propertyName);
}
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAssociation(org.alfresco.repo.ref.QName)
*/
@@ -347,8 +332,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{
return dictionaryDAO.getAssociation(associationName);
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName)
@@ -362,8 +346,7 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
}
return aspects;
}
/*
* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName)
@@ -379,8 +362,10 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return props;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getProperties(org.alfresco.service.namespace.QName)
*/
public Collection<QName> getProperties(QName model)
{
Collection<PropertyDefinition> propDefs = dictionaryDAO.getProperties(model);
@@ -392,10 +377,29 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return props;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraint(org.alfresco.service.namespace.QName)
*/
public ConstraintDefinition getConstraint(QName constraintQName)
{
return dictionaryDAO.getConstraint(constraintQName);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraints(org.alfresco.service.namespace.QName)
*/
public Collection<ConstraintDefinition> getConstraints(QName model)
{
return dictionaryDAO.getConstraints(model);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getConstraints(org.alfresco.service.namespace.QName, boolean)
*/
public Collection<ConstraintDefinition> getConstraints(QName model, boolean referenceableDefsOnly)
{
return dictionaryDAO.getConstraints(model, referenceableDefsOnly);
}
public void init()
{

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.dictionary;
import java.util.Collection;
import java.util.List;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
@@ -138,26 +139,26 @@ public interface DictionaryDAO extends ModelQuery
public Collection<NamespaceDefinition> getNamespaces(QName modelName);
/**
* @param model the model to retrieve constraints for
* @param model the model to retrieve constraint defs (including property constaint refs)
* @return the constraints of the model
*/
public Collection<ConstraintDefinition> getConstraints(QName model);
/**
* validate against dictionary
*
* if new model
* then nothing to validate
*
* else if an existing model
* then could be updated (or unchanged) so validate to currently only allow incremental updates
* - addition of new types, aspects (except default aspects), properties, associations
* - no deletion of types, aspects or properties or associations
* - no addition, update or deletion of default/mandatory aspects
*
* @param newOrUpdatedModel
* @param model the model to retrieve constraint defs (optionally only referenceable constraints)
* @return the constraints of the model
*/
public void validateModel(M2Model newOrUpdatedModel);
public Collection<ConstraintDefinition> getConstraints(QName model, boolean referenceableDefsOnly);
/**
* Return diffs between input model and model in the Dictionary.
*
* If the input model does not exist in the Dictionary then no diffs will be returned.
*
* @param model
* @return model diffs (if any)
*/
public List<M2ModelDiff> diffModel(M2Model model);
/**
*

View File

@@ -46,7 +46,6 @@ import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.QName;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -54,7 +53,7 @@ import org.apache.commons.logging.LogFactory;
/**
* Default implementation of the Dictionary.
*
* @author David Caruana
* @author David Caruana, janv
*
*/
public class DictionaryDAOImpl implements DictionaryDAO
@@ -190,7 +189,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
DictionaryRegistry dictionaryRegistry = initDictionaryRegistry(tenantDomain);
if (dictionaryRegistry == null)
{
{
// unexpected
throw new AlfrescoRuntimeException("Failed to init dictionaryRegistry " + tenantDomain);
}
@@ -298,13 +297,13 @@ public class DictionaryDAOImpl implements DictionaryDAO
// Publish new Model Definition
getCompiledModels(tenantDomain).put(modelName, compiledModel);
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("Registered model " + modelName.toPrefixString(namespaceDAO));
logger.trace("Registered model: " + modelName.toPrefixString(namespaceDAO));
for (M2Namespace namespace : model.getNamespaces())
{
logger.debug("Registered namespace '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')");
logger.trace("Registered namespace: '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')");
}
}
@@ -942,10 +941,37 @@ public class DictionaryDAOImpl implements DictionaryDAO
*/
public Collection<ConstraintDefinition> getConstraints(QName modelName)
{
return getConstraints(modelName, false);
}
public Collection<ConstraintDefinition> getConstraints(QName modelName, boolean referenceableDefsOnly)
{
CompiledModel model = getCompiledModel(modelName);
return model.getConstraints();
if (referenceableDefsOnly)
{
return getReferenceableConstraintDefs(model);
}
else
{
return model.getConstraints();
}
}
private Collection<ConstraintDefinition> getReferenceableConstraintDefs(CompiledModel model)
{
Collection<ConstraintDefinition> conDefs = model.getConstraints();
Collection<PropertyDefinition> propDefs = model.getProperties();
for (PropertyDefinition propDef : propDefs)
{
for (ConstraintDefinition conDef : propDef.getConstraints())
{
conDefs.remove(conDef);
}
}
return conDefs;
}
// re-entrant (eg. via reset)
private DictionaryRegistry getDictionaryRegistry(String tenantDomain)
{
@@ -974,9 +1000,9 @@ public class DictionaryDAOImpl implements DictionaryDAO
readLock.unlock();
}
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("getDictionaryRegistry: not in cache (or threadlocal) - re-init ["+Thread.currentThread().getId()+", "+AlfrescoTransactionSupport.getTransactionId()+"]"+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
logger.trace("getDictionaryRegistry: not in cache (or threadlocal) - re-init ["+Thread.currentThread().getId()+", "+AlfrescoTransactionSupport.getTransactionId()+"]"+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
// reset caches - may have been invalidated (e.g. in a cluster)
@@ -1088,13 +1114,12 @@ public class DictionaryDAOImpl implements DictionaryDAO
/**
* Return diffs between input model and model in the Dictionary.
*
* If the input model does not exist in the Dictionary or is equivalent to the one in the Dictionary
* then no diffs will be returned.
* If the input model does not exist in the Dictionary then no diffs will be returned.
*
* @param model
* @return model diffs (if any)
*/
private List<M2ModelDiff> diffModel(M2Model model)
public List<M2ModelDiff> diffModel(M2Model model)
{
// Compile model definition
CompiledModel compiledModel = model.compile(this, namespaceDAO);
@@ -1140,7 +1165,8 @@ public class DictionaryDAOImpl implements DictionaryDAO
{
Collection<TypeDefinition> previousTypes = previousVersion.getTypes();
Collection<AspectDefinition> previousAspects = previousVersion.getAspects();
Collection<ConstraintDefinition> previousConDefs = getReferenceableConstraintDefs(previousVersion);
if (model == null)
{
// delete model
@@ -1151,13 +1177,18 @@ public class DictionaryDAOImpl implements DictionaryDAO
for (AspectDefinition previousAspect : previousAspects)
{
M2ModelDiffs.add(new M2ModelDiff(previousAspect.getName(), M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED));
}
}
for (ConstraintDefinition previousConDef : previousConDefs)
{
M2ModelDiffs.add(new M2ModelDiff(previousConDef.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_DELETED));
}
}
else
{
// update model
Collection<TypeDefinition> types = model.getTypes();
Collection<AspectDefinition> aspects = model.getAspects();
Collection<ConstraintDefinition> conDefs = getReferenceableConstraintDefs(model);
if (previousTypes.size() != 0)
{
@@ -1182,6 +1213,18 @@ public class DictionaryDAOImpl implements DictionaryDAO
M2ModelDiffs.add(new M2ModelDiff(aspect.getName(), M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED));
}
}
if (previousConDefs.size() != 0)
{
M2ModelDiffs.addAll(M2ConstraintDefinition.diffConstraintLists(new ArrayList<ConstraintDefinition>(previousConDefs), new ArrayList<ConstraintDefinition>(conDefs)));
}
else
{
for (ConstraintDefinition conDef : conDefs)
{
M2ModelDiffs.add(new M2ModelDiff(conDef.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_CREATED));
}
}
}
}
else
@@ -1211,41 +1254,6 @@ public class DictionaryDAOImpl implements DictionaryDAO
return M2ModelDiffs;
}
/**
* validate against dictionary
*
* if new model
* then nothing to validate
*
* else if an existing model
* then could be updated (or unchanged) so validate to currently only allow incremental updates
* - addition of new types, aspects (except default aspects), properties, associations
* - no deletion of types, aspects or properties or associations
* - no addition, update or deletion of default/mandatory aspects
*
* @param newOrUpdatedModel
*/
public void validateModel(M2Model newOrUpdatedModel)
{
// Check that all the passed values are not null
ParameterCheck.mandatory("newOrUpdatedModel", newOrUpdatedModel);
List<M2ModelDiff> modelDiffs = diffModel(newOrUpdatedModel);
for (M2ModelDiff modelDiff : modelDiffs)
{
if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
{
throw new AlfrescoRuntimeException("Failed to validate model update - found deleted " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
}
if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED))
{
throw new AlfrescoRuntimeException("Failed to validate model update - found non-incrementally updated " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
}
}
}
/* package */ class DictionaryRegistry
{
private Map<String, List<CompiledModel>> uriToModels = new HashMap<String, List<CompiledModel>>(0);

View File

@@ -19,11 +19,12 @@
package org.alfresco.repo.dictionary;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
@@ -38,12 +39,16 @@ import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.version.Version2Model;
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.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
@@ -58,6 +63,7 @@ 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.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -119,6 +125,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
/** The tenant deployer service */
private TenantAdminService tenantAdminService;
private TransactionService transactionService;
/** Transaction listener */
private DictionaryModelTypeTransactionListener transactionListener;
@@ -205,6 +213,14 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
this.tenantAdminService = tenantAdminService;
}
/**
* Set the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setStoreUrls(List<String> storeUrls)
{
this.storeUrls = storeUrls;
@@ -263,19 +279,25 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
*/
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
{
if (logger.isTraceEnabled())
{
logger.trace("onContentUpdate: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
queueModel(nodeRef);
}
@SuppressWarnings("unchecked")
private void queueModel(NodeRef nodeRef)
{
Set<NodeRef> pendingModelUpdates = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_MODELS);
if (pendingModelUpdates == null)
Set<NodeRef> pendingModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_MODELS);
if (pendingModels == null)
{
pendingModelUpdates = new HashSet<NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_PENDING_MODELS, pendingModelUpdates);
//pendingModels = Collections.newSetFromMap(new ConcurrentHashMap()); // Java 6
pendingModels = new CopyOnWriteArraySet<NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_PENDING_MODELS, pendingModels);
}
pendingModelUpdates.add(tenantService.getName(nodeRef));
pendingModels.add(tenantService.getName(nodeRef));
AlfrescoTransactionSupport.bindListener(this.transactionListener);
}
@@ -292,6 +314,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
Map<QName, Serializable> before,
Map<QName, Serializable> after)
{
if (logger.isTraceEnabled())
{
logger.trace("onUpdateProperties: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
Boolean beforeValue = (Boolean)before.get(ContentModel.PROP_MODEL_ACTIVE);
Boolean afterValue = (Boolean)after.get(ContentModel.PROP_MODEL_ACTIVE);
@@ -312,12 +339,17 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
public void onRemoveAspect(NodeRef nodeRef, QName aspect)
{
// undo/cancel checkout removes the "workingcopy" aspect prior to deleting the node - hence need to track here
if (aspect.equals(ContentModel.ASPECT_WORKING_COPY))
{
AlfrescoTransactionSupport.bindResource(KEY_WORKING_COPY, nodeRef);
}
if (logger.isTraceEnabled())
{
logger.trace("onRemoveAspect: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
// undo/cancel checkout removes the "workingcopy" aspect prior to deleting the node - hence need to track here
if (aspect.equals(ContentModel.ASPECT_WORKING_COPY))
{
AlfrescoTransactionSupport.bindResource(KEY_WORKING_COPY, nodeRef);
}
// restore removes the "archived" aspect prior to restoring (via delete/move) the node - hence need to track here
if (aspect.equals(ContentModel.ASPECT_ARCHIVED))
{
@@ -328,13 +360,13 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
@SuppressWarnings("unchecked")
public void beforeDeleteNode(NodeRef nodeRef)
{
boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY);
NodeRef wcNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_WORKING_COPY);
if ((wcNodeRef != null) && (wcNodeRef.equals(nodeRef)))
{
workingCopy = true;
}
boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY);
NodeRef wcNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_WORKING_COPY);
if ((wcNodeRef != null) && (wcNodeRef.equals(nodeRef)))
{
workingCopy = true;
}
boolean archived = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED);
NodeRef aNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_ARCHIVED);
if ((aNodeRef != null) && (aNodeRef.equals(nodeRef)))
@@ -342,24 +374,28 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
archived = true;
}
// Ignore if the node is a working copy or archived
if (! (workingCopy || archived))
boolean isVersionNode = nodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID);
// Ignore if the node is a working copy, archived or version node
if (! (workingCopy || archived || isVersionNode))
{
QName modelName = (QName)this.nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_NAME);
if (logger.isTraceEnabled())
{
logger.trace("beforeDeleteNode: nodeRef="+nodeRef+" validate model delete (modelName="+modelName+")");
}
if (modelName != null)
{
// Validate model delete against usages - content and/or workflows
validateModelDelete(modelName);
if (logger.isDebugEnabled())
{
logger.debug("beforeDeleteNode: modelName="+modelName+" ("+nodeRef+")");
}
Set<NodeRef> pendingModelDeletes = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS);
if (pendingModelDeletes == null)
{
pendingModelDeletes = new HashSet<NodeRef>();
//pendingModelDeletes = Collections.newSetFromMap(new ConcurrentHashMap()); // Java 6
pendingModelDeletes = new CopyOnWriteArraySet<NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_PENDING_DELETE_MODELS, pendingModelDeletes);
}
pendingModelDeletes.add(tenantService.getName(nodeRef));
@@ -367,13 +403,26 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
AlfrescoTransactionSupport.bindListener(this.transactionListener);
}
}
else
{
if (logger.isTraceEnabled())
{
logger.trace("beforeDeleteNode: nodeRef="+nodeRef+ " ignored ("+(workingCopy ? " workingCopy " : "")+(archived ? " archived " : "")+(isVersionNode ? " isVersionNode " : "")+") ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
}
}
@SuppressWarnings("unchecked")
//@SuppressWarnings("unchecked")
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
/*
NodeRef nodeRef = childAssocRef.getChildRef();
if (logger.isTraceEnabled())
{
logger.trace("onDeleteNode: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
Set<NodeRef> pendingDeleteModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS);
if (pendingDeleteModels != null)
@@ -393,20 +442,32 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
return null;
}
}, tenantSystemUserName);
if (logger.isTraceEnabled())
{
logger.trace("onDeleteNode: Dictionary destroyed ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
}
}
*/
}
public void onCreateNode(ChildAssociationRef childAssocRef)
{
NodeRef nodeRef = childAssocRef.getChildRef();
if (logger.isTraceEnabled())
{
logger.trace("onCreateNode: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
if (nodeService.getType(nodeRef).equals(ContentModel.TYPE_DICTIONARY_MODEL))
{
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
if ((value != null) && (value == true))
{
queueModel(nodeRef);
}
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
if ((value != null) && (value == true))
{
queueModel(nodeRef);
}
}
}
@@ -429,6 +490,81 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
this.contentService = contentService;
}
@SuppressWarnings("unchecked")
@Override
public void afterCommit()
{
Set<NodeRef> pendingModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_MODELS);
Set<NodeRef> pendingDeleteModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS);
if (logger.isTraceEnabled())
{
logger.trace("afterCommit: pendingModelsCnt="+(pendingModels != null ? pendingModels.size() : "0")+
", pendingDeleteModelsCnt="+(pendingDeleteModels != null ? pendingDeleteModels.size() : "0"));
}
Set<String> systemTenants = new HashSet<String>(10);
if (pendingModels != null)
{
// unbind the resource from the transaction
AlfrescoTransactionSupport.unbindResource(KEY_PENDING_MODELS);
for (NodeRef nodeRef : pendingModels)
{
String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier());
String tenantSystemUserName = tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);
systemTenants.add(tenantSystemUserName);
}
}
if (pendingDeleteModels != null)
{
// unbind the resource from the transaction
AlfrescoTransactionSupport.unbindResource(KEY_PENDING_DELETE_MODELS);
for (NodeRef nodeRef : pendingDeleteModels)
{
String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier());
String tenantSystemUserName = tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain);
systemTenants.add(tenantSystemUserName);
}
}
if (systemTenants.size() > 0)
{
for (final String tenantSystemUserName : systemTenants)
{
RetryingTransactionCallback<Void> work = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
// invalidate - to force lazy re-init
// note: since afterCommit - need to either clear shared cache or destroy in separate txn
dictionaryDAO.destroy();
if (logger.isTraceEnabled())
{
logger.trace("afterCommit: Dictionary destroyed ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
return null;
}
}, tenantSystemUserName);
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(work, true, true);
}
}
}
/**
* @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean)
*/
@@ -440,14 +576,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
if (pendingModels != null)
{
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("beforeCommit: pendingModelsCnt="+pendingModels.size());
logger.trace("beforeCommit: pendingModelsCnt="+pendingModels.size());
}
// unbind the resource from the transaction
AlfrescoTransactionSupport.unbindResource(KEY_PENDING_MODELS);
for (NodeRef pendingNodeRef : pendingModels)
{
String tenantDomain = tenantService.getDomain(pendingNodeRef.getStoreRef().getIdentifier());
@@ -459,6 +592,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
{
public Object doWork()
{
// Ignore if the node no longer exists
if (! nodeService.exists(nodeRef))
{
return null;
}
// Find out whether the model is active
boolean isActive = false;
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
@@ -472,7 +611,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
{
if (isActive == true)
{
// 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
ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
@@ -482,7 +621,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
M2Model m2Model = M2Model.createModel(contentReader.getContentInputStream());
// Try and compile the model
ModelDefinition modelDefinition = m2Model.compile(dictionaryDAO, namespaceDAO).getModelDefinition();
CompiledModel compiledModel= m2Model.compile(dictionaryDAO, namespaceDAO);
ModelDefinition modelDefinition = compiledModel.getModelDefinition();
// Update the meta data for the model
Map<QName, Serializable> props = nodeService.getProperties(nodeRef);
@@ -493,32 +633,16 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
props.put(ContentModel.PROP_MODEL_VERSION, modelDefinition.getVersion());
nodeService.setProperties(nodeRef, props);
ArrayList<NodeRef> modelNodeRefs = getModelNodes(nodeRef.getStoreRef(), modelDefinition.getName());
for (NodeRef existingNodeRef : modelNodeRefs)
{
if (! existingNodeRef.equals(nodeRef))
{
// check if existing model node is active
Boolean existingValue = (Boolean)nodeService.getProperty(existingNodeRef, ContentModel.PROP_MODEL_ACTIVE);
if ((existingValue != null) && (existingValue.booleanValue() == true))
{
String name = (String)nodeService.getProperty(existingNodeRef, ContentModel.PROP_NAME);
// for MT import, model may have been activated by DictionaryRepositoryBootstrap
if (logger.isDebugEnabled())
{
logger.debug("Re-activating '"+modelDefinition.getName()+"' - existing active model: " + name);
}
//throw new AlfrescoRuntimeException("Cannot activate '"+modelDefinition.getName()+"' - existing active model: " + name);
}
}
}
// Validate model against dictionary - could be new, unchanged or updated
dictionaryDAO.validateModel(m2Model);
// Validate model against dictionary - could be new, unchanged or updated
validateModel(modelDefinition.getName(), m2Model, compiledModel);
// invalidate - to force lazy re-init
dictionaryDAO.destroy();
//dictionaryDAO.destroy();
if (logger.isTraceEnabled())
{
logger.trace("beforeCommit: activating nodeRef="+nodeRef+" ("+modelDefinition.getName()+")");
}
}
}
else
@@ -530,7 +654,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
validateModelDelete(modelName);
// invalidate - to force lazy re-init
dictionaryDAO.destroy();
//dictionaryDAO.destroy();
if (logger.isTraceEnabled())
{
logger.trace("beforeCommit: deactivating nodeRef="+nodeRef+" ("+modelName+")");
}
}
}
}
@@ -572,7 +701,7 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
private void validateModelDelete(final 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 concurrent addition of new content/workflow as model is being deleted
try
{
@@ -580,8 +709,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
}
catch (DictionaryException e)
{
logger.warn("Model ' + modelName + ' does not exist ... skip delete validation : " + e);
return;
if (logger.isDebugEnabled())
{
logger.debug("Dictionary model '" + modelName + "' does not exist ... skip delete validation : " + e);
}
return;
}
// TODO - in case of MT we do not currently allow deletion of an overridden model (with usages) ... but could allow if (re-)inherited model is equivalent to an incremental update only ?
@@ -687,29 +819,162 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
}
}
private ArrayList<NodeRef> getModelNodes(StoreRef storeRef, QName modelName)
{
ArrayList<NodeRef> nodeRefs = new ArrayList<NodeRef>();
ResultSet rs = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, "TYPE:\""+ContentModel.TYPE_DICTIONARY_MODEL+"\"");
try
{
if (rs.length() > 0)
{
for (NodeRef modelNodeRef : rs.getNodeRefs())
{
QName name = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME);
if ((name != null) && (name.equals(modelName)))
{
nodeRefs.add(modelNodeRef);
}
}
}
}
finally
{
rs.close();
}
return nodeRefs;
}
/**
* validate against dictionary
*
* if new model
* then nothing to validate
*
* else if an existing model
* then could be updated (or unchanged) so validate to currently only allow incremental updates
* - addition of new types, aspects (except default aspects), properties, associations
* - no deletion of types, aspects or properties or associations
* - no addition, update or deletion of default/mandatory aspects
*
* @paramn modelName
* @param newOrUpdatedModel
*/
private void validateModel(QName modelName, M2Model model, CompiledModel compiledModel)
{
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(model);
for (M2ModelDiff modelDiff : modelDiffs)
{
if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
{
// TODO - check tenants if model is shared / inherited
if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_PROPERTY))
{
validatePropertyDelete(modelName, modelDiff.getElementName(), false);
continue;
}
else if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_CONSTRAINT))
{
validateConstraintDelete(compiledModel, modelDiff.getElementName(), false);
continue;
}
else
{
throw new AlfrescoRuntimeException("Failed to validate model update - found deleted " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
}
}
if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED))
{
throw new AlfrescoRuntimeException("Failed to validate model update - found non-incrementally updated " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
}
}
// TODO validate that any deleted constraints are not being referenced - else currently will become anon - or push down into model compilation (check backwards compatibility ...)
}
private void validatePropertyDelete(QName modelName, QName propertyName, boolean sharedModel)
{
String tenantDomain = TenantService.DEFAULT_DOMAIN;
if (sharedModel)
{
tenantDomain = " for tenant [" + tenantService.getCurrentUserDomain() + "]";
}
boolean found = false;
// check for property usages
for (PropertyDefinition prop : dictionaryDAO.getProperties(modelName, null))
{
// TODO ... match property
if (prop.getName().equals(propertyName))
{
// found
found = true;
validateIndexedProperty(tenantDomain, prop);
break;
}
}
if (! found)
{
throw new AlfrescoRuntimeException("Failed to validate property delete" + tenantDomain + " - property definition '" + propertyName + "' not defined in model '" + modelName + "'");
}
}
private void validateIndexedProperty(String tenantDomain, PropertyDefinition propDef)
{
QName propName = propDef.getName();
if (! propDef.isIndexed())
{
// TODO ... implement DB-level referential integrity
throw new AlfrescoRuntimeException("Failed to validate property delete" + tenantDomain + " - cannot delete unindexed property definition '" + propName);
}
for (String storeUrl : this.storeUrls)
{
StoreRef store = new StoreRef(storeUrl);
// search for indexed PROPERTY
String escapePropName = propName.toPrefixString().replace(":", "\\:");
ResultSet rs = searchService.query(store, SearchService.LANGUAGE_LUCENE, "@"+escapePropName+":*");
try
{
if (rs.length() > 0)
{
throw new AlfrescoRuntimeException("Failed to validate property delete" + tenantDomain + " - found " + rs.length() + " nodes in store " + store + " with PROPERTY '" + propName + "'" );
}
}
finally
{
rs.close();
}
}
}
// validate delete of a referencable constraint def
private void validateConstraintDelete(CompiledModel compiledModel, QName constraintName, boolean sharedModel)
{
String tenantDomain = TenantService.DEFAULT_DOMAIN;
if (sharedModel)
{
tenantDomain = " for tenant [" + tenantService.getCurrentUserDomain() + "]";
}
Set<QName> referencedBy = new HashSet<QName>(0);
// check for references to constraint definition
// note: could be anon prop constraint (if no referenceable constraint)
Collection<QName> allModels = dictionaryDAO.getModels();
for (QName model : allModels)
{
Collection<PropertyDefinition> propDefs = null;
if (compiledModel.getModelDefinition().getName().equals(model))
{
// TODO deal with multiple pending model updates
propDefs = compiledModel.getProperties();
}
else
{
propDefs = dictionaryDAO.getProperties(model);
}
for (PropertyDefinition propDef : propDefs)
{
for (ConstraintDefinition conDef : propDef.getConstraints())
{
if (constraintName.equals(conDef.getRef()))
{
referencedBy.add(conDef.getName());
}
}
}
}
if (referencedBy.size() == 1)
{
throw new AlfrescoRuntimeException("Failed to validate constraint delete" + tenantDomain + " - constraint definition '" + constraintName + "' is being referenced by '" + referencedBy.toArray()[0] + "' property constraint");
}
else if (referencedBy.size() > 1)
{
throw new AlfrescoRuntimeException("Failed to validate constraint delete" + tenantDomain + " - constraint definition '" + constraintName + "' is being referenced by " + referencedBy.size() + " property constraints");
}
}
}

View File

@@ -19,21 +19,23 @@
package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
@@ -42,14 +44,17 @@ import org.alfresco.util.PropertyMap;
/**
* Dictionary model type unit test
*
* @author Roy Wetherall
* @author Roy Wetherall, janv
*/
public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
{
/** QName of the test model */
private static final QName TEST_MODEL_ONE = QName.createQName("{http://www.alfresco.org/test/testmodel1/1.0}testModelOne");
/** QNames of the test models */
private static final QName TEST_MODEL_ONE = QName.createQName("{http://www.alfresco.org/test/testmodel1/1.0}testModelOne");
private static final QName TEST_MODEL_TWO = QName.createQName("{http://www.alfresco.org/test/testmodel2/1.0}testModelTwo");
/** Test model XMLs */
/** Test model XML */
public static final String MODEL_ONE_XML =
"<model name='test1:testModelOne' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
@@ -60,17 +65,19 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel1/1.0' prefix='test1'/>" +
" </namespaces>" +
" <types>" +
" <type name='test1:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test1:prop1'>" +
" <type>d:text</type>" +
@@ -92,22 +99,61 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel1/1.0' prefix='test1'/>" +
" </namespaces>" +
" <types>" +
" <type name='test1:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test1:prop1'>" +
" <type>d:text</type>" +
" </property>" +
" <property name='test1:prop2'>" +
" <type>d:boolean</type>" +
" </property>" +
" </properties>" +
" </type>" +
" </types>" +
"</model>";
public static final String MODEL_ONE_MODIFIED2_XML =
"<model name='test1:testModelOne' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
" <description>Test model one (updated 2)</description>" +
" <author>Alfresco</author>" +
" <published>2005-05-30</published>" +
" <version>1.2</version>" +
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel1/1.0' prefix='test1'/>" +
" </namespaces>" +
" <types>" +
" <type name='test1:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test1:prop1'>" +
" <type>d:text</type>" +
" </property>" +
" <property name='test1:prop99'>" +
" <type>d:text</type>" +
" </property>" +
" </properties>" +
@@ -116,7 +162,126 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" </types>" +
"</model>";
public static final String MODEL_TWO_XML =
"<model name='test2:testModelTwo' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
" <description>Test model two</description>" +
" <author>Alfresco</author>" +
" <published>2010-01-13</published>" +
" <version>1.0</version>" +
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel2/1.0' prefix='test2'/>" +
" </namespaces>" +
" <constraints>" +
" <constraint name='test2:con1' type='LIST'>" +
" <parameter name='allowedValues'>" +
" <list>" +
" <value>alfresco</value>" +
" <value>file</value>" +
" </list>" +
" </parameter>" +
" </constraint>" +
" </constraints>" +
" <types>" +
" <type name='test2:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test2:prop2'>" +
" <type>d:text</type>" +
" <constraints>" +
" <constraint ref='test2:con1'/>" +
" </constraints>" +
" </property>" +
" </properties>" +
" </type>" +
" </types>" +
"</model>";
public static final String MODEL_TWO_INVALID_XML =
"<model name='test2:testModelTwo' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
" <description>Test model two</description>" +
" <author>Alfresco</author>" +
" <published>2010-01-14</published>" +
" <version>1.1</version>" +
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel2/1.0' prefix='test2'/>" +
" </namespaces>" +
" <types>" +
" <type name='test2:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test2:prop2'>" +
" <type>d:text</type>" +
" <constraints>" +
" <constraint ref='test2:con1'/>" +
" </constraints>" +
" </property>" +
" </properties>" +
" </type>" +
" </types>" +
"</model>";
public static final String MODEL_TWO_MODIFIED_XML =
"<model name='test2:testModelTwo' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
" <description>Test model two - modified</description>" +
" <author>Alfresco - modified</author>" +
" <published>2010-01-14</published>" +
" <version>1.1</version>" +
" <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" +
" <import uri='http://www.alfresco.org/model/content/1.0' prefix='cm'/>" +
" </imports>" +
" <namespaces>" +
" <namespace uri='http://www.alfresco.org/test/testmodel2/1.0' prefix='test2'/>" +
" </namespaces>" +
" <types>" +
" <type name='test2:base'>" +
" <title>Base</title>" +
" <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" +
" <property name='test2:prop2'>" +
" <type>d:text</type>" +
" </property>" +
" </properties>" +
" </type>" +
" </types>" +
"</model>";
/** Services used in tests */
private DictionaryService dictionaryService;
private NamespaceService namespaceService;
@@ -142,6 +307,11 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
TenantAdminService tenantAdminService = (TenantAdminService)this.applicationContext.getBean("tenantAdminService");
MessageService messageService = (MessageService)this.applicationContext.getBean("messageService");
List<String> storeUrlsToValidate = new ArrayList<String>(1);
storeUrlsToValidate.add(this.storeRef.toString());
DictionaryModelType dictionaryModelType = (DictionaryModelType)this.applicationContext.getBean("dictionaryModelType");
dictionaryModelType.setStoreUrls(storeUrlsToValidate);
DictionaryRepositoryBootstrap bootstrap = new DictionaryRepositoryBootstrap();
bootstrap.setContentService(this.contentService);
bootstrap.setDictionaryDAO(this.dictionaryDAO);
@@ -189,6 +359,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
// Create a model node
PropertyMap properties = new PropertyMap(1);
properties.put(ContentModel.PROP_MODEL_ACTIVE, true);
final NodeRef modelNode = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
@@ -211,6 +382,10 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
{
public NodeRef execute() throws Exception
{
// Check that the namespace is in the namespace service
String uri = namespaceService.getNamespaceURI("test1");
assertNotNull(uri);
// Check that the meta data has been extracted from the model
assertEquals(QName.createQName("{http://www.alfresco.org/test/testmodel1/1.0}testModelOne"),
DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_NAME));
@@ -244,7 +419,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
// Check that the policy has not been fired since we have updated a working copy
assertEquals("1.0", DictionaryModelTypeTest.this.nodeService.getProperty(workingCopy, ContentModel.PROP_MODEL_VERSION));
// Now check the model changed back in
// Check-in the model change
DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null;
}
@@ -253,13 +428,319 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
{
// Now check that the model has been updated
assertEquals("1.1", DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_VERSION));
return null;
}
});
// create node using new type
final NodeRef node1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Exception
{
NodeRef node = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("http://www.alfresco.org/model/system/1.0", "node1"),
QName.createQName("http://www.alfresco.org/test/testmodel1/1.0", "base"),
null).getChildRef();
assertNotNull(node);
return node;
}
});
try
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
fail("Unexpected - should not be able to delete model");
}
catch (AlfrescoRuntimeException are)
{
// expected
}
// delete node
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
nodeService.deleteNode(node1);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
}
public void testUpdateDictionaryModelPropertyDelete()
{
try
{
// Check that the model has not yet been loaded into the dictionary
this.dictionaryService.getModel(TEST_MODEL_ONE);
fail("This model has not yet been loaded into the dictionary service");
}
catch (DictionaryException exception)
{
// We expect this exception
}
// Check that the namespace is not yet in the namespace service
String uri = this.namespaceService.getNamespaceURI("test1");
assertNull(uri);
// Create a model node
PropertyMap properties = new PropertyMap(1);
properties.put(ContentModel.PROP_MODEL_ACTIVE, true);
final NodeRef modelNode = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.ALFRESCO_URI, "dictionaryModels"),
ContentModel.TYPE_DICTIONARY_MODEL,
properties).getChildRef();
assertNotNull(modelNode);
// Add the model content to the model node
ContentWriter contentWriter = this.contentService.getWriter(modelNode, ContentModel.PROP_CONTENT, true);
contentWriter.setEncoding("UTF-8");
contentWriter.setMimetype(MimetypeMap.MIMETYPE_XML);
contentWriter.putContent(MODEL_ONE_MODIFIED_XML);
// End the transaction to force update
setComplete();
endTransaction();
// create node using new type
final NodeRef node1 = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Exception
{
// Check that the namespace is in the namespace service
String uri = namespaceService.getNamespaceURI("test1");
assertNotNull(uri);
// Create a model node
PropertyMap properties = new PropertyMap(1);
properties.put(QName.createQName("http://www.alfresco.org/test/testmodel1/1.0", "prop2"), "false"); // boolean
NodeRef node = nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.ALFRESCO_URI, "node1"),
QName.createQName("http://www.alfresco.org/test/testmodel1/1.0", "base"),
properties).getChildRef();
assertNotNull(node);
return node;
}
});
final NodeRef workingCopy = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Exception
{
// Update model
NodeRef workingCopy = DictionaryModelTypeTest.this.cociService.checkout(modelNode);
ContentWriter contentWriter2 = DictionaryModelTypeTest.this.contentService.getWriter(workingCopy, ContentModel.PROP_CONTENT, true);
contentWriter2.putContent(MODEL_ONE_MODIFIED2_XML);
return workingCopy;
}
});
try
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Check that the policy has not been fired since we have updated a working copy
assertEquals("1.1", DictionaryModelTypeTest.this.nodeService.getProperty(workingCopy, ContentModel.PROP_MODEL_VERSION));
// Check-in the model change
DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null;
}
});
fail("Unexpected - should not be able to update model");
}
catch (AlfrescoRuntimeException are)
{
// expected
}
// delete node
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
nodeService.deleteNode(node1);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Check that the policy has not been fired since we have updated a working copy
assertEquals("1.1", DictionaryModelTypeTest.this.nodeService.getProperty(workingCopy, ContentModel.PROP_MODEL_VERSION));
// Check-in the model change
DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Now check that the model has been updated
assertEquals("1.2", DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_VERSION));
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
}
public void testUpdateDictionaryModelConstraintDelete()
{
try
{
// Check that the model has not yet been loaded into the dictionary
this.dictionaryService.getModel(TEST_MODEL_TWO);
fail("This model has not yet been loaded into the dictionary service");
}
catch (DictionaryException exception)
{
// We expect this exception
}
// Check that the namespace is not yet in the namespace service
String uri = this.namespaceService.getNamespaceURI("test2");
assertNull(uri);
// Create a model node
PropertyMap properties = new PropertyMap(1);
properties.put(ContentModel.PROP_MODEL_ACTIVE, true);
final NodeRef modelNode = this.nodeService.createNode(
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.ALFRESCO_URI, "dictionaryModels"),
ContentModel.TYPE_DICTIONARY_MODEL,
properties).getChildRef();
assertNotNull(modelNode);
// Add the model content to the model node
ContentWriter contentWriter = this.contentService.getWriter(modelNode, ContentModel.PROP_CONTENT, true);
contentWriter.setEncoding("UTF-8");
contentWriter.setMimetype(MimetypeMap.MIMETYPE_XML);
contentWriter.putContent(MODEL_TWO_XML);
// End the transaction to force update
setComplete();
endTransaction();
final NodeRef workingCopy = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Exception
{
// Check that the namespace is in the namespace service
String uri = namespaceService.getNamespaceURI("test2");
assertNotNull(uri);
Collection<ConstraintDefinition> constraints = dictionaryService.getConstraints(TEST_MODEL_TWO, true);
assertEquals(1, constraints.size());
assertEquals("test2:con1", constraints.iterator().next().getName().getPrefixString());
// Update model
NodeRef workingCopy = DictionaryModelTypeTest.this.cociService.checkout(modelNode);
ContentWriter contentWriter2 = DictionaryModelTypeTest.this.contentService.getWriter(workingCopy, ContentModel.PROP_CONTENT, true);
contentWriter2.putContent(MODEL_TWO_INVALID_XML);
return workingCopy;
}
});
try
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Check that the policy has not been fired since we have updated a working copy
assertEquals("1.0", DictionaryModelTypeTest.this.nodeService.getProperty(workingCopy, ContentModel.PROP_MODEL_VERSION));
// Check-in the model change
DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null;
}
});
fail("Unexpected - should not be able to update model");
}
catch (AlfrescoRuntimeException are)
{
assertTrue(are.getMessage().contains("Failed to validate constraint delete"));
}
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Check that the policy has not been fired since the previous update was invalid
assertEquals("1.0", DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_VERSION));
// Update model
ContentWriter contentWriter2 = DictionaryModelTypeTest.this.contentService.getWriter(workingCopy, ContentModel.PROP_CONTENT, true);
contentWriter2.putContent(MODEL_TWO_MODIFIED_XML);
// Check-in the model change
DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// Now check that the model has been updated
Collection<ConstraintDefinition> constraints = dictionaryService.getConstraints(TEST_MODEL_TWO, true);
assertEquals(0, constraints.size());
assertEquals("1.1", DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_VERSION));
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
@@ -268,7 +749,6 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
return null;
}
});
}
public void testIsActiveFlagAndDelete()
@@ -371,8 +851,16 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
// The model should now be loaded
assertNotNull(DictionaryModelTypeTest.this.dictionaryService.getModel(TEST_MODEL_ONE));
// Delete the model
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// The model should not be loaded
try
{

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -31,6 +32,8 @@ import org.alfresco.repo.i18n.MessageDeployer;
import org.alfresco.repo.i18n.MessageService;
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.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
@@ -42,10 +45,11 @@ 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.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.extensions.surf.util.Pair;
/**
* Bootstrap the dictionary from specified locations within the repository
@@ -55,8 +59,7 @@ import org.springframework.context.ApplicationEvent;
public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean implements TenantDeployer, DictionaryListener, MessageDeployer
{
// Logging support
private static Log logger = LogFactory
.getLog("org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap");
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>();
@@ -206,9 +209,16 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
*/
public void onDictionaryInit()
{
long startTime = System.currentTimeMillis();
Collection<QName> modelsBefore = dictionaryDAO.getModels();
int modelsBeforeCnt = (modelsBefore != null ? modelsBefore.size() : 0);
List<String> loadedModels = new ArrayList<String>();
if (this.repositoryModelsLocations != null)
{
Map<String, M2Model> modelMap = new HashMap<String, M2Model>();
Map<String, Pair<RepositoryLocation, M2Model>> modelMap = new HashMap<String, Pair<RepositoryLocation, M2Model>>();
// Register the models found in the repository
@@ -249,7 +259,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
for (M2Namespace namespace : model.getNamespaces())
{
modelMap.put(namespace.getUri(), model);
modelMap.put(namespace.getUri(), new Pair<RepositoryLocation, M2Model>(repositoryLocation, model));
}
}
}
@@ -264,10 +274,29 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
}
// Load the models ensuring that they are loaded in the correct order
List<String> loadedModels = new ArrayList<String>();
for (Map.Entry<String, M2Model> entry : modelMap.entrySet())
for (Map.Entry<String, Pair<RepositoryLocation, M2Model>> entry : modelMap.entrySet())
{
loadModel(modelMap, loadedModels, entry.getValue());
RepositoryLocation importedLocation = entry.getValue().getFirst();
M2Model importedModel = entry.getValue().getSecond();
loadModel(modelMap, loadedModels, importedModel, importedLocation);
}
}
Collection<QName> modelsAfter = dictionaryDAO.getModels();
int modelsAfterCnt = (modelsAfter != null ? modelsAfter.size() : 0);
if (modelsAfterCnt != (modelsBeforeCnt + loadedModels.size()))
{
String tenantDomain = tenantAdminService.getCurrentUserDomain();
logger.warn("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" ["+AlfrescoTransactionSupport.getTransactionId()+"] in "+(System.currentTimeMillis()-startTime)+" msecs "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
else
{
if (logger.isDebugEnabled())
{
String tenantDomain = tenantAdminService.getCurrentUserDomain();
logger.debug("Model count: before="+modelsBeforeCnt+", load="+loadedModels.size()+", after="+modelsAfterCnt+" ["+AlfrescoTransactionSupport.getTransactionId()+"] in "+(System.currentTimeMillis()-startTime)+" msecs "+(tenantDomain.equals(TenantService.DEFAULT_DOMAIN) ? "" : " (Tenant: "+tenantDomain+")"));
}
}
}
@@ -371,18 +400,21 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
* @param loadedModels the list of models already loaded
* @param model the model to try and load
*/
private void loadModel(Map<String, M2Model> modelMap, List<String> loadedModels, M2Model model)
private void loadModel(Map<String, Pair<RepositoryLocation, M2Model>> modelMap, List<String> loadedModels, M2Model model, RepositoryLocation modelLocation)
{
String modelName = model.getName();
if (loadedModels.contains(modelName) == false)
{
for (M2Namespace importNamespace : model.getImports())
{
M2Model importedModel = modelMap.get(importNamespace.getUri());
if (importedModel != null)
Pair<RepositoryLocation, M2Model> entry = modelMap.get(importNamespace.getUri());
if (entry != null)
{
RepositoryLocation importedLocation = entry.getFirst();
M2Model importedModel = entry.getSecond();
// Ensure that the imported model is loaded first
loadModel(modelMap, loadedModels, importedModel);
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
@@ -390,6 +422,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
try
{
if (logger.isDebugEnabled())
{
logger.debug("Loading model: " + modelName + " (from ["+ modelLocation.getStoreRef() + "]"+ modelLocation.getPath() + ")");
}
dictionaryDAO.putModel(model);
loadedModels.add(modelName);
}
@@ -398,7 +435,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
// 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);
}
}
}
}
/**
@@ -446,11 +483,12 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
*/
public void register()
{
dictionaryDAO.destroy(); // deployer - force reload on next get
// deployer - force reload on next get (eg. bootstrap "rmc:rmcustom")
dictionaryDAO.destroy();
// register with Dictionary Service to allow (re-)init
dictionaryDAO.register(this);
// register with Dictionary Service to allow (re-)init
dictionaryDAO.register(this);
// register with Message Service to allow (re-)init
messageService.register(this);

View File

@@ -997,30 +997,35 @@ public class DiffModelTest extends TestCase
System.out.println(M2ModelDiff.toString());
}
assertEquals(8, modelDiffs.size());
assertEquals(16, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(0, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_DELETED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(0, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED));
assertEquals(0, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_CREATED));
assertEquals(6, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_DELETED));
}
public void testIncUpdatePropertiesAdded()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL2_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL2_EXTRA_PROPERTIES_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
@@ -1029,10 +1034,12 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString());
}
assertEquals(2, modelDiffs.size());
assertEquals(8, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(4, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_CREATED));
}
public void testIncUpdateTypesAndAspectsAdded()
@@ -1045,7 +1052,7 @@ public class DiffModelTest extends TestCase
byteArrayInputStream = new ByteArrayInputStream(MODEL3_EXTRA_TYPES_AND_ASPECTS_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
@@ -1054,49 +1061,53 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString());
}
assertEquals(4, modelDiffs.size());
assertEquals(8, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED));
assertEquals(4, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
}
public void testIncUpdateAssociationsAdded()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL5_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL5_EXTRA_ASSOCIATIONS_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
for (M2ModelDiff modelDiff : modelDiffs)
{
System.out.println(modelDiff.toString());
}
}
assertEquals(4, modelDiffs.size());
assertEquals(12, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(6, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_CREATED));
}
public void testIncUpdateTitleDescription()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL6_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL6_UPDATE1_XML.getBytes());
@@ -1109,12 +1120,13 @@ public class DiffModelTest extends TestCase
for (M2ModelDiff modelDiff : modelDiffs)
{
System.out.println(modelDiff.toString());
}
}
assertEquals(2, modelDiffs.size());
assertEquals(4, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED_INC));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UPDATED_INC));
}
public void testNonIncUpdatePropertiesRemoved()
@@ -1127,7 +1139,7 @@ public class DiffModelTest extends TestCase
byteArrayInputStream = new ByteArrayInputStream(MODEL2_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
@@ -1136,91 +1148,99 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString());
}
assertEquals(2, modelDiffs.size());
assertEquals(8, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(4, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_DELETED));
}
public void testNonIncUpdateTypesAndAspectsRemoved()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL3_EXTRA_TYPES_AND_ASPECTS_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL3_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
for (M2ModelDiff modelDiff : modelDiffs)
{
System.out.println(modelDiff.toString());
}
}
assertEquals(4, modelDiffs.size());
assertEquals(8, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_DELETED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED));
assertEquals(4, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
}
public void testNonIncUpdateDefaultAspectAdded()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL4_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL4_EXTRA_DEFAULT_ASPECT_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
for (M2ModelDiff modelDiff : modelDiffs)
{
System.out.println(modelDiff.toString());
}
}
assertEquals(2, modelDiffs.size());
assertEquals(4, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
}
public void testNonIncUpdateAssociationsRemoved()
{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(MODEL5_EXTRA_ASSOCIATIONS_XML.getBytes());
M2Model model = M2Model.createModel(byteArrayInputStream);
QName modelName = dictionaryDAO.putModel(model);
QName modelName = dictionaryDAO.putModel(model);
CompiledModel previousVersion = dictionaryDAO.getCompiledModel(modelName);
byteArrayInputStream = new ByteArrayInputStream(MODEL5_XML.getBytes());
model = M2Model.createModel(byteArrayInputStream);
modelName = dictionaryDAO.putModel(model);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
CompiledModel newVersion = dictionaryDAO.getCompiledModel(modelName);
List<M2ModelDiff> modelDiffs = dictionaryDAO.diffModel(previousVersion, newVersion);
for (M2ModelDiff modelDiff : modelDiffs)
{
System.out.println(modelDiff.toString());
}
}
assertEquals(4, modelDiffs.size());
assertEquals(12, modelDiffs.size());
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(6, countDiffs(modelDiffs, M2ModelDiff.TYPE_PROPERTY, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASSOCIATION, M2ModelDiff.DIFF_DELETED));
}
private int countDiffs(List<M2ModelDiff> M2ModelDiffs, String elementType, String diffType)

View File

@@ -570,38 +570,12 @@ import org.alfresco.util.EqualsHelper;
// check all properties (including inherited properties)
Collection<M2ModelDiff> propertyDiffs = M2PropertyDefinition.diffPropertyLists(getProperties().values(), classDef.getProperties().values());
for (M2ModelDiff propertyDiff : propertyDiffs)
{
// note: incremental property updates not supported yet, added for completeness
if (propertyDiff.getDiffType().equals(M2ModelDiff.DIFF_CREATED) || propertyDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED_INC))
{
isUpdatedIncrementally = true;
}
if (propertyDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED) || propertyDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
{
isUpdated = true;
break;
}
}
modelDiffs.addAll(propertyDiffs);
// check all associations (including inherited associations, child associations and inherited child associations)
Collection<M2ModelDiff> assocDiffs = M2AssociationDefinition.diffAssocLists(getAssociations().values(), classDef.getAssociations().values());
for (M2ModelDiff assocDiff : assocDiffs)
{
// note: incremental association updates not supported yet, added for completeness
if (assocDiff.getDiffType().equals(M2ModelDiff.DIFF_CREATED) || assocDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED_INC))
{
isUpdatedIncrementally = true;
}
if (assocDiff.getDiffType().equals(M2ModelDiff.DIFF_UPDATED) || assocDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
{
isUpdated = true;
break;
}
}
modelDiffs.addAll(assocDiffs);
// check default/mandatory aspects (including inherited default aspects)
Collection<M2ModelDiff> defaultAspectsDiffs = M2ClassDefinition.diffClassLists(new ArrayList<ClassDefinition>(getDefaultAspects()), new ArrayList<ClassDefinition>(classDef.getDefaultAspects()), M2ModelDiff.TYPE_DEFAULT_ASPECT);
@@ -676,16 +650,9 @@ import org.alfresco.util.EqualsHelper;
}
else
{
for (M2ModelDiff modelDiff : modelDiffs)
{
if (! modelDiff.getDiffType().equals(M2ModelDiff.DIFF_UNCHANGED))
{
throw new DictionaryException("Unexpected: diff found although '" + name + "' not marked as updated");
}
}
modelDiffs.add(new M2ModelDiff(name, modelDiffType, M2ModelDiff.DIFF_UNCHANGED));
}
return modelDiffs;
}

View File

@@ -18,6 +18,8 @@
*/
package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint;
@@ -31,6 +33,7 @@ import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.InvalidPropertyException;
@@ -39,7 +42,7 @@ import org.springframework.beans.PropertyAccessException;
/**
* Compiled Property Constraint
*
* @author Derek Hulley
* @author Derek Hulley. janv
*/
/* package */class M2ConstraintDefinition implements ConstraintDefinition
{
@@ -319,7 +322,18 @@ import org.springframework.beans.PropertyAccessException;
{
return constraint;
}
public QName getRef()
{
QName refQName = null;
String ref = m2Constraint.getRef();
if (ref != null)
{
refQName = QName.createQName(ref, prefixResolver);
}
return refQName;
}
/**
* Well-known constraint types
*/
@@ -365,10 +379,106 @@ import org.springframework.beans.PropertyAccessException;
return new ListOfValuesConstraint();
}
};
/**
* @return Returns the constraint implementation
*/
protected abstract Constraint newInstance();
}
/* package */ M2ModelDiff diffConstraint(ConstraintDefinition conDef)
{
M2ModelDiff modelDiff = null;
boolean isUpdated = false;
boolean isUpdatedIncrementally = false;
if (this == conDef)
{
modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UNCHANGED);
return modelDiff;
}
// check name - cannot be null
if (! name.equals(conDef.getName()))
{
isUpdated = true;
}
// check title
if (! EqualsHelper.nullSafeEquals(getTitle(), conDef.getTitle(), false))
{
isUpdatedIncrementally = true;
}
// check description
if (! EqualsHelper.nullSafeEquals(getDescription(), conDef.getDescription(), false))
{
isUpdatedIncrementally = true;
}
// check type string
if (! EqualsHelper.nullSafeEquals(getConstraint().getType(), conDef.getConstraint().getType()))
{
isUpdated = true;
}
if (isUpdated)
{
modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UPDATED);
}
else if (isUpdatedIncrementally)
{
modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UPDATED_INC);
}
else
{
modelDiff = new M2ModelDiff(name, M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_UNCHANGED);
}
return modelDiff;
}
/*package*/ static Collection<M2ModelDiff> diffConstraintLists(Collection<ConstraintDefinition> previousConstraints, Collection<ConstraintDefinition> newConstraints)
{
List<M2ModelDiff> modelDiffs = new ArrayList<M2ModelDiff>();
for (ConstraintDefinition previousConstraint : previousConstraints)
{
boolean found = false;
for (ConstraintDefinition newConstraint : newConstraints)
{
if (newConstraint.getName().equals(previousConstraint.getName()))
{
modelDiffs.add(((M2ConstraintDefinition)previousConstraint).diffConstraint(previousConstraint));
found = true;
break;
}
}
if (! found)
{
modelDiffs.add(new M2ModelDiff(previousConstraint.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_DELETED));
}
}
for (ConstraintDefinition newConstraint : newConstraints)
{
boolean found = false;
for (ConstraintDefinition previousConstraint : previousConstraints)
{
if (newConstraint.getName().equals(previousConstraint.getName()))
{
found = true;
break;
}
}
if (! found)
{
modelDiffs.add(new M2ModelDiff(newConstraint.getName(), M2ModelDiff.TYPE_CONSTRAINT, M2ModelDiff.DIFF_CREATED));
}
}
return modelDiffs;
}
}

View File

@@ -41,6 +41,7 @@ public class M2ModelDiff
public static final String TYPE_DEFAULT_ASPECT = "DEFAULT_ASPECT";
public static final String TYPE_PROPERTY = "PROPERTY";
public static final String TYPE_ASSOCIATION = "ASSOCIATION";
public static final String TYPE_CONSTRAINT = "TYPE_CONSTRAINT";
private QName elementName;
private String elementType;
@@ -53,11 +54,13 @@ public class M2ModelDiff
ParameterCheck.mandatoryString("elementType", elementType);
ParameterCheck.mandatoryString("diffType", diffType);
if ((!elementType.equals(TYPE_TYPE)) &&
(!elementType.equals(TYPE_ASPECT)) &&
(!elementType.equals(TYPE_DEFAULT_ASPECT)) &&
if ((!elementType.equals(TYPE_TYPE)) &&
(!elementType.equals(TYPE_ASPECT)) &&
(!elementType.equals(TYPE_DEFAULT_ASPECT)) &&
(!elementType.equals(TYPE_PROPERTY)) &&
(!elementType.equals(TYPE_ASSOCIATION)))
(!elementType.equals(TYPE_ASSOCIATION)) &&
(!elementType.equals(TYPE_CONSTRAINT))
)
{
throw new AlfrescoRuntimeException("Unknown element type = " + elementType);
}

View File

@@ -94,9 +94,9 @@ public class NamespaceDAOImpl implements NamespaceDAO
removeNamespaceRegistry(tenantDomain);
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("Namespaces destroyed");
logger.trace("Namespaces destroyed");
}
}
@@ -111,18 +111,18 @@ public class NamespaceDAOImpl implements NamespaceDAO
throw new AlfrescoRuntimeException("Dictionary should be registered in order to perform reset");
}
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("Resetting namespaces ...");
logger.trace("Resetting namespaces ...");
}
dictionaryDAO.init();
NamespaceRegistry namespaceRegistry = getNamespaceRegistry(tenantDomain);
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("... resetting namespaces completed");
logger.trace("... resetting namespaces completed");
}
return namespaceRegistry;
@@ -176,9 +176,9 @@ public class NamespaceDAOImpl implements NamespaceDAO
getNamespaceRegistry(tenantDomain).setUrisCache(new ArrayList<String>());
getNamespaceRegistry(tenantDomain).setPrefixesCache(new HashMap<String, String>());
if (logger.isDebugEnabled())
if (logger.isTraceEnabled())
{
logger.debug("Empty namespaces initialised");
logger.trace("Empty namespaces initialised");
}
return getNamespaceRegistryLocal(tenantDomain);

View File

@@ -211,7 +211,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest
bootstrap.setModels(bootstrapModels);
bootstrap.setDictionaryDAO(dictionaryDAO);
bootstrap.register();
bootstrap.bootstrap();
}
/**

View File

@@ -51,4 +51,11 @@ public interface ConstraintDefinition
* @return Returns the constraint implementation
*/
public Constraint getConstraint();
/**
* @return Returns the referenced constraint definition, if any (null for explicit or inline constraint def)
*
* @since 3.2R
*/
public QName getRef();
}

View File

@@ -19,7 +19,6 @@
package org.alfresco.service.cmr.dictionary;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.alfresco.service.NotAuditable;
@@ -258,6 +257,17 @@ public interface DictionaryService
Collection<QName> getAllAssociations();
/**
* Gets the definition of the constraint
*
* @param constraintName the constraint name
* @return the constraint definition (or null, if it doesn't exist)
*
* @since 3.2.1
*/
@NotAuditable
public ConstraintDefinition getConstraint(QName constraintQName);
/**
* Get constraints for the specified model
*
* @param model
@@ -265,6 +275,19 @@ public interface DictionaryService
*/
public Collection<ConstraintDefinition> getConstraints(QName model);
/**
* Get constraints for the specified model
* Optionally return referenceable (ie. non-property specific) constraints only
*
* @param model
* @param referenceableDefsOnly
* @return
*
* @since 3.2R
*/
Collection<ConstraintDefinition> getConstraints(QName model, boolean referenceableDefsOnly);
// TODO: Behaviour definitions
}