Author: enorman Date: Fri Aug 6 22:23:05 2010 New Revision: 983135 URL: http://svn.apache.org/viewvc?rev=983135&view=rev Log: SLING-1627 import operation support for overwrite of properties
Added: sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java (with props) sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/servlets/post/testimport_replaceProps.json Modified: sling/trunk/bundles/jcr/contentloader/pom.xml sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java sling/trunk/launchpad/integration-tests/src/main/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletImportTest.java Modified: sling/trunk/bundles/jcr/contentloader/pom.xml URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/pom.xml?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/pom.xml (original) +++ sling/trunk/bundles/jcr/contentloader/pom.xml Fri Aug 6 22:23:05 2010 @@ -161,6 +161,12 @@ <groupId>org.jmock</groupId> <artifactId>jmock-junit4</artifactId> </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-nop</artifactId> + <version>1.5.2</version> + <scope>test</scope> + </dependency> <!-- for security content loader (users/groups/acls) --> <dependency> Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java (original) +++ sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java Fri Aug 6 22:23:05 2010 @@ -24,10 +24,34 @@ package org.apache.sling.jcr.contentload */ public abstract class ImportOptions { + /** + * Specifies whether imported nodes should overwrite existing nodes. + * NOTE: this means the existing node will be deleted and a new node + * will be created in the same location. + * @return true to overwrite nodes, false otherwise + */ public abstract boolean isOverwrite(); + /** + * Specifies whether imported properties should overwrite existing properties. + * @return true to overwrite node properties, false otherwise + */ + public abstract boolean isPropertyOverwrite(); + + /** + * Specifies whether versionable nodes is automatically checked in at the + * end of the import operation. + * @return true to checkin the versionable nodes, false otherwise + */ public abstract boolean isCheckin(); + /** + * Check if the import provider for the given file extension should + * be ignored. + * + * @param extension the extension to check + * @return true to ignore the provider, false otherwise + */ public abstract boolean isIgnoredImportProvider(String extension); } \ No newline at end of file Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java (original) +++ sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java Fri Aug 6 22:23:05 2010 @@ -307,9 +307,10 @@ public class DefaultContentCreator imple public void createProperty(String name, int propertyType, String value) throws RepositoryException { final Node node = this.parentNodeStack.peek(); - // check if the property already exists, don't overwrite it in this case + // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case if (node.hasProperty(name) - && !node.getProperty(name).isNew()) { + && !this.configuration.isPropertyOverwrite() + && !node.getProperty(name).isNew()) { return; } @@ -364,8 +365,9 @@ public class DefaultContentCreator imple public void createProperty(String name, int propertyType, String[] values) throws RepositoryException { final Node node = this.parentNodeStack.peek(); - // check if the property already exists, don't overwrite it in this case + // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case if (node.hasProperty(name) + && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) { return; } Modified: sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java (original) +++ sling/trunk/bundles/jcr/contentloader/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java Fri Aug 6 22:23:05 2010 @@ -41,6 +41,12 @@ public class PathEntry extends ImportOpt */ public static final String OVERWRITE_DIRECTIVE = "overwrite"; + /** + * The overwriteProperties directive specifying if content properties + * should be overwritten or just initially added. + */ + public static final String OVERWRITE_PROPERTIES_DIRECTIVE = "overwriteProperties"; + /** The uninstall directive specifying if content should be uninstalled. */ public static final String UNINSTALL_DIRECTIVE = "uninstall"; @@ -75,6 +81,9 @@ public class PathEntry extends ImportOpt /** Should existing content be overwritten? */ private final boolean overwrite; + /** Should existing content properties be overwritten? */ + private final boolean overwriteProperties; + /** Should existing content be uninstalled? */ private final boolean uninstall; @@ -123,6 +132,14 @@ public class PathEntry extends ImportOpt this.overwrite = false; } + // overwriteProperties directive + final String overwritePropertiesValue = entry.getDirectiveValue(OVERWRITE_PROPERTIES_DIRECTIVE); + if (overwritePropertiesValue != null) { + this.overwriteProperties = Boolean.valueOf(overwritePropertiesValue); + } else { + this.overwriteProperties = false; + } + // uninstall directive final String uninstallValue = entry.getDirectiveValue(UNINSTALL_DIRECTIVE); if (uninstallValue != null) { @@ -177,7 +194,15 @@ public class PathEntry extends ImportOpt return this.overwrite; } - public boolean isUninstall() { + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite() + */ + @Override + public boolean isPropertyOverwrite() { + return this.overwriteProperties; + } + + public boolean isUninstall() { return this.uninstall; } Added: sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java?rev=983135&view=auto ============================================================================== --- sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java (added) +++ sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java Fri Aug 6 22:23:05 2010 @@ -0,0 +1,97 @@ +package org.apache.sling.jcr.contentloader.internal; + +import java.util.HashMap; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.apache.sling.jcr.contentloader.ImportOptions; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.integration.junit4.JMock; +import org.jmock.integration.junit4.JUnit4Mockery; +import org.junit.runner.RunWith; + +...@runwith(JMock.class) +public class DefaultContentCreatorTest { + + DefaultContentCreator contentCreator; + + Mockery mockery = new JUnit4Mockery(); + + Node parentNode; + + Property prop; + + @org.junit.Test public void willRewriteUndefinedPropertyType() throws RepositoryException { + contentCreator = new DefaultContentCreator(null); + parentNode = mockery.mock(Node.class); + prop = mockery.mock(Property.class); + contentCreator.init(new ImportOptions(){ + + @Override + public boolean isCheckin() { + return false; + } + + @Override + public boolean isIgnoredImportProvider(String extension) { + return false; + } + + @Override + public boolean isOverwrite() { + return true; + } + + @Override + public boolean isPropertyOverwrite() { + return true; + } }, new HashMap<String, ImportProvider>(), null, null); + + contentCreator.prepareParsing(parentNode, null); + this.mockery.checking(new Expectations() {{ + oneOf (parentNode).hasProperty("foo"); will(returnValue(Boolean.TRUE)); + oneOf (parentNode).setProperty(with(equal("foo")), with(equal("bar"))); + }}); + contentCreator.createProperty("foo", PropertyType.UNDEFINED, "bar"); + } + + @org.junit.Test public void willNotRewriteUndefinedPropertyType() throws RepositoryException { + contentCreator = new DefaultContentCreator(null); + parentNode = mockery.mock(Node.class); + prop = mockery.mock(Property.class); + contentCreator.init(new ImportOptions(){ + + @Override + public boolean isCheckin() { + return false; + } + + @Override + public boolean isIgnoredImportProvider(String extension) { + return false; + } + + @Override + public boolean isOverwrite() { + return false; + } + + @Override + public boolean isPropertyOverwrite() { + return false; + } }, new HashMap<String, ImportProvider>(), null, null); + + contentCreator.prepareParsing(parentNode, null); + this.mockery.checking(new Expectations() {{ + oneOf (parentNode).hasProperty("foo"); will(returnValue(Boolean.TRUE)); + oneOf (parentNode).getProperty("foo"); will(returnValue(prop)); + oneOf (prop).isNew(); will(returnValue(Boolean.FALSE)); + }}); + contentCreator.createProperty("foo", PropertyType.UNDEFINED, "bar"); + } + +} Propchange: sling/trunk/bundles/jcr/contentloader/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java (original) +++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java Fri Aug 6 22:23:05 2010 @@ -165,10 +165,22 @@ public interface SlingPostConstants { * copy or move operation is to be replaced if existing (value is * ":replace"). Copy or move is only possible if the destination exists if * the replace parameter is set to the case-insignificant value true. + * + * This request parameter is also used to indicate whether the destination node + * for an import operation is to be replaced if existing. The parameter value is + * checked to see if it matches the case-insignificant value true. */ public static final String RP_REPLACE = RP_PREFIX + "replace"; /** + * Name of the request parameter indicating whether the destination for a + * property change during an import operation is to be replaced if existing. + * The parameter value is checked to see if it matches the case-insignificant + * value true. + */ + public static final String RP_REPLACE_PROPERTIES = RP_PREFIX + "replaceProperties"; + + /** * Optional request parameter indicating the order of newly created nodes in * creation, copy and move operation requests (value is ":order"). * <p> Modified: sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java (original) +++ sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/ImportOperation.java Fri Aug 6 22:23:05 2010 @@ -106,8 +106,9 @@ public class ImportOperation extends Abs } //import options passed as request parameters. - final boolean replace = "true".equals(request.getParameter(SlingPostConstants.RP_REPLACE)); - final boolean checkin = "true".equals(request.getParameter(SlingPostConstants.RP_CHECKIN)); + final boolean replace = "true".equalsIgnoreCase(request.getParameter(SlingPostConstants.RP_REPLACE)); + final boolean replaceProperties = "true".equalsIgnoreCase(request.getParameter(SlingPostConstants.RP_REPLACE_PROPERTIES)); + final boolean checkin = "true".equalsIgnoreCase(request.getParameter(SlingPostConstants.RP_CHECKIN)); String basePath = getItemPath(request); basePath = removeAndValidateWorkspace(basePath, request.getResourceResolver().adaptTo(Session.class)); @@ -165,6 +166,14 @@ public class ImportOperation extends Abs public boolean isOverwrite() { return replace; } + + /* (non-Javadoc) + * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite() + */ + @Override + public boolean isPropertyOverwrite() { + return replaceProperties; + } }, new ContentImportListener() { Modified: sling/trunk/launchpad/integration-tests/src/main/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletImportTest.java URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/integration-tests/src/main/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletImportTest.java?rev=983135&r1=983134&r2=983135&view=diff ============================================================================== --- sling/trunk/launchpad/integration-tests/src/main/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletImportTest.java (original) +++ sling/trunk/launchpad/integration-tests/src/main/java/org/apache/sling/launchpad/webapp/integrationtest/servlets/post/PostServletImportTest.java Fri Aug 6 22:23:05 2010 @@ -111,7 +111,6 @@ public class PostServletImportTest exten } } - /** * Test import operation which replaces existing content */ @@ -153,6 +152,67 @@ public class PostServletImportTest exten } /** + * SLING-1627: test import of content over existing content with the ':replaceProperties" + * parameter set and the ":replace" property not set. + */ + public void testImportReplaceProperties() throws IOException, JSONException { + final String testPath = TEST_BASE_PATH; + Map<String, String> props = new HashMap<String, String>(); + String testNode = testClient.createNode(HTTP_BASE_URL + testPath, props); + urlsToDelete.add(testNode); + + //1. First import some initial content + props.clear(); + props.put(SlingPostConstants.RP_OPERATION, + SlingPostConstants.OPERATION_IMPORT); + + String testNodeName = "testNode_" + String.valueOf(random.nextInt()); + props.put(SlingPostConstants.RP_NODE_NAME_HINT, testNodeName); + String jsonContent = (String)getStreamAsString(getClass().getResourceAsStream("/integration-test/servlets/post/testimport.json")); + props.put(SlingPostConstants.RP_CONTENT, jsonContent); + props.put(SlingPostConstants.RP_CONTENT_TYPE, "json"); + props.put(SlingPostConstants.RP_REDIRECT_TO, testPath + "/*"); + String importedNodeUrl = testClient.createNode(HTTP_BASE_URL + testPath, props); + + // assert content at new location + String content = getContent(importedNodeUrl + ".3.json", CONTENT_TYPE_JSON); + + JSONObject jsonObj = new JSONObject(content); + assertNotNull(jsonObj); + + //assert the imported content is there. + String expectedJsonContent = (String)getStreamAsString(getClass().getResourceAsStream("/integration-test/servlets/post/importresults.json")); + assertExpectedJSON(new JSONObject(expectedJsonContent), jsonObj); + + + //2. Second, import on top of the node from #1 to replace some properties. + + props.clear(); + props.put(SlingPostConstants.RP_OPERATION, + SlingPostConstants.OPERATION_IMPORT); + + String jsonContent2 = (String)getStreamAsString(getClass().getResourceAsStream("/integration-test/servlets/post/testimport_replaceProps.json")); + + props.put(SlingPostConstants.RP_CONTENT, jsonContent2); + props.put(SlingPostConstants.RP_CONTENT_TYPE, "json"); + props.put(SlingPostConstants.RP_REDIRECT_TO, importedNodeUrl); + props.put(SlingPostConstants.RP_REPLACE, "false"); + props.put(SlingPostConstants.RP_REPLACE_PROPERTIES, "true"); + String importedNodeUrl2 = testClient.createNode(importedNodeUrl, props); + assertEquals(importedNodeUrl, importedNodeUrl2); + + // assert content at new location + String content2 = getContent(importedNodeUrl2 + ".3.json", CONTENT_TYPE_JSON); + + JSONObject jsonObj2 = new JSONObject(content2); + assertNotNull(jsonObj2); + + //assert the imported content is there. + String expectedJsonContent2 = (String)getStreamAsString(getClass().getResourceAsStream("/integration-test/servlets/post/testimport_replaceProps.json")); + assertExpectedJSON(new JSONObject(expectedJsonContent2), jsonObj2); + } + + /** * Test import operation which checks in versionable nodes. */ public void testImportCheckinNodes() throws IOException, JSONException { @@ -635,5 +695,6 @@ public class PostServletImportTest exten String postUrl = location.substring(0, location.lastIndexOf('/')); assertPostStatus(postUrl + SlingPostConstants.DEFAULT_CREATE_SUFFIX, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, postParams, null); } + } Added: sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/servlets/post/testimport_replaceProps.json URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/servlets/post/testimport_replaceProps.json?rev=983135&view=auto ============================================================================== --- sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/servlets/post/testimport_replaceProps.json (added) +++ sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/servlets/post/testimport_replaceProps.json Fri Aug 6 22:23:05 2010 @@ -0,0 +1,20 @@ +{ + "propOne" : "propOneValueChanged", + "childOne" : { + "childPropOne" : false + }, + "propIntArray" : [ + 6, + 7, + 8, + 9 + ], + "propBoolArray" : [ + false, + true, + true, + true, + false, + true + ] +}