mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
MOB-29: Content Storage Lifecycle Management
- Some collapsing of common code in Constraint hierarchy. - Added aspect 'cm:storeSelector' with property 'cm:storeName'. - Defined a new, unused content store 'storeSelectorContentStore' (see content-services-context.xml). If this store is used, then setting the 'cm:storeName' property will force content to be stored in one of the named stores. A LIST constraint is indirectly enforced for the 'cm:storeName' property. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14329 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -267,6 +267,10 @@ public interface ContentModel
|
||||
static final QName ASPECT_THUMBNAILED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnailed");
|
||||
static final QName ASSOC_THUMBNAILS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "thumbnails");
|
||||
|
||||
// StoreSelector Aspect
|
||||
static final QName ASPECT_STORE_SELECTOR = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "storeSelector");
|
||||
static final QName PROP_STORE_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "storeName");
|
||||
|
||||
// Preference Aspect
|
||||
static final QName ASPECT_PREFERENCES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "preferences");
|
||||
static final QName PROP_PREFERENCE_VALUES = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "preferenceValues");
|
||||
|
@@ -1302,10 +1302,15 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi
|
||||
// TODO Not sure this try block is necessary.
|
||||
try
|
||||
{
|
||||
// Invoke policy behaviors.
|
||||
// invokeBeforeUpdateNode(nodeRef);
|
||||
// Map<QName, Serializable> oldProps = getProperties(nodeRef);
|
||||
// Prepare fr policy invocation.
|
||||
Map<QName, Serializable> propsBefore = null;
|
||||
if (fInvokePolicies)
|
||||
{
|
||||
propsBefore = getProperties(nodeRef);
|
||||
}
|
||||
// Remove all properties
|
||||
fAVMService.deleteNodeProperties(avmVersionPath.getSecond());
|
||||
// Rebuild node properties
|
||||
Map<QName, PropertyValue> values = new HashMap<QName, PropertyValue>();
|
||||
for (Map.Entry<QName, Serializable> entry : properties.entrySet())
|
||||
{
|
||||
@@ -1332,7 +1337,14 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi
|
||||
PropertyValue propertyValue = makePropertyValue(propertyDef, value);
|
||||
values.put(propertyQName, propertyValue);
|
||||
}
|
||||
// Finally set node properties
|
||||
fAVMService.setNodeProperties(avmVersionPath.getSecond(), values);
|
||||
// Invoke policies
|
||||
if (fInvokePolicies)
|
||||
{
|
||||
Map<QName, Serializable> propsAfter = properties;
|
||||
invokeOnUpdateProperties(nodeRef, propsBefore, propsAfter);
|
||||
}
|
||||
// Invoke policy behaviors.
|
||||
// invokeOnUpdateNode(nodeRef);
|
||||
// invokeOnUpdateProperties(nodeRef, oldProps, properties);
|
||||
@@ -1413,13 +1425,6 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi
|
||||
try
|
||||
{
|
||||
fAVMService.setContentData(avmVersionPath.getSecond(), (ContentData)value);
|
||||
if (fInvokePolicies)
|
||||
{
|
||||
Map<QName, Serializable> propsBefore = new HashMap<QName, Serializable>();
|
||||
Map<QName, Serializable> propsAfter = new HashMap<QName, Serializable>();
|
||||
propsAfter.put(ContentModel.PROP_CONTENT, value);
|
||||
invokeOnUpdateProperties(nodeRef, propsBefore, propsAfter);
|
||||
}
|
||||
}
|
||||
catch (ClassCastException e)
|
||||
{
|
||||
@@ -1430,10 +1435,20 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi
|
||||
}
|
||||
try
|
||||
{
|
||||
// Map<QName, Serializable> propsBefore = getProperties(nodeRef);
|
||||
Map<QName, Serializable> propsBefore = null;
|
||||
if (fInvokePolicies)
|
||||
{
|
||||
propsBefore = getProperties(nodeRef);
|
||||
}
|
||||
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
|
||||
PropertyValue propertyValue = makePropertyValue(propertyDef, value);
|
||||
fAVMService.setNodeProperty(avmVersionPath.getSecond(), qname, propertyValue);
|
||||
if (fInvokePolicies)
|
||||
{
|
||||
Map<QName, Serializable> propsAfter = new HashMap<QName, Serializable>(propsBefore);
|
||||
propsAfter.put(qname, value);
|
||||
invokeOnUpdateProperties(nodeRef, propsBefore, propsAfter);
|
||||
}
|
||||
// Map<QName, Serializable> propsAfter = getProperties(nodeRef);
|
||||
// Invoke policy behaviors.
|
||||
// invokeOnUpdateNode(nodeRef);
|
||||
|
@@ -37,6 +37,7 @@ import org.alfresco.repo.content.metadata.OpenOfficeMetadataExtracterTest;
|
||||
import org.alfresco.repo.content.metadata.PdfBoxMetadataExtracterTest;
|
||||
import org.alfresco.repo.content.replication.ContentStoreReplicatorTest;
|
||||
import org.alfresco.repo.content.replication.ReplicatingContentStoreTest;
|
||||
import org.alfresco.repo.content.routing.StoreSelectorAspectContentStoreTest;
|
||||
import org.alfresco.repo.content.transform.BinaryPassThroughContentTransformerTest;
|
||||
import org.alfresco.repo.content.transform.ComplexContentTransformerTest;
|
||||
import org.alfresco.repo.content.transform.ContentTransformerRegistryTest;
|
||||
@@ -96,6 +97,7 @@ public class ContentTestSuite extends TestSuite
|
||||
suite.addTestSuite(MimetypeMapTest.class);
|
||||
suite.addTestSuite(RoutingContentServiceTest.class);
|
||||
suite.addTestSuite(RoutingContentStoreTest.class);
|
||||
suite.addTestSuite(StoreSelectorAspectContentStoreTest.class);
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
@@ -62,6 +62,19 @@ public class NodeContentContext extends ContentContext
|
||||
this.propertyQName = propertyQName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("NodeContentContext")
|
||||
.append("[ contentUrl=").append(getContentUrl())
|
||||
.append(", existing=").append((getExistingContentReader() == null ? false : true))
|
||||
.append(", nodeRef=").append(nodeRef)
|
||||
.append(", propertyQName=").append(propertyQName)
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the node holding the content metadata
|
||||
*/
|
||||
|
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 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.content.routing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.AbstractRoutingContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.NodeContentContext;
|
||||
import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link AbstractRoutingContentStore routing content store} that diverts
|
||||
* and moves content based on the <b>cm:storeSelector</b> aspect.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class StoreSelectorAspectContentStore extends AbstractRoutingContentStore implements InitializingBean
|
||||
{
|
||||
private static final String ERR_INVALID_DEFAULT_STORE = "content.routing.err.invalid_default_store";
|
||||
|
||||
private static Log logger = LogFactory.getLog(StoreSelectorAspectContentStore.class);
|
||||
|
||||
private NodeService nodeService;
|
||||
private Map<String, ContentStore> storesByName;
|
||||
private List<ContentStore> stores;
|
||||
private String defaultStoreName;
|
||||
|
||||
public StoreSelectorAspectContentStore()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeService the service to access the properties
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param storesByName a map of content stores keyed by a common name
|
||||
*/
|
||||
public void setStoresByName(Map<String, ContentStore> storesByName)
|
||||
{
|
||||
this.storesByName = storesByName;
|
||||
this.stores = new ArrayList<ContentStore>(storesByName.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the stores keyed by store name
|
||||
*/
|
||||
public Map<String, ContentStore> getStoresByName()
|
||||
{
|
||||
return storesByName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the store to select if the content being created is not associated
|
||||
* with any specific value in the <b>cm:storeSelector</b> or if the aspect is not
|
||||
* present.
|
||||
*
|
||||
* @param defaultStoreName the name of one of the stores
|
||||
*
|
||||
* @see #setStoresByName(Map)
|
||||
*/
|
||||
public void setDefaultStoreName(String defaultStoreName)
|
||||
{
|
||||
this.defaultStoreName = defaultStoreName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the required properties are present
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
PropertyCheck.mandatory(this, "nodeService", nodeService);
|
||||
PropertyCheck.mandatory(this, "storesByName", storesByName);
|
||||
PropertyCheck.mandatory(this, "defaultStoreName", defaultStoreName);
|
||||
// Check that the default store name is valid
|
||||
if (storesByName.get(defaultStoreName) == null)
|
||||
{
|
||||
AlfrescoRuntimeException.create(ERR_INVALID_DEFAULT_STORE, defaultStoreName, storesByName.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ContentStore> getAllStores()
|
||||
{
|
||||
return stores;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContentStore selectWriteStore(ContentContext ctx)
|
||||
{
|
||||
ContentStore store;
|
||||
String storeNameProp;
|
||||
if (!(ctx instanceof NodeContentContext))
|
||||
{
|
||||
storeNameProp = "<NodeRef not available>";
|
||||
store = storesByName.get(defaultStoreName);
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeRef nodeRef = ((NodeContentContext) ctx).getNodeRef(); // Never null
|
||||
storeNameProp = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_STORE_NAME);
|
||||
if (storeNameProp == null)
|
||||
{
|
||||
storeNameProp = "<null>";
|
||||
store = storesByName.get(defaultStoreName);
|
||||
}
|
||||
else
|
||||
{
|
||||
store = storesByName.get(storeNameProp);
|
||||
if (store == null)
|
||||
{
|
||||
// There was no store with that name
|
||||
storeNameProp = "<unmapped store: " + storeNameProp + ">";
|
||||
store = storesByName.get(defaultStoreName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"ContentStore selected: \n" +
|
||||
" Node context: " + ctx + "\n" +
|
||||
" Store name: " + storeNameProp + "\n" +
|
||||
" Store Selected: " + store);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* A constraint that acts as a list of values, where the values are the store names
|
||||
* injected into the {@link StoreSelectorAspectContentStore}.
|
||||
* <p>
|
||||
* If the store is not active or is incorrectly configured, then this constraint
|
||||
* will contain a single value of 'Default'. Any attempt to set another value will
|
||||
* lead to constraint failures.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public static class StoreSelectorConstraint extends ListOfValuesConstraint
|
||||
{
|
||||
private StoreSelectorAspectContentStore store;
|
||||
/**
|
||||
* Required default constructor
|
||||
*/
|
||||
public StoreSelectorConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
public void setStore(StoreSelectorAspectContentStore store)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize()
|
||||
{
|
||||
checkPropertyNotNull("store", store);
|
||||
List<String> allowedValues = new ArrayList<String>(store.getStoresByName().keySet());
|
||||
super.setAllowedValues(allowedValues);
|
||||
// Now initialize as we have set the LOV
|
||||
super.initialize();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 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.content.routing;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.ContentServiceImpl;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.filestore.FileContentStore;
|
||||
import org.alfresco.repo.content.routing.StoreSelectorAspectContentStore.StoreSelectorConstraint;
|
||||
import org.alfresco.repo.node.integrity.IntegrityException;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.TempFileProvider;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Tests {@link StoreSelectorAspectContentStore}
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class StoreSelectorAspectContentStoreTest extends TestCase
|
||||
{
|
||||
private static final String STORE_ONE = "Store1";
|
||||
private static final String STORE_TWO = "Store2";
|
||||
private static final String STORE_THREE = "Store3";
|
||||
|
||||
private static ConfigurableApplicationContext ctx =
|
||||
(ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext();
|
||||
|
||||
private TransactionService transactionService;
|
||||
private NodeService nodeService;
|
||||
private FileFolderService fileFolderService;
|
||||
|
||||
private Map<String, ContentStore> storesByName;
|
||||
private FileContentStore fileStore1;
|
||||
private FileContentStore fileStore2;
|
||||
private FileContentStore fileStore3;
|
||||
private StoreSelectorAspectContentStore store;
|
||||
private NodeRef contentNodeRef;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
transactionService = serviceRegistry.getTransactionService();
|
||||
nodeService = serviceRegistry.getNodeService();
|
||||
fileFolderService = serviceRegistry.getFileFolderService();
|
||||
|
||||
AuthenticationUtil.pushAuthentication();
|
||||
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
|
||||
|
||||
fileStore1 = new FileContentStore(
|
||||
ctx,
|
||||
TempFileProvider.getSystemTempDir() + "/fileStore1");
|
||||
fileStore2 = new FileContentStore(
|
||||
ctx,
|
||||
TempFileProvider.getSystemTempDir() + "/fileStore2");
|
||||
fileStore3 = new FileContentStore(
|
||||
ctx,
|
||||
TempFileProvider.getSystemTempDir() + "/fileStore3");
|
||||
|
||||
storesByName = new HashMap<String, ContentStore>(7);
|
||||
storesByName.put(STORE_ONE, fileStore1);
|
||||
storesByName.put(STORE_TWO, fileStore2);
|
||||
storesByName.put(STORE_THREE, fileStore3);
|
||||
|
||||
store = (StoreSelectorAspectContentStore) ctx.getBean("storeSelectorContentStore");
|
||||
store.setStoresByName(storesByName);
|
||||
store.setDefaultStoreName(STORE_ONE);
|
||||
store.afterPropertiesSet();
|
||||
|
||||
// Force the constraint to re-initialize
|
||||
StoreSelectorConstraint storeConstraint = (StoreSelectorConstraint) ctx.getBean("storeSelectorContentStore.constraint");
|
||||
storeConstraint.initialize();
|
||||
|
||||
// Change the content service's default store
|
||||
ContentServiceImpl contentService = (ContentServiceImpl) ctx.getBean("contentService");
|
||||
contentService.setStore(store);
|
||||
|
||||
// Create a content node
|
||||
RetryingTransactionCallback<NodeRef> makeNodeCallback = new RetryingTransactionCallback<NodeRef>()
|
||||
{
|
||||
public NodeRef execute() throws Throwable
|
||||
{
|
||||
StoreRef storeRef = nodeService.createStore(
|
||||
StoreRef.PROTOCOL_TEST,
|
||||
getName() + "_" + System.currentTimeMillis());
|
||||
NodeRef rootNodeRef = nodeService.getRootNode(storeRef);
|
||||
// Create a folder
|
||||
NodeRef folderNodeRef = nodeService.createNode(
|
||||
rootNodeRef,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
ContentModel.TYPE_FOLDER).getChildRef();
|
||||
// Add some content
|
||||
return fileFolderService.create(
|
||||
folderNodeRef,
|
||||
getName() + ".txt",
|
||||
ContentModel.TYPE_CONTENT).getNodeRef();
|
||||
}
|
||||
};
|
||||
contentNodeRef = transactionService.getRetryingTransactionHelper().doInTransaction(makeNodeCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
AuthenticationUtil.popAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to the file
|
||||
* @return Returns the new content URL
|
||||
*/
|
||||
private String writeToFile()
|
||||
{
|
||||
RetryingTransactionCallback<String> writeContentCallback = new RetryingTransactionCallback<String>()
|
||||
{
|
||||
public String execute() throws Throwable
|
||||
{
|
||||
ContentWriter writer = fileFolderService.getWriter(contentNodeRef);
|
||||
writer.putContent("Some test content");
|
||||
return writer.getContentUrl();
|
||||
}
|
||||
};
|
||||
return transactionService.getRetryingTransactionHelper().doInTransaction(writeContentCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the store that must hold the content
|
||||
* @param storeName the name of the store
|
||||
*/
|
||||
private void setStoreNameProperty(String storeName)
|
||||
{
|
||||
// The nodeService is transactional
|
||||
nodeService.setProperty(contentNodeRef, ContentModel.PROP_STORE_NAME, storeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a <tt>null</tt> <b>cm:storeName</b> property is acceptable.
|
||||
*/
|
||||
public void testNullStoreNameProperty() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
setStoreNameProperty(null);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new Exception("Failed to set store name property to null", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that an invalid <b>cm:storeName</b> property is kicked out.
|
||||
*/
|
||||
public void testInvalidStoreNameProperty() throws Exception
|
||||
{
|
||||
RetryingTransactionCallback<Object> setInvalidStoreNameCallback = new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
setStoreNameProperty("bogus");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(setInvalidStoreNameCallback, false, true);
|
||||
setStoreNameProperty("bogus");
|
||||
fail("Expected integrity error for bogus store name");
|
||||
}
|
||||
catch (IntegrityException e)
|
||||
{
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the default store is used if the property is not set
|
||||
*/
|
||||
public void testWriteWithoutAspect() throws Exception
|
||||
{
|
||||
String contentUrl = writeToFile();
|
||||
// The content should be in the default store
|
||||
assertTrue("Default store does not have content", fileStore1.exists(contentUrl));
|
||||
assertFalse("Mapped store should not have content", fileStore2.exists(contentUrl));
|
||||
assertFalse("Mapped store should not have content", fileStore3.exists(contentUrl));
|
||||
}
|
||||
|
||||
public void testSimpleWritesWithAspect() throws Exception
|
||||
{
|
||||
for (Map.Entry<String, ContentStore> entry : storesByName.entrySet())
|
||||
{
|
||||
String storeName = entry.getKey();
|
||||
ContentStore store = entry.getValue();
|
||||
setStoreNameProperty(storeName);
|
||||
String contentUrl = writeToFile();
|
||||
assertTrue("Content not in store " + storeName, store.exists(contentUrl));
|
||||
}
|
||||
}
|
||||
}
|
@@ -41,8 +41,12 @@ import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.cache.EhCacheAdapter;
|
||||
import org.alfresco.repo.dictionary.DictionaryDAOImpl.DictionaryRegistry;
|
||||
import org.alfresco.repo.dictionary.NamespaceDAOImpl.NamespaceRegistry;
|
||||
import org.alfresco.repo.dictionary.constraint.AbstractConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.ConstraintRegistry;
|
||||
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.RegisteredConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.StringLengthConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.UserNameConstraint;
|
||||
import org.alfresco.repo.tenant.SingleTServiceImpl;
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
@@ -73,6 +77,17 @@ public class DictionaryDAOTest extends TestCase
|
||||
@Override
|
||||
public void setUp()
|
||||
{
|
||||
// Registered the required constraints
|
||||
ConstraintRegistry constraintRegistry = ConstraintRegistry.getInstance();
|
||||
AbstractConstraint constraintReg1 = new UserNameConstraint();
|
||||
constraintReg1.setShortName("cm:reg1");
|
||||
constraintReg1.setRegistry(constraintRegistry);
|
||||
constraintReg1.initialize();
|
||||
AbstractConstraint constraintReg2 = new UserNameConstraint();
|
||||
constraintReg2.setShortName("cm:reg2");
|
||||
constraintReg2.setRegistry(constraintRegistry);
|
||||
constraintReg2.initialize();
|
||||
|
||||
// register resource bundles for messages
|
||||
I18NUtil.registerResourceBundle(TEST_RESOURCE_MESSAGES);
|
||||
|
||||
@@ -185,6 +200,10 @@ public class DictionaryDAOTest extends TestCase
|
||||
|
||||
public void testConstraints()
|
||||
{
|
||||
// Check that the registered constraints are correct
|
||||
assertNotNull("Constraint reg1 not registered", ConstraintRegistry.getInstance().getConstraint("cm:reg1"));
|
||||
assertNotNull("Constraint reg2 not registered", ConstraintRegistry.getInstance().getConstraint("cm:reg2"));
|
||||
|
||||
// get the constraints for a property without constraints
|
||||
QName propNoConstraintsQName = QName.createQName(TEST_URL, "fileprop");
|
||||
PropertyDefinition propNoConstraintsDef = service.getProperty(propNoConstraintsQName);
|
||||
@@ -195,7 +214,10 @@ public class DictionaryDAOTest extends TestCase
|
||||
PropertyDefinition propDef = service.getProperty(prop1QName);
|
||||
List<ConstraintDefinition> constraints = propDef.getConstraints();
|
||||
assertNotNull("Null constraints list", constraints);
|
||||
assertEquals("Incorrect number of constraints", 2, constraints.size());
|
||||
assertEquals("Incorrect number of constraints", 3, constraints.size());
|
||||
assertTrue("Constraint instance incorrect", constraints.get(0).getConstraint() instanceof RegexConstraint);
|
||||
assertTrue("Constraint instance incorrect", constraints.get(1).getConstraint() instanceof StringLengthConstraint);
|
||||
assertTrue("Constraint instance incorrect", constraints.get(2).getConstraint() instanceof RegisteredConstraint);
|
||||
|
||||
// check the individual constraints
|
||||
ConstraintDefinition constraintDef = constraints.get(0);
|
||||
@@ -203,9 +225,6 @@ public class DictionaryDAOTest extends TestCase
|
||||
// check that the constraint implementation is valid (it used a reference)
|
||||
Constraint constraint = constraintDef.getConstraint();
|
||||
assertNotNull("Reference constraint has no implementation", constraint);
|
||||
|
||||
// make sure it is the correct type of constraint
|
||||
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
|
||||
}
|
||||
|
||||
public void testConstraintsOverrideInheritance()
|
||||
@@ -219,25 +238,28 @@ public class DictionaryDAOTest extends TestCase
|
||||
PropertyDefinition prop1Def = service.getProperty(baseQName, prop1QName);
|
||||
assertNotNull(prop1Def);
|
||||
List<ConstraintDefinition> prop1Constraints = prop1Def.getConstraints();
|
||||
assertEquals("Incorrect number of constraints", 2, prop1Constraints.size());
|
||||
assertEquals("Incorrect number of constraints", 3, prop1Constraints.size());
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(0).getConstraint() instanceof RegexConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(1).getConstraint() instanceof StringLengthConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(2).getConstraint() instanceof RegisteredConstraint);
|
||||
|
||||
// check the inherited property on folder (must be same as above)
|
||||
prop1Def = service.getProperty(folderQName, prop1QName);
|
||||
assertNotNull(prop1Def);
|
||||
prop1Constraints = prop1Def.getConstraints();
|
||||
assertEquals("Incorrect number of constraints", 2, prop1Constraints.size());
|
||||
assertEquals("Incorrect number of constraints", 3, prop1Constraints.size());
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(0).getConstraint() instanceof RegexConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(1).getConstraint() instanceof StringLengthConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(2).getConstraint() instanceof RegisteredConstraint);
|
||||
|
||||
// check the overridden property on file (must be reverse of above)
|
||||
prop1Def = service.getProperty(fileQName, prop1QName);
|
||||
assertNotNull(prop1Def);
|
||||
prop1Constraints = prop1Def.getConstraints();
|
||||
assertEquals("Incorrect number of constraints", 2, prop1Constraints.size());
|
||||
assertEquals("Incorrect number of constraints", 3, prop1Constraints.size());
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(0).getConstraint() instanceof StringLengthConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(1).getConstraint() instanceof RegexConstraint);
|
||||
assertTrue("Constraint instance incorrect", prop1Constraints.get(2).getConstraint() instanceof RegisteredConstraint);
|
||||
}
|
||||
|
||||
public void testArchive()
|
||||
|
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.NumericRangeConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.RegisteredConstraint;
|
||||
import org.alfresco.repo.dictionary.constraint.StringLengthConstraint;
|
||||
import org.alfresco.service.cmr.dictionary.Constraint;
|
||||
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
|
||||
@@ -48,36 +49,26 @@ import org.springframework.beans.PropertyAccessException;
|
||||
*/
|
||||
/* package */class M2ConstraintDefinition implements ConstraintDefinition
|
||||
{
|
||||
private static final String PROP_SHORT_NAME = "shortName";
|
||||
|
||||
public static final String ERR_CYCLIC_REF = "d_dictionary.constraint.err.cyclic_ref";
|
||||
|
||||
public static final String ERR_TYPE_AND_REF = "d_dictionary.constraint.err.type_and_ref";
|
||||
|
||||
public static final String ERR_TYPE_OR_REF = "d_dictionary.constraint.err.type_or_ref";
|
||||
|
||||
public static final String ERR_REF_NOT_FOUND = "d_dictionary.constraint.err.ref_not_found";
|
||||
|
||||
public static final String ERR_ANON_NEEDS_PROPERTY = "d_dictionary.constraint.err.anon_needs_property";
|
||||
|
||||
public static final String ERR_INVALID_TYPE = "d_dictionary.constraint.err.invalid_type";
|
||||
|
||||
public static final String ERR_SIMPLE_AND_LIST = "d_dictionary.constraint.err.property_simple_and_list";
|
||||
|
||||
public static final String ERR_CONSTRUCT_FAILURE = "d_dictionary.constraint.err.construct_failure";
|
||||
|
||||
public static final String ERR_PROPERTY_MISMATCH = "d_dictionary.constraint.err.property_mismatch";
|
||||
public static final String ERR_RESERVED_PROPERTY = "d_dictionary.constraint.err.reserved_property";
|
||||
|
||||
private static int anonPropCount = 0;
|
||||
|
||||
private ModelDefinition model;
|
||||
|
||||
private NamespacePrefixResolver prefixResolver;
|
||||
|
||||
private M2Constraint m2Constraint;
|
||||
|
||||
private QName name;
|
||||
|
||||
private Constraint constraint;
|
||||
|
||||
private boolean resolving;
|
||||
|
||||
/* package */M2ConstraintDefinition(M2PropertyDefinition m2PropertyDef, M2Constraint m2Constraint,
|
||||
@@ -176,6 +167,7 @@ import org.springframework.beans.PropertyAccessException;
|
||||
// try to establish it as a class
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Class clazz = Class.forName(type);
|
||||
constraint = (Constraint) clazz.newInstance();
|
||||
}
|
||||
@@ -200,6 +192,13 @@ import org.springframework.beans.PropertyAccessException;
|
||||
{
|
||||
for (M2NamedValue namedValue : constraintNamedValues)
|
||||
{
|
||||
String namedValueName = namedValue.getName();
|
||||
// Check for reserved properties
|
||||
if (namedValueName.equals(PROP_SHORT_NAME))
|
||||
{
|
||||
throw new DictionaryException(ERR_RESERVED_PROPERTY, PROP_SHORT_NAME, namedValueName);
|
||||
}
|
||||
|
||||
Object value = null;
|
||||
if (namedValue.getSimpleValue() != null && namedValue.getListValue() != null)
|
||||
{
|
||||
@@ -215,24 +214,24 @@ import org.springframework.beans.PropertyAccessException;
|
||||
}
|
||||
try
|
||||
{
|
||||
beanWrapper.setPropertyValue(namedValue.getName(), value);
|
||||
beanWrapper.setPropertyValue(namedValueName, value);
|
||||
}
|
||||
catch (PropertyAccessException e)
|
||||
{
|
||||
throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValue.getName(), shortName);
|
||||
throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName);
|
||||
}
|
||||
catch (InvalidPropertyException e)
|
||||
{
|
||||
throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValue.getName(), shortName);
|
||||
throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass in the name as a special property, if it is available
|
||||
if (beanWrapper.isWritableProperty("_shortName"))
|
||||
// Pass in the short name as a special property, if it is available
|
||||
if (beanWrapper.isWritableProperty(PROP_SHORT_NAME))
|
||||
{
|
||||
try
|
||||
{
|
||||
beanWrapper.setPropertyValue("_shortName", shortName);
|
||||
beanWrapper.setPropertyValue(PROP_SHORT_NAME, shortName);
|
||||
}
|
||||
catch (PropertyAccessException e)
|
||||
{
|
||||
@@ -279,6 +278,14 @@ import org.springframework.beans.PropertyAccessException;
|
||||
*/
|
||||
public static enum ConstraintType
|
||||
{
|
||||
REGISTERED
|
||||
{
|
||||
@Override
|
||||
protected Constraint newInstance()
|
||||
{
|
||||
return new RegisteredConstraint();
|
||||
}
|
||||
},
|
||||
REGEX
|
||||
{
|
||||
@Override
|
||||
|
@@ -25,6 +25,8 @@
|
||||
package org.alfresco.repo.dictionary.constraint;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.Constraint;
|
||||
import org.alfresco.service.cmr.dictionary.ConstraintException;
|
||||
@@ -44,20 +46,31 @@ public abstract class AbstractConstraint implements Constraint
|
||||
|
||||
/** The constraint name. May be useful in error messages */
|
||||
private String shortName;
|
||||
|
||||
private ConstraintRegistry registry;
|
||||
|
||||
/**
|
||||
* Sets the constraint name. Automatically called after construction. Please excuse the strange method name as we
|
||||
* want the property name to begin with an underscore to avoid property name clashes.
|
||||
*
|
||||
* @param shortName
|
||||
* @deprecated
|
||||
*/
|
||||
public void set_shortName(String shortName)
|
||||
{
|
||||
this.shortName = shortName;
|
||||
setShortName(shortName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the constraint name. May be useful in error messages.
|
||||
* Sets the constraint name
|
||||
* @param name
|
||||
*/
|
||||
public void setShortName(String name)
|
||||
{
|
||||
this.shortName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constraint name.
|
||||
*
|
||||
* @return the constraint name.
|
||||
*/
|
||||
@@ -66,14 +79,41 @@ public abstract class AbstractConstraint implements Constraint
|
||||
return this.shortName;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getType()
|
||||
/**
|
||||
* Optionally specify the registry that will be used to register the constraint.
|
||||
* This is used when instantiating constraints outside the dictionary.
|
||||
*
|
||||
* @param registry the constraint registry
|
||||
*/
|
||||
public void setRegistry(ConstraintRegistry registry)
|
||||
{
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public String getType()
|
||||
{
|
||||
return this.getClass().getName();
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
return new HashMap<String, Object>(3);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Registers the constraint with the registry, if present. Call this method if
|
||||
* you want the constraint to be auto-registered.
|
||||
*/
|
||||
public void initialize()
|
||||
{
|
||||
if (registry != null)
|
||||
{
|
||||
registry.register(shortName, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the given value is not <tt>null</tt>.
|
||||
*
|
||||
@@ -86,7 +126,7 @@ public abstract class AbstractConstraint implements Constraint
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, value);
|
||||
throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, name, getShortName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,8 +24,6 @@
|
||||
*/
|
||||
package org.alfresco.repo.dictionary.constraint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.ConstraintException;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
|
||||
@@ -33,7 +31,6 @@ import org.alfresco.service.cmr.security.AuthorityType;
|
||||
|
||||
public class AuthorityNameConstraint extends AbstractConstraint
|
||||
{
|
||||
|
||||
private static final String ERR_INVALID_AUTHORITY_NAME = "d_dictionary.constraint.authority_name.invalid_authority_name";
|
||||
private static final String ERR_NON_STRING = "d_dictionary.constraint.authority_name.non_string";
|
||||
|
||||
@@ -57,20 +54,4 @@ public class AuthorityNameConstraint extends AbstractConstraint
|
||||
throw new ConstraintException(ERR_INVALID_AUTHORITY_NAME, value, type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#initialize()
|
||||
*/
|
||||
public void initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 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.dictionary.constraint;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.Constraint;
|
||||
|
||||
/**
|
||||
* A registry of constraints.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ConstraintRegistry
|
||||
{
|
||||
private static ConstraintRegistry instance = new ConstraintRegistry();
|
||||
|
||||
private Map<String, Constraint> constraints;
|
||||
|
||||
/**
|
||||
* @return Returns the singleton
|
||||
*/
|
||||
public static ConstraintRegistry getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor
|
||||
* @see #getInstance()
|
||||
*/
|
||||
private ConstraintRegistry()
|
||||
{
|
||||
constraints = new HashMap<String, Constraint>(13);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the constraint by name
|
||||
*/
|
||||
public void register(String name, Constraint constraint)
|
||||
{
|
||||
if (this == instance)
|
||||
{
|
||||
constraints.put(name, constraint);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.register(name, constraint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the constraint by name
|
||||
*
|
||||
* @param name the name by which the constraint was registered
|
||||
* @return Returns the constraint or <tt>null</tt> if it does not exist.
|
||||
*/
|
||||
public Constraint getConstraint(String name)
|
||||
{
|
||||
if (this == instance)
|
||||
{
|
||||
return constraints.get(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return instance.getConstraint(name);
|
||||
}
|
||||
}
|
||||
}
|
@@ -319,9 +319,7 @@ public class ConstraintsTest extends TestCase
|
||||
{
|
||||
private List<Object> tested;
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#initialize()
|
||||
*/
|
||||
@Override
|
||||
public void initialize()
|
||||
{
|
||||
tested = new ArrayList<Object>(4);
|
||||
@@ -348,13 +346,5 @@ public class ConstraintsTest extends TestCase
|
||||
throw new ConstraintException("Non-String value");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -59,8 +59,8 @@ public class ListOfValuesConstraint extends AbstractConstraint
|
||||
caseSensitive = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getType()
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getType()
|
||||
@@ -135,10 +135,23 @@ public class ListOfValuesConstraint extends AbstractConstraint
|
||||
this.caseSensitive = caseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize()
|
||||
{
|
||||
super.initialize();
|
||||
checkPropertyNotNull("allowedValues", allowedValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("caseSensitive", this.caseSensitive);
|
||||
params.put("allowedValues", this.allowedValues);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
protected void evaluateSingleValue(Object value)
|
||||
{
|
||||
@@ -168,17 +181,4 @@ public class ListOfValuesConstraint extends AbstractConstraint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("caseSensitive", this.caseSensitive);
|
||||
params.put("allowedValues", this.allowedValues);
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
@@ -53,9 +53,6 @@ public class NumericRangeConstraint extends AbstractConstraint
|
||||
private double minValue = Double.MIN_VALUE;
|
||||
private double maxValue = Double.MAX_VALUE;
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getType()
|
||||
*/
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
@@ -118,17 +115,18 @@ public class NumericRangeConstraint extends AbstractConstraint
|
||||
}
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#initialize()
|
||||
*/
|
||||
public void initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.repo.dictionary.constraint.AbstractConstraint#evaluateSingleValue(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("minValue", this.minValue);
|
||||
params.put("maxValue", this.maxValue);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
protected void evaluateSingleValue(Object value)
|
||||
{
|
||||
// ensure that the value can be converted to a double
|
||||
@@ -154,17 +152,4 @@ public class NumericRangeConstraint extends AbstractConstraint
|
||||
throw new ConstraintException(ERR_OUT_OF_RANGE, checkValue, minValue, maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("minValue", this.minValue);
|
||||
params.put("maxValue", this.maxValue);
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
@@ -57,9 +57,9 @@ public class RegexConstraint extends AbstractConstraint
|
||||
private String expression;
|
||||
private Pattern patternMatcher;
|
||||
private boolean requiresMatch = true;
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getType()
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
@@ -113,7 +113,19 @@ public class RegexConstraint extends AbstractConstraint
|
||||
{
|
||||
this.requiresMatch = requiresMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("expression", this.expression);
|
||||
params.put("requiresMatch", this.requiresMatch);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize()
|
||||
{
|
||||
checkPropertyNotNull("expression", expression);
|
||||
@@ -146,14 +158,4 @@ public class RegexConstraint extends AbstractConstraint
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("expression", this.expression);
|
||||
params.put("requiresMatch", this.requiresMatch);
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 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.dictionary.constraint;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.Constraint;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||
|
||||
/**
|
||||
* Constraint implementation that defers to constraints registered with the
|
||||
* static instance of the {@link ConstraintRegistry}.
|
||||
*
|
||||
* @see #setAllowedValues(List)
|
||||
* @see #setCaseSensitive(boolean)
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public final class RegisteredConstraint implements Constraint
|
||||
{
|
||||
private static final String ERR_NAME_NOT_REGISTERED = "d_dictionary.constraint.registered.not_registered";
|
||||
|
||||
private String shortName;
|
||||
private String registeredName;
|
||||
|
||||
public RegisteredConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(80);
|
||||
sb.append("RegisteredConstraint")
|
||||
.append("[ registeredName=").append(registeredName)
|
||||
.append(", constraint=").append(ConstraintRegistry.getInstance().getConstraint(registeredName))
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getShortName()
|
||||
{
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public void setShortName(String shortName)
|
||||
{
|
||||
this.shortName = shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the constraint that will be used to look up the constraint
|
||||
* that will be delegated to.
|
||||
*/
|
||||
public void setRegisteredName(String registeredName)
|
||||
{
|
||||
this.registeredName = registeredName;
|
||||
}
|
||||
|
||||
public void initialize()
|
||||
{
|
||||
if (registeredName == null)
|
||||
{
|
||||
throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, "registeredName");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the constraint that matches the registered name
|
||||
*/
|
||||
private Constraint getConstraint()
|
||||
{
|
||||
Constraint constraint = ConstraintRegistry.getInstance().getConstraint(registeredName);
|
||||
if (constraint == null)
|
||||
{
|
||||
throw new DictionaryException(ERR_NAME_NOT_REGISTERED, registeredName);
|
||||
}
|
||||
return constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers to the registered constraint
|
||||
*/
|
||||
public String getType()
|
||||
{
|
||||
return getConstraint().getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers to the registered constraint
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
return getConstraint().getParameters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers to the registered constraint
|
||||
*/
|
||||
public void evaluate(Object value)
|
||||
{
|
||||
getConstraint().evaluate(value);
|
||||
}
|
||||
}
|
@@ -50,8 +50,8 @@ public class StringLengthConstraint extends AbstractConstraint
|
||||
private int minLength = 0;
|
||||
private int maxLength = Integer.MAX_VALUE;
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getType()
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getType()
|
||||
@@ -116,8 +116,15 @@ public class StringLengthConstraint extends AbstractConstraint
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
public void initialize()
|
||||
@Override
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("minLength", this.minLength);
|
||||
params.put("maxLength", this.maxLength);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
protected void evaluateSingleValue(Object value)
|
||||
@@ -144,17 +151,4 @@ public class StringLengthConstraint extends AbstractConstraint
|
||||
throw new ConstraintException(ERR_INVALID_LENGTH, checkValue, minLength, maxLength);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
Map<String, Object> params = new HashMap<String, Object>(2);
|
||||
|
||||
params.put("minLength", this.minLength);
|
||||
params.put("maxLength", this.maxLength);
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
@@ -24,8 +24,6 @@
|
||||
*/
|
||||
package org.alfresco.repo.dictionary.constraint;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.ConstraintException;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
|
||||
@@ -62,20 +60,4 @@ public class UserNameConstraint extends AbstractConstraint
|
||||
throw new ConstraintException(ERR_INVALID_USERNAME, value, type);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#initialize()
|
||||
*/
|
||||
public void initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.alfresco.service.cmr.dictionary.Constraint#getParameters()
|
||||
*/
|
||||
public Map<String, Object> getParameters()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,12 @@
|
||||
</data-types>
|
||||
|
||||
<constraints>
|
||||
<constraint name="test:registered1" type="REGISTERED">
|
||||
<parameter name="registeredName"><value>cm:reg1</value></parameter>
|
||||
</constraint>
|
||||
<constraint name="test:registered2" type="REGISTERED">
|
||||
<parameter name="registeredName"><value>cm:reg2</value></parameter>
|
||||
</constraint>
|
||||
<constraint name="test:regex1" type="REGEX">
|
||||
<parameter name="expression"><value>[A-Z]*</value></parameter>
|
||||
<parameter name="requiresMatch"><value>false</value></parameter>
|
||||
@@ -69,6 +75,7 @@
|
||||
<constraints>
|
||||
<constraint ref="test:regex1"/>
|
||||
<constraint ref="test:stringLength1"/>
|
||||
<constraint ref="test:registered1"/>
|
||||
</constraints>
|
||||
</property>
|
||||
</properties>
|
||||
@@ -159,6 +166,7 @@
|
||||
<constraints>
|
||||
<constraint ref="test:stringLength2"/>
|
||||
<constraint ref="test:regex2"/>
|
||||
<constraint ref="test:registered2"/>
|
||||
</constraints>
|
||||
</property>
|
||||
</overrides>
|
||||
|
@@ -56,7 +56,7 @@ public interface Constraint
|
||||
/**
|
||||
* Returns the parameters passed to the instance of the constraint.
|
||||
*
|
||||
* @return Map of parameters, null if there are no parameters
|
||||
* @return Map of parameters or an empty <tt>Map</tt> if none exist
|
||||
*/
|
||||
public Map<String, Object> getParameters();
|
||||
|
||||
|
Reference in New Issue
Block a user