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,6 +64,7 @@
<property name="namespaceService" ref="namespaceService"/> <property name="namespaceService" ref="namespaceService"/>
<property name="tenantService" ref="tenantService"/> <property name="tenantService" ref="tenantService"/>
<property name="tenantAdminService" ref="tenantAdminService"/> <property name="tenantAdminService" ref="tenantAdminService"/>
<property name="transactionService" ref="TransactionService"/>
<property name="storeUrls"> <property name="storeUrls">
<list> <list>

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.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 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) * Bootstrap Dictionary DAO with pre-defined models & message resources (from classpath)
* *
* @author David Caruana * @author David Caruana, janv
* *
*/ */
public class DictionaryBootstrap implements DictionaryListener public class DictionaryBootstrap implements DictionaryListener
@@ -50,7 +52,7 @@ public class DictionaryBootstrap implements DictionaryListener
private TenantService tenantService; private TenantService tenantService;
// Logger // 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() 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() 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())) if ((tenantService == null) || (! tenantService.isTenantUser()))
{ {
// register models // register models
@@ -132,12 +138,13 @@ public class DictionaryBootstrap implements DictionaryListener
} }
try try
{ {
M2Model model = M2Model.createModel(modelStream);
if (logger.isDebugEnabled()) 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); dictionaryDAO.putModel(model);
} }
catch(DictionaryException e) catch(DictionaryException e)
@@ -145,6 +152,14 @@ public class DictionaryBootstrap implements DictionaryListener
throw new DictionaryException("Could not import bootstrap model " + bootstrapModel, e); 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

@@ -46,7 +46,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
{ {
private DictionaryDAO dictionaryDAO; private DictionaryDAO dictionaryDAO;
// TODO: Check passed arguments are valid // TODO: Check passed arguments are valid
/** /**
@@ -59,7 +58,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
this.dictionaryDAO = dictionaryDAO; this.dictionaryDAO = dictionaryDAO;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllModels() * @see org.alfresco.repo.dictionary.DictionaryService#getAllModels()
*/ */
@@ -68,7 +66,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getModels(); return dictionaryDAO.getModels();
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getModel(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getModel(org.alfresco.repo.ref.QName)
*/ */
@@ -90,7 +87,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return propertyTypes; return propertyTypes;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getPropertyTypes(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getPropertyTypes(org.alfresco.repo.ref.QName)
*/ */
@@ -105,7 +101,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return qnames; return qnames;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllTypes() * @see org.alfresco.repo.dictionary.DictionaryService#getAllTypes()
*/ */
@@ -128,7 +123,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getSubTypes(superType, follow); return dictionaryDAO.getSubTypes(superType, follow);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getTypes(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getTypes(org.alfresco.repo.ref.QName)
*/ */
@@ -143,7 +137,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return qnames; return qnames;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAllAspects() * @see org.alfresco.repo.dictionary.DictionaryService#getAllAspects()
*/ */
@@ -170,7 +163,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return associations; return associations;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getSubAspects(org.alfresco.service.namespace.QName, boolean) * @see org.alfresco.service.cmr.dictionary.DictionaryService#getSubAspects(org.alfresco.service.namespace.QName, boolean)
*/ */
@@ -208,7 +200,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return qnames; return qnames;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#isSubClass(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#isSubClass(org.alfresco.repo.ref.QName, org.alfresco.repo.ref.QName)
*/ */
@@ -248,7 +239,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return subClassOf; return subClassOf;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getPropertyType(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getPropertyType(org.alfresco.repo.ref.QName)
*/ */
@@ -257,7 +247,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getDataType(name); return dictionaryDAO.getDataType(name);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getDataType(java.lang.Class) * @see org.alfresco.service.cmr.dictionary.DictionaryService#getDataType(java.lang.Class)
*/ */
@@ -266,7 +255,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getDataType(javaClass); return dictionaryDAO.getDataType(javaClass);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getType(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getType(org.alfresco.repo.ref.QName)
*/ */
@@ -275,7 +263,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getType(name); return dictionaryDAO.getType(name);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getAspect(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getAspect(org.alfresco.repo.ref.QName)
*/ */
@@ -284,7 +271,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getAspect(name); return dictionaryDAO.getAspect(name);
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getClass(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getClass(org.alfresco.repo.ref.QName)
*/ */
@@ -331,7 +317,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return null; return null;
} }
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.DictionaryService#getProperty(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.DictionaryService#getProperty(org.alfresco.repo.ref.QName)
*/ */
@@ -348,7 +333,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return dictionaryDAO.getAssociation(associationName); return dictionaryDAO.getAssociation(associationName);
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName) * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName)
@@ -363,7 +347,6 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return aspects; return aspects;
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAllProperties(org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName)
@@ -380,7 +363,9 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
} }
/* (non-Javadoc)
* @see org.alfresco.service.cmr.dictionary.DictionaryService#getProperties(org.alfresco.service.namespace.QName)
*/
public Collection<QName> getProperties(QName model) public Collection<QName> getProperties(QName model)
{ {
Collection<PropertyDefinition> propDefs = dictionaryDAO.getProperties(model); Collection<PropertyDefinition> propDefs = dictionaryDAO.getProperties(model);
@@ -392,11 +377,30 @@ public class DictionaryComponent implements DictionaryService, TenantDeployer
return props; 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) public Collection<ConstraintDefinition> getConstraints(QName model)
{ {
return dictionaryDAO.getConstraints(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() public void init()
{ {
dictionaryDAO.init(); dictionaryDAO.init();

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
@@ -138,26 +139,26 @@ public interface DictionaryDAO extends ModelQuery
public Collection<NamespaceDefinition> getNamespaces(QName modelName); 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 * @return the constraints of the model
*/ */
public Collection<ConstraintDefinition> getConstraints(QName model); public Collection<ConstraintDefinition> getConstraints(QName model);
/** /**
* validate against dictionary * @param model the model to retrieve constraint defs (optionally only referenceable constraints)
* * @return the constraints of the model
* 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); 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.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -54,7 +53,7 @@ import org.apache.commons.logging.LogFactory;
/** /**
* Default implementation of the Dictionary. * Default implementation of the Dictionary.
* *
* @author David Caruana * @author David Caruana, janv
* *
*/ */
public class DictionaryDAOImpl implements DictionaryDAO public class DictionaryDAOImpl implements DictionaryDAO
@@ -299,12 +298,12 @@ public class DictionaryDAOImpl implements DictionaryDAO
// Publish new Model Definition // Publish new Model Definition
getCompiledModels(tenantDomain).put(modelName, compiledModel); 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()) for (M2Namespace namespace : model.getNamespaces())
{ {
logger.debug("Registered namespace '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')"); logger.trace("Registered namespace: '" + namespace.getUri() + "' (prefix '" + namespace.getPrefix() + "')");
} }
} }
@@ -941,9 +940,36 @@ public class DictionaryDAOImpl implements DictionaryDAO
* @see org.alfresco.repo.dictionary.DictionaryDAO#getConstraints(org.alfresco.service.namespace.QName) * @see org.alfresco.repo.dictionary.DictionaryDAO#getConstraints(org.alfresco.service.namespace.QName)
*/ */
public Collection<ConstraintDefinition> getConstraints(QName modelName) public Collection<ConstraintDefinition> getConstraints(QName modelName)
{
return getConstraints(modelName, false);
}
public Collection<ConstraintDefinition> getConstraints(QName modelName, boolean referenceableDefsOnly)
{ {
CompiledModel model = getCompiledModel(modelName); 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) // re-entrant (eg. via reset)
@@ -974,9 +1000,9 @@ public class DictionaryDAOImpl implements DictionaryDAO
readLock.unlock(); 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) // 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. * 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 * If the input model does not exist in the Dictionary then no diffs will be returned.
* then no diffs will be returned.
* *
* @param model * @param model
* @return model diffs (if any) * @return model diffs (if any)
*/ */
private List<M2ModelDiff> diffModel(M2Model model) public List<M2ModelDiff> diffModel(M2Model model)
{ {
// Compile model definition // Compile model definition
CompiledModel compiledModel = model.compile(this, namespaceDAO); CompiledModel compiledModel = model.compile(this, namespaceDAO);
@@ -1140,6 +1165,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
{ {
Collection<TypeDefinition> previousTypes = previousVersion.getTypes(); Collection<TypeDefinition> previousTypes = previousVersion.getTypes();
Collection<AspectDefinition> previousAspects = previousVersion.getAspects(); Collection<AspectDefinition> previousAspects = previousVersion.getAspects();
Collection<ConstraintDefinition> previousConDefs = getReferenceableConstraintDefs(previousVersion);
if (model == null) if (model == null)
{ {
@@ -1152,12 +1178,17 @@ public class DictionaryDAOImpl implements DictionaryDAO
{ {
M2ModelDiffs.add(new M2ModelDiff(previousAspect.getName(), M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED)); 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 else
{ {
// update model // update model
Collection<TypeDefinition> types = model.getTypes(); Collection<TypeDefinition> types = model.getTypes();
Collection<AspectDefinition> aspects = model.getAspects(); Collection<AspectDefinition> aspects = model.getAspects();
Collection<ConstraintDefinition> conDefs = getReferenceableConstraintDefs(model);
if (previousTypes.size() != 0) 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)); 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 else
@@ -1211,41 +1254,6 @@ public class DictionaryDAOImpl implements DictionaryDAO
return M2ModelDiffs; 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 /* package */ class DictionaryRegistry
{ {
private Map<String, List<CompiledModel>> uriToModels = new HashMap<String, List<CompiledModel>>(0); private Map<String, List<CompiledModel>> uriToModels = new HashMap<String, List<CompiledModel>>(0);

View File

@@ -19,11 +19,12 @@
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; 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.tenant.TenantService;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListenerAdapter; 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.repo.workflow.BPMEngineRegistry;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; 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.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition; import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
@@ -58,6 +63,7 @@ import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -119,6 +125,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
/** The tenant deployer service */ /** The tenant deployer service */
private TenantAdminService tenantAdminService; private TenantAdminService tenantAdminService;
private TransactionService transactionService;
/** Transaction listener */ /** Transaction listener */
private DictionaryModelTypeTransactionListener transactionListener; private DictionaryModelTypeTransactionListener transactionListener;
@@ -205,6 +213,14 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
this.tenantAdminService = tenantAdminService; this.tenantAdminService = tenantAdminService;
} }
/**
* Set the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setStoreUrls(List<String> storeUrls) public void setStoreUrls(List<String> storeUrls)
{ {
this.storeUrls = storeUrls; this.storeUrls = storeUrls;
@@ -263,19 +279,25 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
*/ */
public void onContentUpdate(NodeRef nodeRef, boolean newContent) public void onContentUpdate(NodeRef nodeRef, boolean newContent)
{ {
if (logger.isTraceEnabled())
{
logger.trace("onContentUpdate: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
queueModel(nodeRef); queueModel(nodeRef);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void queueModel(NodeRef nodeRef) private void queueModel(NodeRef nodeRef)
{ {
Set<NodeRef> pendingModelUpdates = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_MODELS); Set<NodeRef> pendingModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_MODELS);
if (pendingModelUpdates == null) if (pendingModels == null)
{ {
pendingModelUpdates = new HashSet<NodeRef>(); //pendingModels = Collections.newSetFromMap(new ConcurrentHashMap()); // Java 6
AlfrescoTransactionSupport.bindResource(KEY_PENDING_MODELS, pendingModelUpdates); pendingModels = new CopyOnWriteArraySet<NodeRef>();
AlfrescoTransactionSupport.bindResource(KEY_PENDING_MODELS, pendingModels);
} }
pendingModelUpdates.add(tenantService.getName(nodeRef)); pendingModels.add(tenantService.getName(nodeRef));
AlfrescoTransactionSupport.bindListener(this.transactionListener); AlfrescoTransactionSupport.bindListener(this.transactionListener);
} }
@@ -292,6 +314,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
Map<QName, Serializable> before, Map<QName, Serializable> before,
Map<QName, Serializable> after) Map<QName, Serializable> after)
{ {
if (logger.isTraceEnabled())
{
logger.trace("onUpdateProperties: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
Boolean beforeValue = (Boolean)before.get(ContentModel.PROP_MODEL_ACTIVE); Boolean beforeValue = (Boolean)before.get(ContentModel.PROP_MODEL_ACTIVE);
Boolean afterValue = (Boolean)after.get(ContentModel.PROP_MODEL_ACTIVE); Boolean afterValue = (Boolean)after.get(ContentModel.PROP_MODEL_ACTIVE);
@@ -312,11 +339,16 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
public void onRemoveAspect(NodeRef nodeRef, QName aspect) 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 (logger.isTraceEnabled())
if (aspect.equals(ContentModel.ASPECT_WORKING_COPY)) {
{ logger.trace("onRemoveAspect: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
AlfrescoTransactionSupport.bindResource(KEY_WORKING_COPY, nodeRef); }
}
// 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 // restore removes the "archived" aspect prior to restoring (via delete/move) the node - hence need to track here
if (aspect.equals(ContentModel.ASPECT_ARCHIVED)) if (aspect.equals(ContentModel.ASPECT_ARCHIVED))
@@ -328,12 +360,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void beforeDeleteNode(NodeRef nodeRef) public void beforeDeleteNode(NodeRef nodeRef)
{ {
boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY); boolean workingCopy = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY);
NodeRef wcNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_WORKING_COPY); NodeRef wcNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_WORKING_COPY);
if ((wcNodeRef != null) && (wcNodeRef.equals(nodeRef))) if ((wcNodeRef != null) && (wcNodeRef.equals(nodeRef)))
{ {
workingCopy = true; workingCopy = true;
} }
boolean archived = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED); boolean archived = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_ARCHIVED);
NodeRef aNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_ARCHIVED); NodeRef aNodeRef = (NodeRef)AlfrescoTransactionSupport.getResource(KEY_ARCHIVED);
@@ -342,24 +374,28 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
archived = true; archived = true;
} }
// Ignore if the node is a working copy or archived boolean isVersionNode = nodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID);
if (! (workingCopy || archived))
// 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); 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) if (modelName != null)
{ {
// Validate model delete against usages - content and/or workflows // Validate model delete against usages - content and/or workflows
validateModelDelete(modelName); validateModelDelete(modelName);
if (logger.isDebugEnabled())
{
logger.debug("beforeDeleteNode: modelName="+modelName+" ("+nodeRef+")");
}
Set<NodeRef> pendingModelDeletes = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS); Set<NodeRef> pendingModelDeletes = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS);
if (pendingModelDeletes == null) 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); AlfrescoTransactionSupport.bindResource(KEY_PENDING_DELETE_MODELS, pendingModelDeletes);
} }
pendingModelDeletes.add(tenantService.getName(nodeRef)); pendingModelDeletes.add(tenantService.getName(nodeRef));
@@ -367,13 +403,26 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
AlfrescoTransactionSupport.bindListener(this.transactionListener); 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) public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{ {
/*
NodeRef nodeRef = childAssocRef.getChildRef(); 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); Set<NodeRef> pendingDeleteModels = (Set<NodeRef>)AlfrescoTransactionSupport.getResource(KEY_PENDING_DELETE_MODELS);
if (pendingDeleteModels != null) if (pendingDeleteModels != null)
@@ -393,20 +442,32 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
return null; return null;
} }
}, tenantSystemUserName); }, tenantSystemUserName);
if (logger.isTraceEnabled())
{
logger.trace("onDeleteNode: Dictionary destroyed ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
} }
} }
*/
} }
public void onCreateNode(ChildAssociationRef childAssocRef) public void onCreateNode(ChildAssociationRef childAssocRef)
{ {
NodeRef nodeRef = childAssocRef.getChildRef(); NodeRef nodeRef = childAssocRef.getChildRef();
if (logger.isTraceEnabled())
{
logger.trace("onCreateNode: nodeRef="+nodeRef+ " ["+AlfrescoTransactionSupport.getTransactionId()+"]");
}
if (nodeService.getType(nodeRef).equals(ContentModel.TYPE_DICTIONARY_MODEL)) if (nodeService.getType(nodeRef).equals(ContentModel.TYPE_DICTIONARY_MODEL))
{ {
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE); Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
if ((value != null) && (value == true)) if ((value != null) && (value == true))
{ {
queueModel(nodeRef); queueModel(nodeRef);
} }
} }
} }
@@ -429,6 +490,81 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
this.contentService = contentService; 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) * @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean)
*/ */
@@ -440,14 +576,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
if (pendingModels != null) 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) for (NodeRef pendingNodeRef : pendingModels)
{ {
String tenantDomain = tenantService.getDomain(pendingNodeRef.getStoreRef().getIdentifier()); String tenantDomain = tenantService.getDomain(pendingNodeRef.getStoreRef().getIdentifier());
@@ -459,6 +592,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
{ {
public Object doWork() public Object doWork()
{ {
// Ignore if the node no longer exists
if (! nodeService.exists(nodeRef))
{
return null;
}
// Find out whether the model is active // Find out whether the model is active
boolean isActive = false; boolean isActive = false;
Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE); Boolean value = (Boolean)nodeService.getProperty(nodeRef, ContentModel.PROP_MODEL_ACTIVE);
@@ -482,7 +621,8 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
M2Model m2Model = M2Model.createModel(contentReader.getContentInputStream()); M2Model m2Model = M2Model.createModel(contentReader.getContentInputStream());
// Try and compile the model // 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 // Update the meta data for the model
Map<QName, Serializable> props = nodeService.getProperties(nodeRef); 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()); props.put(ContentModel.PROP_MODEL_VERSION, modelDefinition.getVersion());
nodeService.setProperties(nodeRef, props); 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 // Validate model against dictionary - could be new, unchanged or updated
dictionaryDAO.validateModel(m2Model); validateModel(modelDefinition.getName(), m2Model, compiledModel);
// invalidate - to force lazy re-init // invalidate - to force lazy re-init
dictionaryDAO.destroy(); //dictionaryDAO.destroy();
if (logger.isTraceEnabled())
{
logger.trace("beforeCommit: activating nodeRef="+nodeRef+" ("+modelDefinition.getName()+")");
}
} }
} }
else else
@@ -530,7 +654,12 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
validateModelDelete(modelName); validateModelDelete(modelName);
// invalidate - to force lazy re-init // 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) private void validateModelDelete(final QName modelName)
{ {
// TODO add model locking during delete (would need to be tenant-aware & cluster-aware) to avoid potential // 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 try
{ {
@@ -580,8 +709,11 @@ public class DictionaryModelType implements ContentServicePolicies.OnContentUpda
} }
catch (DictionaryException e) catch (DictionaryException e)
{ {
logger.warn("Model ' + modelName + ' does not exist ... skip delete validation : " + e); if (logger.isDebugEnabled())
return; {
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 ? // 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) /**
{ * validate against dictionary
ArrayList<NodeRef> nodeRefs = new ArrayList<NodeRef>(); *
* 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);
ResultSet rs = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, "TYPE:\""+ContentModel.TYPE_DICTIONARY_MODEL+"\""); for (M2ModelDiff modelDiff : modelDiffs)
try {
{ if (modelDiff.getDiffType().equals(M2ModelDiff.DIFF_DELETED))
if (rs.length() > 0) {
{ // TODO - check tenants if model is shared / inherited
for (NodeRef modelNodeRef : rs.getNodeRefs()) if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_PROPERTY))
{ {
QName name = (QName)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_NAME); validatePropertyDelete(modelName, modelDiff.getElementName(), false);
if ((name != null) && (name.equals(modelName)))
{ continue;
nodeRefs.add(modelNodeRef); }
} else if (modelDiff.getElementType().equals(M2ModelDiff.TYPE_CONSTRAINT))
} {
} validateConstraintDelete(compiledModel, modelDiff.getElementName(), false);
} continue;
finally }
{ else
rs.close(); {
} throw new AlfrescoRuntimeException("Failed to validate model update - found deleted " + modelDiff.getElementType() + " '" + modelDiff.getElementName() + "'");
return nodeRefs; }
} }
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; package org.alfresco.repo.dictionary;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.i18n.MessageService; import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.coci.CheckOutCheckInService; 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.DictionaryException;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.BaseAlfrescoSpringTest;
@@ -42,14 +44,17 @@ import org.alfresco.util.PropertyMap;
/** /**
* Dictionary model type unit test * Dictionary model type unit test
* *
* @author Roy Wetherall * @author Roy Wetherall, janv
*/ */
public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
{ {
/** QName of the test model */ /** 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_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 = public static final String MODEL_ONE_XML =
"<model name='test1:testModelOne' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" + "<model name='test1:testModelOne' xmlns='http://www.alfresco.org/model/dictionary/1.0'>" +
@@ -60,6 +65,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <imports>" + " <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" + " <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>" + " </imports>" +
" <namespaces>" + " <namespaces>" +
@@ -71,6 +77,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <type name='test1:base'>" + " <type name='test1:base'>" +
" <title>Base</title>" + " <title>Base</title>" +
" <description>The Base Type</description>" + " <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" + " <properties>" +
" <property name='test1:prop1'>" + " <property name='test1:prop1'>" +
" <type>d:text</type>" + " <type>d:text</type>" +
@@ -92,6 +99,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <imports>" + " <imports>" +
" <import uri='http://www.alfresco.org/model/dictionary/1.0' prefix='d'/>" + " <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>" + " </imports>" +
" <namespaces>" + " <namespaces>" +
@@ -103,11 +111,168 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
" <type name='test1:base'>" + " <type name='test1:base'>" +
" <title>Base</title>" + " <title>Base</title>" +
" <description>The Base Type</description>" + " <description>The Base Type</description>" +
" <parent>cm:content</parent>" +
" <properties>" + " <properties>" +
" <property name='test1:prop1'>" + " <property name='test1:prop1'>" +
" <type>d:text</type>" + " <type>d:text</type>" +
" </property>" + " </property>" +
" <property name='test1:prop2'>" + " <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>" +
" </type>" +
" </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>" + " <type>d:text</type>" +
" </property>" + " </property>" +
" </properties>" + " </properties>" +
@@ -142,6 +307,11 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
TenantAdminService tenantAdminService = (TenantAdminService)this.applicationContext.getBean("tenantAdminService"); TenantAdminService tenantAdminService = (TenantAdminService)this.applicationContext.getBean("tenantAdminService");
MessageService messageService = (MessageService)this.applicationContext.getBean("messageService"); 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(); DictionaryRepositoryBootstrap bootstrap = new DictionaryRepositoryBootstrap();
bootstrap.setContentService(this.contentService); bootstrap.setContentService(this.contentService);
bootstrap.setDictionaryDAO(this.dictionaryDAO); bootstrap.setDictionaryDAO(this.dictionaryDAO);
@@ -189,6 +359,7 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
// Create a model node // Create a model node
PropertyMap properties = new PropertyMap(1); PropertyMap properties = new PropertyMap(1);
properties.put(ContentModel.PROP_MODEL_ACTIVE, true); properties.put(ContentModel.PROP_MODEL_ACTIVE, true);
final NodeRef modelNode = this.nodeService.createNode( final NodeRef modelNode = this.nodeService.createNode(
this.rootNodeRef, this.rootNodeRef,
ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN,
@@ -211,6 +382,10 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
{ {
public NodeRef execute() throws Exception 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 // Check that the meta data has been extracted from the model
assertEquals(QName.createQName("{http://www.alfresco.org/test/testmodel1/1.0}testModelOne"), assertEquals(QName.createQName("{http://www.alfresco.org/test/testmodel1/1.0}testModelOne"),
DictionaryModelTypeTest.this.nodeService.getProperty(modelNode, ContentModel.PROP_MODEL_NAME)); 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 // 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)); 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); DictionaryModelTypeTest.this.cociService.checkin(workingCopy, null);
return null; return null;
} }
@@ -260,6 +435,50 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
} }
}); });
// 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>() transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{ {
public Object execute() throws Exception public Object execute() throws Exception
@@ -268,7 +487,268 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
return null; 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
{
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
} }
public void testIsActiveFlagAndDelete() public void testIsActiveFlagAndDelete()
@@ -371,8 +851,16 @@ public class DictionaryModelTypeTest extends BaseAlfrescoSpringTest
// The model should now be loaded // The model should now be loaded
assertNotNull(DictionaryModelTypeTest.this.dictionaryService.getModel(TEST_MODEL_ONE)); assertNotNull(DictionaryModelTypeTest.this.dictionaryService.getModel(TEST_MODEL_ONE));
// Delete the model
DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode); DictionaryModelTypeTest.this.nodeService.deleteNode(modelNode);
return null;
}
});
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
// The model should not be loaded // The model should not be loaded
try try
{ {

View File

@@ -19,6 +19,7 @@
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -31,6 +32,8 @@ import org.alfresco.repo.i18n.MessageDeployer;
import org.alfresco.repo.i18n.MessageService; import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantDeployer; 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.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
@@ -42,10 +45,11 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent; 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 * 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 public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean implements TenantDeployer, DictionaryListener, MessageDeployer
{ {
// Logging support // Logging support
private static Log logger = LogFactory private static Log logger = LogFactory.getLog(DictionaryRepositoryBootstrap.class);
.getLog("org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap");
/** Locations in the repository from which models should be loaded */ /** Locations in the repository from which models should be loaded */
private List<RepositoryLocation> repositoryModelsLocations = new ArrayList<RepositoryLocation>(); private List<RepositoryLocation> repositoryModelsLocations = new ArrayList<RepositoryLocation>();
@@ -206,9 +209,16 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
*/ */
public void onDictionaryInit() 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) 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 // Register the models found in the repository
@@ -249,7 +259,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
for (M2Namespace namespace : model.getNamespaces()) 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 // Load the models ensuring that they are loaded in the correct order
List<String> loadedModels = new ArrayList<String>(); for (Map.Entry<String, Pair<RepositoryLocation, M2Model>> entry : modelMap.entrySet())
for (Map.Entry<String, 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 loadedModels the list of models already loaded
* @param model the model to try and load * @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(); String modelName = model.getName();
if (loadedModels.contains(modelName) == false) if (loadedModels.contains(modelName) == false)
{ {
for (M2Namespace importNamespace : model.getImports()) for (M2Namespace importNamespace : model.getImports())
{ {
M2Model importedModel = modelMap.get(importNamespace.getUri()); Pair<RepositoryLocation, M2Model> entry = modelMap.get(importNamespace.getUri());
if (importedModel != null) if (entry != null)
{ {
RepositoryLocation importedLocation = entry.getFirst();
M2Model importedModel = entry.getSecond();
// Ensure that the imported model is loaded first // 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 // else we can assume that the imported model is already loaded, if this not the case then
// an error will be raised during compilation // an error will be raised during compilation
@@ -390,6 +422,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
try try
{ {
if (logger.isDebugEnabled())
{
logger.debug("Loading model: " + modelName + " (from ["+ modelLocation.getStoreRef() + "]"+ modelLocation.getPath() + ")");
}
dictionaryDAO.putModel(model); dictionaryDAO.putModel(model);
loadedModels.add(modelName); loadedModels.add(modelName);
} }
@@ -446,10 +483,11 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme
*/ */
public void register() 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 // register with Dictionary Service to allow (re-)init
dictionaryDAO.register(this); dictionaryDAO.register(this);
// register with Message Service to allow (re-)init // register with Message Service to allow (re-)init
messageService.register(this); messageService.register(this);

View File

@@ -997,17 +997,22 @@ public class DiffModelTest extends TestCase
System.out.println(M2ModelDiff.toString()); 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_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED)); assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_UPDATED)); 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_TYPE, M2ModelDiff.DIFF_DELETED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED)); assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED)); assertEquals(2, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED)); assertEquals(0, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_DELETED)); 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() public void testIncUpdatePropertiesAdded()
@@ -1029,10 +1034,12 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED_INC)); 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() public void testIncUpdateTypesAndAspectsAdded()
@@ -1054,13 +1061,15 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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_TYPE, M2ModelDiff.DIFF_CREATED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, 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() public void testIncUpdateAssociationsAdded()
@@ -1082,14 +1091,16 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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_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() public void testIncUpdateTitleDescription()
@@ -1111,10 +1122,11 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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() public void testNonIncUpdatePropertiesRemoved()
@@ -1136,10 +1148,12 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_TYPE, M2ModelDiff.DIFF_UNCHANGED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_UPDATED)); 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() public void testNonIncUpdateTypesAndAspectsRemoved()
@@ -1161,13 +1175,15 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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_TYPE, M2ModelDiff.DIFF_DELETED));
assertEquals(1, countDiffs(modelDiffs, M2ModelDiff.TYPE_ASPECT, 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() public void testNonIncUpdateDefaultAspectAdded()
@@ -1189,10 +1205,11 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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() public void testNonIncUpdateAssociationsRemoved()
@@ -1214,13 +1231,16 @@ public class DiffModelTest extends TestCase
System.out.println(modelDiff.toString()); 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_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) 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) // check all properties (including inherited properties)
Collection<M2ModelDiff> propertyDiffs = M2PropertyDefinition.diffPropertyLists(getProperties().values(), classDef.getProperties().values()); Collection<M2ModelDiff> propertyDiffs = M2PropertyDefinition.diffPropertyLists(getProperties().values(), classDef.getProperties().values());
for (M2ModelDiff propertyDiff : propertyDiffs) modelDiffs.addAll(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;
}
}
// check all associations (including inherited associations, child associations and inherited child associations) // check all associations (including inherited associations, child associations and inherited child associations)
Collection<M2ModelDiff> assocDiffs = M2AssociationDefinition.diffAssocLists(getAssociations().values(), classDef.getAssociations().values()); Collection<M2ModelDiff> assocDiffs = M2AssociationDefinition.diffAssocLists(getAssociations().values(), classDef.getAssociations().values());
for (M2ModelDiff assocDiff : assocDiffs) modelDiffs.addAll(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;
}
}
// check default/mandatory aspects (including inherited default aspects) // 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); Collection<M2ModelDiff> defaultAspectsDiffs = M2ClassDefinition.diffClassLists(new ArrayList<ClassDefinition>(getDefaultAspects()), new ArrayList<ClassDefinition>(classDef.getDefaultAspects()), M2ModelDiff.TYPE_DEFAULT_ASPECT);
@@ -676,13 +650,6 @@ import org.alfresco.util.EqualsHelper;
} }
else 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)); modelDiffs.add(new M2ModelDiff(name, modelDiffType, M2ModelDiff.DIFF_UNCHANGED));
} }

View File

@@ -18,6 +18,8 @@
*/ */
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; 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.cmr.dictionary.ModelDefinition;
import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.InvalidPropertyException;
@@ -39,7 +42,7 @@ import org.springframework.beans.PropertyAccessException;
/** /**
* Compiled Property Constraint * Compiled Property Constraint
* *
* @author Derek Hulley * @author Derek Hulley. janv
*/ */
/* package */class M2ConstraintDefinition implements ConstraintDefinition /* package */class M2ConstraintDefinition implements ConstraintDefinition
{ {
@@ -320,6 +323,17 @@ import org.springframework.beans.PropertyAccessException;
return constraint; 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 * Well-known constraint types
*/ */
@@ -371,4 +385,100 @@ import org.springframework.beans.PropertyAccessException;
*/ */
protected abstract Constraint newInstance(); 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_DEFAULT_ASPECT = "DEFAULT_ASPECT";
public static final String TYPE_PROPERTY = "PROPERTY"; public static final String TYPE_PROPERTY = "PROPERTY";
public static final String TYPE_ASSOCIATION = "ASSOCIATION"; public static final String TYPE_ASSOCIATION = "ASSOCIATION";
public static final String TYPE_CONSTRAINT = "TYPE_CONSTRAINT";
private QName elementName; private QName elementName;
private String elementType; private String elementType;
@@ -57,7 +58,9 @@ public class M2ModelDiff
(!elementType.equals(TYPE_ASPECT)) && (!elementType.equals(TYPE_ASPECT)) &&
(!elementType.equals(TYPE_DEFAULT_ASPECT)) && (!elementType.equals(TYPE_DEFAULT_ASPECT)) &&
(!elementType.equals(TYPE_PROPERTY)) && (!elementType.equals(TYPE_PROPERTY)) &&
(!elementType.equals(TYPE_ASSOCIATION))) (!elementType.equals(TYPE_ASSOCIATION)) &&
(!elementType.equals(TYPE_CONSTRAINT))
)
{ {
throw new AlfrescoRuntimeException("Unknown element type = " + elementType); throw new AlfrescoRuntimeException("Unknown element type = " + elementType);
} }

View File

@@ -94,9 +94,9 @@ public class NamespaceDAOImpl implements NamespaceDAO
removeNamespaceRegistry(tenantDomain); 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"); 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(); dictionaryDAO.init();
NamespaceRegistry namespaceRegistry = getNamespaceRegistry(tenantDomain); NamespaceRegistry namespaceRegistry = getNamespaceRegistry(tenantDomain);
if (logger.isDebugEnabled()) if (logger.isTraceEnabled())
{ {
logger.debug("... resetting namespaces completed"); logger.trace("... resetting namespaces completed");
} }
return namespaceRegistry; return namespaceRegistry;
@@ -176,9 +176,9 @@ public class NamespaceDAOImpl implements NamespaceDAO
getNamespaceRegistry(tenantDomain).setUrisCache(new ArrayList<String>()); getNamespaceRegistry(tenantDomain).setUrisCache(new ArrayList<String>());
getNamespaceRegistry(tenantDomain).setPrefixesCache(new HashMap<String, 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); return getNamespaceRegistryLocal(tenantDomain);

View File

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

View File

@@ -51,4 +51,11 @@ public interface ConstraintDefinition
* @return Returns the constraint implementation * @return Returns the constraint implementation
*/ */
public Constraint getConstraint(); 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; package org.alfresco.service.cmr.dictionary;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.service.NotAuditable; import org.alfresco.service.NotAuditable;
@@ -257,6 +256,17 @@ public interface DictionaryService
@NotAuditable @NotAuditable
Collection<QName> getAllAssociations(); 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 * Get constraints for the specified model
* *
@@ -265,6 +275,19 @@ public interface DictionaryService
*/ */
public Collection<ConstraintDefinition> getConstraints(QName model); 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 // TODO: Behaviour definitions
} }