Author: rombert Date: Wed Oct 7 09:08:20 2015 New Revision: 1707209 URL: http://svn.apache.org/viewvc?rev=1707209&view=rev Log: SLING-3540 - maven-sling-plugin with usePut = true does not create intermediary folders
If usePut = true and installation fails with a 409 (Conflict) status attempt to create intermediate paths for deployment. The intermediate path primary type defaults to a Sling:Folder but can be configured using the intermediatePathPrimaryType mojo config property and overriden on the command line using -Dsling.deploy.intermediatePathPrimaryType. Added: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractor.java sling/trunk/tooling/maven/maven-sling-plugin/src/test/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractorTest.java Modified: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java Modified: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java?rev=1707209&r1=1707208&r2=1707209&view=diff ============================================================================== --- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java (original) +++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java Wed Oct 7 09:08:20 2015 @@ -32,6 +32,7 @@ import org.apache.commons.httpclient.Cre import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.FileRequestEntity; @@ -88,7 +89,13 @@ abstract class AbstractBundleInstallMojo */ @Parameter(property="sling.usePut", defaultValue = "false", required = true) protected boolean usePut; - + + /** + * The jcr:primaryType to be used when creating intermediate paths for HTTP PUT deployment + */ + @Parameter(property = "sling.deploy.intermediatePathPrimaryType" , defaultValue = "sling:Folder") + protected String intermediatePathPrimaryType; + /** * The content type / mime type used for the HTTP PUT (if * <code>sling.usePut=true</code>). @@ -137,7 +144,7 @@ abstract class AbstractBundleInstallMojo */ @Parameter(defaultValue = "${project}", required = true, readonly = true) protected MavenProject project; - + public AbstractBundleInstallMojo() { super(); } @@ -281,15 +288,26 @@ abstract class AbstractBundleInstallMojo protected void put(String targetURL, File file) throws MojoExecutionException { - PutMethod filePut = new PutMethod(getPutURL(targetURL, file.getName())); - + boolean success = false; + int status; + try { - filePut.setRequestEntity(new FileRequestEntity(file, mimeType)); - - int status = getHttpClient().executeMethod(filePut); + status = performPut(targetURL, file); if (status >= 200 && status < 300) { - getLog().info("Bundle installed"); - } else { + success = true; + } else if ( status == HttpStatus.SC_CONFLICT) { + + getLog().debug("Bundle not installed due missing parent folders. Attempting to create parent structure."); + createIntermediaryPaths(targetURL); + + getLog().debug("Re-attempting bundle install after creating parent folders."); + status = performPut(targetURL, file); + if (status >= 200 && status < 300) { + success = true; + } + } + + if ( !success ) { String msg = "Installation failed, cause: " + HttpStatus.getStatusText(status); if (failOnError) { @@ -301,11 +319,55 @@ abstract class AbstractBundleInstallMojo } catch (Exception ex) { throw new MojoExecutionException("Installation on " + targetURL + " failed, cause: " + ex.getMessage(), ex); + } + } + + private int performPut(String targetURL, File file) throws HttpException, IOException { + + PutMethod filePut = new PutMethod(getPutURL(targetURL, file.getName())); + try { + filePut.setRequestEntity(new FileRequestEntity(file, mimeType)); + return getHttpClient().executeMethod(filePut); } finally { filePut.releaseConnection(); } } + private void createIntermediaryPaths(String targetURL) throws HttpException, IOException, MojoExecutionException { + + for ( String intermediatePath : IntermediatePathsExtractor.extractIntermediatePaths(targetURL)) { + getLog().debug("Creating intermediate path at " + intermediatePath); + + // verify if the path exists by calling the JSON servlet + // this should always return a 200 OK status if it exists or a 4040 otherwise + GetMethod get = new GetMethod(intermediatePath + ".json"); + try { + int result = getHttpClient().executeMethod(get); + if ( result == HttpStatus.SC_OK ) { + getLog().debug("Path at " + intermediatePath + " already exists"); + continue; + } + } finally { + get.releaseConnection(); + } + + // create the path if it does not exist + PostMethod post = new PostMethod(intermediatePath); + try { + post.addParameter(new NameValuePair("jcr:primaryType", intermediatePathPrimaryType)); + int result = getHttpClient().executeMethod(post); + if ( result != HttpStatus.SC_CREATED && result != HttpStatus.SC_OK) { + throw new MojoExecutionException("Failed creating intermediate path at " + intermediatePath + "." + + " Reason: " + HttpStatus.getStatusText(result)); + } + getLog().info("Created intermediate path at " + intermediatePath + " as a " + intermediatePathPrimaryType); + } finally { + post.releaseConnection(); + } + } + + } + /** * Add configurations to a running OSGi instance for initial content. * @param targetURL The web console base url Added: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractor.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractor.java?rev=1707209&view=auto ============================================================================== --- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractor.java (added) +++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractor.java Wed Oct 7 09:08:20 2015 @@ -0,0 +1,74 @@ +/* + * 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.maven.bundlesupport; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +/** + * Helper which extracts intermediate paths from an URL + * + */ +abstract class IntermediatePathsExtractor { + + /** + * Extracts a list of intermediate paths from an URL. + * + * <p>For instance, <tt>http://localhost:8080/apps/slingshot/install</tt> would have the following intermediate + * paths: + * <ol> + * <li>/apps</li> + * <li>/apps/slingshot</li> + * <li>/apps/slingshot/install</li> + * </ol> + * </p> + * + * @param url the url to extract paths from + * @return the intermediate paths, possibly empty + */ + public static List<String> extractIntermediatePaths(String url) { + + List<String> paths = new ArrayList<String>(); + + URI uri = URI.create(url); + String path = uri.getPath(); + + StringBuilder accu = new StringBuilder(); + for ( String segment : path.split("/") ) { + + // ensure we have a trailing slash to join with the next segment + if ( accu.length() == 0 || accu.charAt(accu.length() - 1) != '/') { + accu.append('/'); + } + + accu.append(segment); + + // don't add the root segment ( / ) + if ( segment.length() != 0 ) { + paths.add(uri.resolve(accu.toString()).toString()); + } + + } + + return paths; + } + + private IntermediatePathsExtractor() { + + } +} Added: sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractorTest.java URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractorTest.java?rev=1707209&view=auto ============================================================================== --- sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractorTest.java (added) +++ sling/trunk/tooling/maven/maven-sling-plugin/src/test/java/org/apache/sling/maven/bundlesupport/IntermediatePathsExtractorTest.java Wed Oct 7 09:08:20 2015 @@ -0,0 +1,58 @@ +/* + * 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.maven.bundlesupport; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class IntermediatePathsExtractorTest { + + @Test + public void extractPaths() { + + doTest("http://localhost:8080/apps/slingshot/install", + Arrays.asList("http://localhost:8080/apps", "http://localhost:8080/apps/slingshot", "http://localhost:8080/apps/slingshot/install")); + } + + private void doTest(String input, List<String> expectedOutput) { + + List<String> paths = IntermediatePathsExtractor.extractIntermediatePaths(input); + + assertThat(paths, equalTo(expectedOutput)); + } + + @Test + public void extractPaths_trailingSlash() { + + doTest("http://localhost:8080/apps/slingshot/install/", + Arrays.asList("http://localhost:8080/apps", "http://localhost:8080/apps/slingshot", "http://localhost:8080/apps/slingshot/install")); + + } + + @Test + public void extractPaths_empty() { + + doTest("http://localhost:8080", Collections.<String> emptyList()); + } + +}