From 941ca950a99f76fb693ff84d781c92df19828b99 Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Mon, 14 Mar 2016 16:15:59 +0000 Subject: [PATCH] Merged 5.0.N (5.0.4) to 5.2.N (5.2.1) 123880 rneamtu: Merged V4.2-BUG-FIX (4.2.7) to 5.0.N (5.0.4) 123574 amorarasu: Merged V4.2.6 (4.2.6) to V4.2-BUG-FIX (4.2.7) 123414 123524 amorarasu: MNT-15798: CLONE - call to guessMimetype() on a node that has no content creates a content property - Content Data should be created only when it has a binary, not as a side effect of getters on ScriptNode. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@123892 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../org/alfresco/repo/jscript/ScriptNode.java | 53 ++++++++--- .../alfresco/repo/jscript/ScriptNodeTest.java | 95 ++++++++++++++++++- 2 files changed, 135 insertions(+), 13 deletions(-) diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java index 9564138213..fbbbe9969e 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptNode.java +++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java @@ -1726,10 +1726,23 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider { Serializable value = (Serializable) this.properties.get(key); + QName qname = createQName(key); + + // MNT-15798 + if (ContentModel.PROP_CONTENT.equals(qname) && isScriptContent(value)) + { + ScriptContentData contentData = (ScriptContentData) value; + // Do not persist the contentData if it was not touched + if (!contentData.isDirty()) + { + continue; + } + } + // perform the conversion from script wrapper object to repo serializable values value = getValueConverter().convertValueForRepo(value); - props.put(createQName(key), value); + props.put(qname, value); } this.nodeService.setProperties(this.nodeRef, props); } @@ -3768,6 +3781,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider { this.contentData = contentData; this.property = property; + this.isDirty = ContentData.hasContent(contentData); } /* (non-Javadoc) @@ -3814,6 +3828,15 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider return null; } + /** + * @return true if the contentData has a binary (content URL) associated and the updates on contentData and related properties should be saved. + * false if the contentData has a temporary value and no actual binary to be persisted. + */ + public boolean isDirty() + { + return this.isDirty; + } + /** * Set the content stream * @@ -3827,7 +3850,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.putContent(content); // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(true); } /** @@ -3844,7 +3867,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.putContent(content.getInputStream()); // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(true); } /** @@ -3885,7 +3908,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.putContent(is); // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(true); } /** @@ -3900,7 +3923,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.putContent(inputStream); // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(true); } /** @@ -3923,7 +3946,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider writer.setEncoding(null); // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(true); } /** @@ -3976,18 +3999,14 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider { this.contentData = ContentData.setEncoding(this.contentData, encoding); services.getNodeService().setProperty(nodeRef, this.property, this.contentData); - - // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(false); } public void setMimetype(String mimetype) { this.contentData = ContentData.setMimetype(this.contentData, mimetype); services.getNodeService().setProperty(nodeRef, this.property, this.contentData); - - // update cached variables after putContent() - this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + updateContentData(false); } /** @@ -4047,8 +4066,18 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider return encoding; } + /** + * Update cached contentData and the isDirty flag + */ + private void updateContentData(boolean touchContent) + { + this.contentData = (ContentData) services.getNodeService().getProperty(nodeRef, this.property); + this.isDirty = touchContent ? true : this.isDirty; + } + private ContentData contentData; private QName property; + private boolean isDirty; } /** diff --git a/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java b/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java index e6d8e0c7f2..01ec997687 100644 --- a/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java +++ b/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2014 Alfresco Software Limited. + * Copyright (C) 2005-2016 Alfresco Software Limited. * * This file is part of Alfresco * @@ -35,6 +35,7 @@ import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.dictionary.DictionaryRepositoryBootstrap; import org.alfresco.repo.dictionary.RepositoryLocation; import org.alfresco.repo.i18n.MessageService; +import org.alfresco.repo.jscript.ScriptNode.ScriptContentData; import org.alfresco.repo.model.Repository; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -47,6 +48,7 @@ import org.alfresco.repo.version.VersionableAspect; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; @@ -80,6 +82,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TestName; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ScriptableObject; /** @@ -545,4 +549,93 @@ public class ScriptNodeTest revertBootstrap(); } + + /** + * MNT-15798 - Content Data should be created only when it has a binary, not as a side effect of getters on ScriptNode. + */ + @Test + public void testContentDataCreation() + { + Repository repositoryHelper = (Repository) APP_CONTEXT_INIT.getApplicationContext().getBean("repositoryHelper"); + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + NodeRef newNode1 = testNodes.createNode(companyHome, "theTestContent1", ContentModel.TYPE_CONTENT, AuthenticationUtil.getFullyAuthenticatedUser()); + + // test on content data + ScriptNode sn = new ScriptNode(newNode1, SERVICE_REGISTRY); + sn.setScope(getScope()); + + ContentData contentData = (ContentData) NODE_SERVICE.getProperty(newNode1, ContentModel.PROP_CONTENT); + assertNull(contentData); + + sn.setMimetype(MimetypeMap.MIMETYPE_PDF); + sn.save(); + contentData = (ContentData) NODE_SERVICE.getProperty(newNode1, ContentModel.PROP_CONTENT); + assertNull(contentData); + + sn.setContent("Marks to prove it."); + sn.save(); + contentData = (ContentData) NODE_SERVICE.getProperty(newNode1, ContentModel.PROP_CONTENT); + assertNotNull(contentData); + assertEquals(true, ContentData.hasContent(contentData)); + + // test on ScriptContentData + NodeRef newNode2 = testNodes.createNode(companyHome, "theTestContent2.txt", ContentModel.TYPE_CONTENT, AuthenticationUtil.getFullyAuthenticatedUser()); + ScriptNode sn2 = new ScriptNode(newNode2, SERVICE_REGISTRY); + sn2.setScope(getScope()); + + ScriptContentData scd = sn2.new ScriptContentData(null, ContentModel.PROP_CONTENT); + //set the "mocked" script content data on the script node + sn2.getProperties().put(ContentModel.PROP_CONTENT.toString(), scd); + + assertEquals(false, scd.isDirty()); + + scd.guessMimetype("theTestContent2.pdf"); + assertEquals(false, scd.isDirty()); + + scd.setMimetype("text/plain"); + assertEquals(false, scd.isDirty()); + + scd.setEncoding("UTF-8"); + assertEquals(false, scd.isDirty()); + + sn2.save(); + contentData = (ContentData) NODE_SERVICE.getProperty(newNode2, ContentModel.PROP_CONTENT); + assertNull(contentData); + + scd.setContent("Marks to prove it."); + assertEquals(true, scd.isDirty()); + + scd.setEncoding("ISO-8859-1"); + assertEquals(true, scd.isDirty()); + + sn2.save(); + contentData = (ContentData) NODE_SERVICE.getProperty(newNode2, ContentModel.PROP_CONTENT); + assertNotNull(contentData); + + NODE_SERVICE.removeProperty(newNode1, ContentModel.PROP_CONTENT); + NODE_SERVICE.removeProperty(newNode2, ContentModel.PROP_CONTENT); + } + + private ScriptableObject getScope() + { + // Create a scope for the value conversion. This scope will be an empty scope exposing basic Object and Function, sufficient for value-conversion. + // In case no context is active for the current thread, we can safely enter end exit one to get hold of a scope + ScriptableObject scope; + Context ctx = Context.getCurrentContext(); + boolean closeContext = false; + if (ctx == null) + { + ctx = Context.enter(); + closeContext = true; + } + scope = ctx.initStandardObjects(); + scope.setParentScope(null); + + if (closeContext) + { + Context.exit(); + } + return scope; + } }