diff --git a/config/alfresco/linkvalidation-service-context.xml b/config/alfresco/linkvalidation-service-context.xml index f1fe27a0f0..abdcb4a5c8 100644 --- a/config/alfresco/linkvalidation-service-context.xml +++ b/config/alfresco/linkvalidation-service-context.xml @@ -266,7 +266,16 @@ - + + ${linkvalidation.pollInterval} + + + + ${linkvalidation.retryInterval} + + + ${linkvalidation.disableOnFail} + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index ac613a56fe..a08b63b84c 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -226,21 +226,29 @@ system.people_container.childname=sys:people system.workflow_container.childname=sys:workflow # Are user names case sensitive? -# ============================== -# -# NOTE: If you are using mysql you must have case sensitive collation -# -# You can do this when creating the alfresco database at the start -# CREATE DATABASE alfresco CHARACTER SET utf8 COLLATE utf8_bin; -# If you want to do this later this is a dump and load fix as it is done when the database, tables and columns are created. -# -# Must other databases are case sensitive by default. -# user.name.caseSensitive=false # AVM Specific properties. avm.remote.idlestream.timeout=30000 +# ################################## # +# WCM Link Validation Configuration # +# ################################## # +# +# Note: Link Validation is disabled by default (as per poll interval = 0) +# +# linkvalidation.pollInterval - Poll interval to check getLatestSnapshotID (in milliseconds), eg. 5000 for 5 sec interval +# If pollInterval is 0, link validation is disabled. +# +# linkvalidation.retryInterval - Retry interval (Virtualization server is not accessible or an error has occurred +# during link validation. +# +# linkvalidation.disableOnFail - If set to TRUE link validation service will be terminated if an error will be occurred. + +linkvalidation.pollInterval=0 +linkvalidation.retryInterval=120000 +linkvalidation.disableOnFail=false + # ECM content usages/quotas system.usages.enabled=true diff --git a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java index ead48fb615..96c006bd3b 100644 --- a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java +++ b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java @@ -83,9 +83,10 @@ public class ImporterBootstrap extends AbstractLifecycleBean private static final Log logger = LogFactory.getLog(ImporterBootstrap.class); private boolean logEnabled = false; - // Dependencies private boolean allowWrite = true; private boolean useExistingStore = false; + private UUID_BINDING uuidBinding = null; + // Dependencies private TransactionService transactionService; private NamespaceService namespaceService; private NodeService nodeService; @@ -125,6 +126,19 @@ public class ImporterBootstrap extends AbstractLifecycleBean this.useExistingStore = useExistingStore; } + /** + * Set the behaviour for generating UUIDs in the import. Values are set by the + * {@link UUID_BINDING} enum and default to {@link UUID_BINDING#CREATE_NEW_WITH_UUID}. + *

+ * This setting overrides the UUID binding behaviour specified in the view properties. + * + * @param uuidBinding the UUID generation behaviour + */ + public void setUuidBinding(UUID_BINDING uuidBinding) + { + this.uuidBinding = uuidBinding; + } + /** * Sets the Transaction Service * @@ -420,18 +434,23 @@ public class ImporterBootstrap extends AbstractLifecycleBean binding.setResourceBundle(bundle); } - String uuidBinding = bootstrapView.getProperty(VIEW_UUID_BINDING); - if (uuidBinding != null && uuidBinding.length() > 0) + String viewUuidBinding = bootstrapView.getProperty(VIEW_UUID_BINDING); + if (viewUuidBinding != null && viewUuidBinding.length() > 0) { try { - binding.setUUIDBinding(UUID_BINDING.valueOf(UUID_BINDING.class, uuidBinding)); + binding.setUUIDBinding(UUID_BINDING.valueOf(UUID_BINDING.class, viewUuidBinding)); } catch(IllegalArgumentException e) { - throw new ImporterException("The value " + uuidBinding + " is an invalid uuidBinding"); + throw new ImporterException("The value " + viewUuidBinding + " is an invalid uuidBinding"); } - } + } + // Override the UUID binding with the bean's + if (this.uuidBinding != null) + { + binding.setUUIDBinding(this.uuidBinding); + } // Now import... ImporterProgress importProgress = null; @@ -539,14 +558,13 @@ public class ImporterBootstrap extends AbstractLifecycleBean private Properties configuration = null; private ResourceBundle resourceBundle = null; private Location bootstrapLocation = null; + /** by default, use create new strategy for bootstrap import */ + private UUID_BINDING uuidBinding = UUID_BINDING.CREATE_NEW_WITH_UUID; private static final String IMPORT_LOCATION_UUID = "bootstrap.location.uuid"; private static final String IMPORT_LOCATION_NODEREF = "bootstrap.location.noderef"; private static final String IMPORT_LOCATION_PATH = "bootstrap.location.path"; - // by default, use create new strategy for bootstrap import - private UUID_BINDING uuidBinding = UUID_BINDING.CREATE_NEW_WITH_UUID; - /** * Set Import Configuration * @@ -620,10 +638,6 @@ public class ImporterBootstrap extends AbstractLifecycleBean return value; } - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.view.ImporterBinding#getUUIDBinding() - */ public UUID_BINDING getUUIDBinding() { return uuidBinding; @@ -639,19 +653,11 @@ public class ImporterBootstrap extends AbstractLifecycleBean this.uuidBinding = uuidBinding; } - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.view.ImporterBinding#searchWithinTransaction() - */ public boolean allowReferenceWithinTransaction() { return true; } - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.view.ImporterBinding#getExcludedClasses() - */ public QName[] getExcludedClasses() { // Note: Do not exclude any classes, we want to import all diff --git a/source/java/org/alfresco/repo/importer/ImporterComponentTest.java b/source/java/org/alfresco/repo/importer/ImporterComponentTest.java index 7dc77e478a..6cd5ded04c 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponentTest.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponentTest.java @@ -33,6 +33,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.cmr.view.ImporterBinding.UUID_BINDING; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.debug.NodeStoreInspector; @@ -80,6 +81,54 @@ public class ImporterComponentTest extends BaseSpringTest System.out.println(NodeStoreInspector.dumpNodeStore(nodeService, storeRef)); } + public void testImportWithUuidBinding() throws Exception + { + Location location = new Location(storeRef); + + // First pass must succeed + InputStream test1 = getClass().getClassLoader().getResourceAsStream("org/alfresco/repo/importer/importercomponent_test.xml"); + InputStreamReader testReader1 = new InputStreamReader(test1, "UTF-8"); + try + { + importerService.importView(testReader1, location, null, new ImportTimerProgress()); + } + finally + { + testReader1.close(); + } + // Second pass must succeed (defaults to CREATE_NEW) + InputStream test2 = getClass().getClassLoader().getResourceAsStream("org/alfresco/repo/importer/importercomponent_test.xml"); + InputStreamReader testReader2 = new InputStreamReader(test2, "UTF-8"); + try + { + importerBootstrap.setUuidBinding(UUID_BINDING.CREATE_NEW_WITH_UUID); + importerService.importView(testReader2, location, null, new ImportTimerProgress()); + } + finally + { + testReader2.close(); + } + // Set the UUID binding to guarantee a failure + InputStream test3 = getClass().getClassLoader().getResourceAsStream("org/alfresco/repo/importer/importercomponent_test.xml"); + InputStreamReader testReader3 = new InputStreamReader(test3, "UTF-8"); + try + { + importerBootstrap.setUuidBinding(UUID_BINDING.THROW_ON_COLLISION); + importerService.importView(testReader3, location, null, new ImportTimerProgress()); + fail("Failed to detected collision of UUID on import with THROW_ON_COLLISION"); + } + catch (Throwable e) + { + // Expected + } + finally + { + importerBootstrap.setUuidBinding(UUID_BINDING.CREATE_NEW_WITH_UUID); + testReader3.close(); + } + System.out.println(NodeStoreInspector.dumpNodeStore(nodeService, storeRef)); + } + public void testBootstrap() { StoreRef bootstrapStoreRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); diff --git a/source/java/org/alfresco/repo/importer/importercomponent_test.xml b/source/java/org/alfresco/repo/importer/importercomponent_test.xml index d5fd119201..eb794f2d37 100644 --- a/source/java/org/alfresco/repo/importer/importercomponent_test.xml +++ b/source/java/org/alfresco/repo/importer/importercomponent_test.xml @@ -22,6 +22,7 @@ + 05efa5db-2817-4ada-a0dc-657db2b2a0ac `¬¦!£$%^&()-_=+tnu0000[]{};'#@~, testuser testuser diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java index faffe1e9a7..0ffc79ae0b 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java @@ -141,16 +141,11 @@ public class RhinoScriptTest extends TestCase // try another script eval - this time a function call returning a result result = cx.evaluateString(scope, "function f(x) {return x+1} f(7)", "TestJS2", 1, null); - assertEquals(8.0, cx.toNumber(result)); - } - catch (Throwable err) - { - err.printStackTrace(); - fail(err.getMessage()); + assertEquals(8.0, Context.toNumber(result)); } finally { - cx.exit(); + Context.exit(); } return null; @@ -186,7 +181,7 @@ public class RhinoScriptTest extends TestCase // evaluate script that touches the wrapped NodeRef Object result = cx.evaluateString(scope, "obj = rootref.getId()", "TestJS3", 1, null); - assertEquals(ref1.getId(), cx.toString(result)); + assertEquals(ref1.getId(), Context.toString(result)); // wrap a scriptable Alfresco Node object - the Node object is a wrapper like TemplateNode ScriptNode node1 = new ScriptNode(root, serviceRegistry, scope); @@ -196,14 +191,9 @@ public class RhinoScriptTest extends TestCase // evaluate scripts that perform methods on the wrapped Node result = cx.evaluateString(scope, TESTSCRIPT1, "TestJS4", 1, null); } - catch (Throwable err) - { - err.printStackTrace(); - fail(err.getMessage()); - } finally { - cx.exit(); + Context.exit(); } return null; @@ -258,10 +248,8 @@ public class RhinoScriptTest extends TestCase // test executing a script directly as a String scriptService.executeScriptString("javascript", TESTSCRIPT1, model); } - catch (Throwable err) + finally { - err.printStackTrace(); - fail(err.getMessage()); } return null; @@ -311,10 +299,8 @@ public class RhinoScriptTest extends TestCase // ensure aspect has been added via script assertTrue(nodeService.hasAspect(childRef.getChildRef(), ContentModel.ASPECT_LOCKABLE)); } - catch (Throwable err) + finally { - err.printStackTrace(); - fail(err.getMessage()); } return null; @@ -361,10 +347,8 @@ public class RhinoScriptTest extends TestCase System.out.println("Result from TESTSCRIPT_CLASSPATH3: " + result.toString()); assertTrue((Boolean)result); // we know the result is a boolean } - catch (Throwable err) + finally { - err.printStackTrace(); - fail(err.getMessage()); } return null; diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index e209d91023..b20d4f8029 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -588,6 +588,26 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest { value = new ContentData(null, MimetypeMap.EXTENSION_BINARY, 0L, "UTF-8"); } + else if (propertyTypeQName.equals(DataTypeDefinition.LOCALE)) + { + value = Locale.CHINESE; + } + else if (propertyTypeQName.equals(DataTypeDefinition.BOOLEAN)) + { + value = Boolean.TRUE; + } + else if (propertyTypeQName.equals(DataTypeDefinition.PATH)) + { + value = new Path(); + } + else if (propertyTypeQName.equals(DataTypeDefinition.QNAME)) + { + value = TYPE_QNAME_EXTENDED_CONTENT; + } + else if (propertyTypeQName.equals(DataTypeDefinition.CATEGORY) || propertyTypeQName.equals(DataTypeDefinition.NODE_REF)) + { + value = new NodeRef("workspace://SpacesStore/12345"); + } else { value = new Long(System.currentTimeMillis()); diff --git a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java index 8b7986f358..d7235e05fb 100644 --- a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/FullNodeServiceTest.java @@ -25,11 +25,14 @@ package org.alfresco.repo.node; import java.io.Serializable; +import java.util.HashMap; import java.util.Locale; import java.util.Map; +import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; @@ -132,6 +135,75 @@ public class FullNodeServiceTest extends BaseNodeServiceTest assertNull("Value returned is not null", mlText); } + public void testMLValuesOnCreate() throws Exception + { + Map properties = new HashMap(); + fillProperties(BaseNodeServiceTest.TYPE_QNAME_TEST_MANY_PROPERTIES, properties); + // Replace the MLText value with a plain string + properties.put(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, "Bonjour"); + // Now switch to French + I18NUtil.setContentLocale(Locale.FRENCH); + // Create a node with the value + NodeRef nodeRef = nodeService.createNode( + rootNodeRef, + BaseNodeServiceTest.ASSOC_TYPE_QNAME_TEST_CHILDREN, + QName.createQName(BaseNodeServiceTest.NAMESPACE, getName()), + BaseNodeServiceTest.TYPE_QNAME_TEST_MANY_PROPERTIES, + properties).getChildRef(); + // Now switch to English + I18NUtil.setContentLocale(Locale.ENGLISH); + // Set the english property + nodeService.setProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, "Hello"); + + // Switch back to French and get the value + I18NUtil.setContentLocale(Locale.FRENCH); + assertEquals( + "Expected French value property", + "Bonjour", + nodeService.getProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE)); + + // Switch back to English and get the value + I18NUtil.setContentLocale(Locale.ENGLISH); + assertEquals( + "Expected English value property", + "Hello", + nodeService.getProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE)); + } + + public void testMLValuesOnAddAspect() throws Exception + { + Map properties = new HashMap(); + fillProperties(BaseNodeServiceTest.TYPE_QNAME_TEST_MANY_PROPERTIES, properties); + // Replace the MLText value with a plain string + properties.put(BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, "Bonjour"); + // Now switch to French + I18NUtil.setContentLocale(Locale.FRENCH); + // Add an aspect + NodeRef nodeRef = rootNodeRef; + nodeService.addAspect( + nodeRef, + BaseNodeServiceTest.ASPECT_QNAME_TEST_TITLED, + properties); + // Now switch to English + I18NUtil.setContentLocale(Locale.ENGLISH); + // Set the english property + nodeService.setProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE, "Hello"); + + // Switch back to French and get the value + I18NUtil.setContentLocale(Locale.FRENCH); + assertEquals( + "Expected French value property", + "Bonjour", + nodeService.getProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE)); + + // Switch back to English and get the value + I18NUtil.setContentLocale(Locale.ENGLISH); + assertEquals( + "Expected English value property", + "Hello", + nodeService.getProperty(nodeRef, BaseNodeServiceTest.PROP_QNAME_ML_TEXT_VALUE)); + } + /** * {@inheritDoc} * diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index 55470448b4..de2db3b9cd 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -25,6 +25,7 @@ package org.alfresco.repo.node; import java.io.Serializable; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Locale; @@ -196,26 +197,20 @@ public class MLPropertyInterceptor implements MethodInterceptor else if (methodName.equals("setProperties")) { NodeRef nodeRef = (NodeRef) args[0]; + Map newProperties =(Map) args[1]; // Get the pivot translation, if appropriate NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); - Map newProperties =(Map) args[1]; // Get the current properties for the node Map currentProperties = nodeService.getProperties(nodeRef); // Convert all properties - Map convertedProperties = new HashMap(newProperties.size() * 2); - for (Map.Entry entry : newProperties.entrySet()) - { - QName propertyQName = entry.getKey(); - Serializable inboundValue = entry.getValue(); - // Get the current property value - Serializable currentValue = currentProperties.get(propertyQName); - // Convert the inbound property value - inboundValue = convertInboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, inboundValue, currentValue); - // Put the value into the map - convertedProperties.put(propertyQName, inboundValue); - } + Map convertedProperties = convertInboundProperties( + currentProperties, + newProperties, + contentLocale, + nodeRef, + pivotNodeRef); // Now complete the call by passing the converted properties nodeService.setProperties(nodeRef, convertedProperties); // Done @@ -236,6 +231,55 @@ public class MLPropertyInterceptor implements MethodInterceptor nodeService.setProperty(nodeRef, propertyQName, inboundValue); // Done } + else if (methodName.equals("createNode") && args.length > 4) + { + NodeRef parentNodeRef = (NodeRef) args[0]; + QName assocTypeQName = (QName) args[1]; + QName assocQName = (QName) args[2]; + QName nodeTypeQName = (QName) args[3]; + Map newProperties =(Map) args[4]; + if (newProperties == null) + { + newProperties = Collections.emptyMap(); + } + NodeRef nodeRef = null; // Not created yet + + // No pivot + NodeRef pivotNodeRef = null; + + // Convert all properties + Map convertedProperties = convertInboundProperties( + null, + newProperties, + contentLocale, + nodeRef, + pivotNodeRef); + // Now complete the call by passing the converted properties + ret = nodeService.createNode(parentNodeRef, assocTypeQName, assocQName, nodeTypeQName, convertedProperties); + // Done + } + else if (methodName.equals("addAspect") && args[2] != null) + { + NodeRef nodeRef = (NodeRef) args[0]; + QName aspectTypeQName = (QName) args[1]; + + // Get the pivot translation, if appropriate + NodeRef pivotNodeRef = getPivotNodeRef(nodeRef); + + Map newProperties =(Map) args[2]; + // Get the current properties for the node + Map currentProperties = nodeService.getProperties(nodeRef); + // Convert all properties + Map convertedProperties = convertInboundProperties( + currentProperties, + newProperties, + contentLocale, + nodeRef, + pivotNodeRef); + // Now complete the call by passing the converted properties + nodeService.addAspect(nodeRef, aspectTypeQName, convertedProperties); + // Done + } else { ret = invocation.proceed(); @@ -328,6 +372,28 @@ public class MLPropertyInterceptor implements MethodInterceptor return ret; } + private Map convertInboundProperties( + Map currentProperties, + Map newProperties, + Locale contentLocale, + NodeRef nodeRef, + NodeRef pivotNodeRef) + { + Map convertedProperties = new HashMap(newProperties.size() * 2); + for (Map.Entry entry : newProperties.entrySet()) + { + QName propertyQName = entry.getKey(); + Serializable inboundValue = entry.getValue(); + // Get the current property value + Serializable currentValue = currentProperties == null ? null : currentProperties.get(propertyQName); + // Convert the inbound property value + inboundValue = convertInboundProperty(contentLocale, nodeRef, pivotNodeRef, propertyQName, inboundValue, currentValue); + // Put the value into the map + convertedProperties.put(propertyQName, inboundValue); + } + return convertedProperties; + } + /** * * @param inboundValue The value that must be set @@ -360,7 +426,7 @@ public class MLPropertyInterceptor implements MethodInterceptor { // This is a multilingual single-valued property // Get the current value from the node service, if not provided - if (currentValue == null) + if (currentValue == null && nodeRef != null) { currentValue = nodeService.getProperty(nodeRef, propertyQName); }