Author: rombert Date: Thu Oct 9 12:27:42 2014 New Revision: 1630401 URL: http://svn.apache.org/r1630401 Log: SLING-3586 - Publishing content fails if intermediate nodes are not present in the repository
Ensure that when a resource is published all its parents, up to the repository root, are created. Added: sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/NtFileContentMatcher.java Modified: sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingLaunchpadBehaviour.java sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentDeploymentTest.java sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/JcrMatchers.java Modified: sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingLaunchpadBehaviour.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingLaunchpadBehaviour.java?rev=1630401&r1=1630400&r2=1630401&view=diff ============================================================================== --- sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingLaunchpadBehaviour.java (original) +++ sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/SlingLaunchpadBehaviour.java Thu Oct 9 12:27:42 2014 @@ -21,7 +21,9 @@ import java.io.InputStream; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.sling.ide.artifacts.EmbeddedArtifact; @@ -379,6 +381,9 @@ public class SlingLaunchpadBehaviour ext // would be an incorrect ( or at least suprising ) behaviour at development time List<IModuleResource> addedOrUpdatedResources = new ArrayList<IModuleResource>(); + IModuleResource[] allResources = getResources(module); + IModuleResource[] publishedResources = getPublishedResources(module); + Set<IPath> handledPaths = new HashSet<IPath>(); switch (deltaKind) { case ServerBehaviourDelegate.CHANGED: @@ -412,6 +417,8 @@ public class SlingLaunchpadBehaviour ext case IModuleResourceDelta.ADDED: case IModuleResourceDelta.CHANGED: case IModuleResourceDelta.NO_CHANGE: // TODO is this needed? + ensureParentIsPublished(resourceDelta.getModuleResource(), repository, allResources, + publishedResources, handledPaths); execute(addFileCommand(repository, resourceDelta.getModuleResource())); addedOrUpdatedResources.add(resourceDelta.getModuleResource()); break; @@ -448,6 +455,73 @@ public class SlingLaunchpadBehaviour ext // setServerPublishState(IServer.PUBLISH_STATE_NONE); } + /** + * Ensures that the parent of this resource has been published to the repository + * + * <p> + * Note that the parents explicitly do not have their child nodes reordered, this will happen when they are + * published due to a resource change + * </p> + * + * @param moduleResource the current resource + * @param repository the repository to publish to + * @param allResources all of the module's resources + * @param publishedResources the resources which have been already published + * @param handledPaths the paths that have been handled already in this publish operation, but possibly not + * registered as published + * @throws IOException + * @throws SerializationException + * @throws CoreException + */ + private void ensureParentIsPublished(IModuleResource moduleResource, Repository repository, + IModuleResource[] allResources, IModuleResource[] publishedResources, Set<IPath> handledPaths) + throws CoreException, SerializationException, IOException { + + Logger logger = Activator.getDefault().getPluginLogger(); + + IPath currentPath = moduleResource.getModuleRelativePath(); + + logger.trace("Ensuring that parent of path {0} is published", currentPath); + + // we assume the root is always published + if (currentPath.segmentCount() == 0) { + logger.trace("Path {0} can not have a parent, skipping", currentPath); + return; + } + + IPath parentPath = currentPath.removeLastSegments(1); + + // already published by us, a parent of another resource that was published in this execution + if (handledPaths.contains(parentPath)) { + logger.trace("Parent path {0} was already handled, skipping", parentPath); + return; + } + + // already published earlier as part of the publish job + for (IModuleResource alreadyPublished : publishedResources) { + if (alreadyPublished.getModuleRelativePath().equals(parentPath)) { + logger.trace("Parent path {0} was already published, skipping", parentPath); + return; + } + } + + for (IModuleResource maybeParent : allResources) { + if (maybeParent.getModuleRelativePath().equals(parentPath)) { + // handle the parent's parent first, if needed + ensureParentIsPublished(maybeParent, repository, allResources, publishedResources, handledPaths); + // create this resource + execute(addFileCommand(repository, maybeParent)); + handledPaths.add(maybeParent.getModuleRelativePath()); + logger.trace("Ensured that resource at path {0} is published", parentPath); + return; + } + } + + throw new IllegalArgumentException("Resource at " + moduleResource.getModuleRelativePath() + + " has parent path " + parentPath + " but no resource with that path is in the module's resources."); + + } + private void execute(Command<?> command) throws CoreException { if (command == null) { return; Modified: sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentDeploymentTest.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentDeploymentTest.java?rev=1630401&r1=1630400&r2=1630401&view=diff ============================================================================== --- sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentDeploymentTest.java (original) +++ sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentDeploymentTest.java Thu Oct 9 12:27:42 2014 @@ -17,6 +17,7 @@ package org.apache.sling.ide.test.impl; import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasChildrenCount; +import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasFileContent; import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPath; import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPrimaryType; import static org.apache.sling.ide.test.impl.helpers.jcr.JcrMatchers.hasPropertyValue; @@ -49,7 +50,6 @@ import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.JavaCore; import org.hamcrest.Matcher; import org.junit.After; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -235,8 +235,13 @@ public class ContentDeploymentTest { } + /** + * This test validates that if the parent of a resource that does not exist in the repository the resource is + * successfully created + * + * @throws Exception + */ @Test - @Ignore("SLING-3586") public void deployFileWithMissingParentFromRepository() throws Exception { wstServer.waitForServerToStart(); @@ -254,7 +259,7 @@ public class ContentDeploymentTest { server.installModule(contentProject); // create filter.xml - project.createVltFilterWithRoots("/test/demo/nested/structure"); + project.createVltFilterWithRoots("/test"); // create file project.createOrUpdateFile(Path.fromPortableString("jcr_root/test/demo/nested/structure/hello.txt"), new ByteArrayInputStream("hello, world".getBytes())); @@ -267,7 +272,7 @@ public class ContentDeploymentTest { public Node call() throws RepositoryException { return repo.getNode("/test/demo/nested/structure/hello.txt"); } - }, nullValue(Node.class)); + }, hasFileContent("hello, world")); } private void assertThatNode(final RepositoryAccessor repo, Poller poller, final String nodePath, Matcher<Node> matcher) Modified: sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/JcrMatchers.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/JcrMatchers.java?rev=1630401&r1=1630400&r2=1630401&view=diff ============================================================================== --- sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/JcrMatchers.java (original) +++ sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/JcrMatchers.java Thu Oct 9 12:27:42 2014 @@ -46,6 +46,10 @@ public final class JcrMatchers { return new PropertyMatcher(propertyName, propertyValue); } + public static Matcher<Node> hasFileContent(String value) { + return new NtFileContentMatcher(value); + } + private JcrMatchers() { } Added: sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/NtFileContentMatcher.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/NtFileContentMatcher.java?rev=1630401&view=auto ============================================================================== --- sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/NtFileContentMatcher.java (added) +++ sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/helpers/jcr/NtFileContentMatcher.java Thu Oct 9 12:27:42 2014 @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.ide.test.impl.helpers.jcr; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +/** + * The <tt>NtFileContentMatcher</tt> verifies the content of an <tt>nt:file</tt> node. + * + * <p> + * However, this matcher does not verify the actual type of the node, just the value of the jcr:content/jcr:data + * property. + * </p> + * + */ +public class NtFileContentMatcher extends TypeSafeMatcher<Node> { + + private final String value; + + public NtFileContentMatcher(String value) { + this.value = value; + } + + @Override + public void describeTo(Description description) { + description.appendText("node[jcr:content][jcr:data] = ").appendValue(value); + } + + @Override + public boolean matchesSafely(Node item) { + try { + if (item == null || !item.hasNode("jcr:content")) { + return false; + } + + Node jcrContent = item.getNode("jcr:content"); + + return jcrContent.hasProperty("jcr:data") && jcrContent.getProperty("jcr:data").getString().equals(value); + } catch (RepositoryException e) { + return false; + } + } + + @Override + protected void describeMismatchSafely(Node item, Description mismatchDescription) { + try { + if (!item.hasNode("jcr:content")) { + mismatchDescription.appendValue("was node with a jcr:content child node"); + return; + } + + Node jcrContent = item.getNode("jcr:content"); + + if (jcrContent.hasProperty("jcr:data")) { + mismatchDescription.appendText("was node [jcr:content] = ").appendValue( + item.getProperty("jcr:data").getString()); + } else { + mismatchDescription.appendText("was node without a property named jcr:data"); + } + } catch (RepositoryException e) { + super.describeMismatchSafely(item, mismatchDescription); + } + } +}