You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2017/03/08 16:11:54 UTC
svn commit: r1785998 -
/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/
Author: sseifert
Date: Wed Mar 8 16:11:54 2017
New Revision: 1785998
URL: http://svn.apache.org/viewvc?rev=1785998&view=rev
Log:
SLING-6622 maven-sling-plugin: Add "fsmount" and "fsunmount" goals
move fsresoure logic do separate class FsMountHelper
remove obsolete method checkWebConsoleVersion
Added:
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java (with props)
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java (with props)
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java (with props)
Modified:
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundleInstallMojo.java
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundlePostMojo.java
sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/BundleUninstallMojo.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=1785998&r1=1785997&r2=1785998&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 Mar 8 16:11:54 2017
@@ -21,25 +21,11 @@ package org.apache.sling.maven.bundlesup
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-
-import javax.json.JsonArray;
-import javax.json.JsonException;
-import javax.json.JsonObject;
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.UsernamePasswordCredentials;
-import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.FileRequestEntity;
-import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
@@ -48,40 +34,14 @@ import org.apache.commons.httpclient.met
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
-import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
-import org.apache.sling.commons.osgi.ManifestHeader;
-import org.apache.sling.commons.osgi.ManifestHeader.Entry;
abstract class AbstractBundleInstallMojo extends AbstractBundlePostMojo {
- /** Header containing the sling initial content information. */
- private static final String HEADER_INITIAL_CONTENT = "Sling-Initial-Content";
- /** The fs resource provider factory. */
- private static final String FS_FACTORY = "org.apache.sling.fsprovider.internal.FsResourceProvider";
/** Mime type for json response. */
- protected static final String JSON_MIME_TYPE = "application/json";
- /** Http header for content type. */
- private static final String HEADER_CONTENT_TYPE = "Content-Type";
-
- /**
- * The URL of the running Sling instance. The default is only useful for <strong>WebConsole</strong> deployment (see {@link #deploymentMethod}).
- */
- @Parameter(property="sling.url", defaultValue="http://localhost:8080/system/console", required = true)
- protected String slingUrl;
-
- /**
- * An optional url suffix which will be appended to the <code>sling.url</code>
- * for use as the real target url. This allows to configure different target URLs
- * in each POM, while using the same common <code>sling.url</code> in a parent
- * POM (eg. <code>sling.url=http://localhost:8080</code> and
- * <code>sling.urlSuffix=/project/specific/path</code>). This is typically used
- * in conjunction with WebDAV or SlingPostServlet deployment methods.
- */
- @Parameter(property="sling.urlSuffix")
- protected String slingUrlSuffix;
+ public static final String JSON_MIME_TYPE = "application/json";
/**
* If a PUT via WebDAV should be used instead of the standard POST to the
@@ -134,18 +94,6 @@ abstract class AbstractBundleInstallMojo
protected String mimeType;
/**
- * The user name to authenticate at the running Sling instance.
- */
- @Parameter(property="sling.user", defaultValue = "admin", required = true)
- private String user;
-
- /**
- * The password to authenticate at the running Sling instance.
- */
- @Parameter(property="sling.password", defaultValue = "admin", required = true)
- private String password;
-
- /**
* The startlevel for the uploaded bundle. Only applies when POSTing to
* Felix Web Console.
*/
@@ -185,18 +133,6 @@ abstract class AbstractBundleInstallMojo
protected abstract String getBundleFileName() throws MojoExecutionException;
/**
- * Returns the combination of <code>sling.url</code> and
- * <code>sling.urlSuffix</code>.
- */
- protected String getTargetURL() {
- String targetURL = slingUrl;
- if (slingUrlSuffix != null) {
- targetURL += slingUrlSuffix;
- }
- return targetURL;
- }
-
- /**
* Returns the URL with the filename appended to it.
* @param targetURL the original requested targetURL to append fileName to
* @param fileName the name of the file to append to the targetURL.
@@ -252,6 +188,11 @@ abstract class AbstractBundleInstallMojo
configure(targetURL, bundleFile);
}
}
+
+ protected void configure(final String targetURL, final File file) throws MojoExecutionException {
+ FsMountHelper fsMountHelper = new FsMountHelper(getLog(), getHttpClient(), project);
+ fsMountHelper.configureInstall(targetURL, file);
+ }
/**
* Retrieve the bundle deployment method matching the configuration.
@@ -271,35 +212,6 @@ abstract class AbstractBundleInstallMojo
}
/**
- * Helper method to throw a meaningful exception for an outdated felix
- * web console.
- * @throws MojoExecutionException
- */
- protected void throwWebConsoleTooOldException()
- throws MojoExecutionException {
- throw new MojoExecutionException("The Apache Felix Web Console is too old to mount " +
- "the initial content through file system provider configs. " +
- "Either upgrade the web console or disable this feature.");
- }
-
- /**
- * Get the http client
- */
- protected HttpClient getHttpClient() {
- final HttpClient client = new HttpClient();
- client.getHttpConnectionManager().getParams().setConnectionTimeout(
- 5000);
-
- // authentication stuff
- client.getParams().setAuthenticationPreemptive(true);
- Credentials defaultcreds = new UsernamePasswordCredentials(user,
- password);
- client.getState().setCredentials(AuthScope.ANY, defaultcreds);
-
- return client;
- }
-
- /**
* Install the bundle via POST to the Felix Web Console
* @param targetURL the URL to the Felix Web Console Bundles listing
* @param file the file to POST
@@ -525,317 +437,4 @@ abstract class AbstractBundleInstallMojo
}
}
- /**
- * Add configurations to a running OSGi instance for initial content.
- * @param targetURL The web console base url
- * @param file The artifact (bundle)
- * @throws MojoExecutionException
- */
- protected void configure(String targetURL, File file)
- throws MojoExecutionException {
- // first, let's get the manifest and see if initial content is configured
- ManifestHeader header = null;
- try {
- final Manifest mf = this.getManifest(file);
- final String value = mf.getMainAttributes().getValue(HEADER_INITIAL_CONTENT);
- if ( value == null ) {
- getLog().debug("Bundle has no initial content - no file system provider config created.");
- return;
- }
- header = ManifestHeader.parse(value);
- if ( header == null || header.getEntries().length == 0 ) {
- getLog().warn("Unable to parse header or header is empty: " + value);
- return;
- }
- } catch (IOException ioe) {
- throw new MojoExecutionException("Unable to read manifest from file " + file, ioe);
- }
- // setup http client
- final HttpClient client = getHttpClient();
-
- getLog().info("Trying to configure file system provider...");
- // quick check if resources are configured
- final List resources = project.getResources();
- if ( resources == null || resources.size() == 0 ) {
- throw new MojoExecutionException("No resources configured for this project.");
- }
- // now get current configurations
- final Map<String,String[]> oldConfigs = this.getCurrentFileProviderConfigs(targetURL, client);
-
- final Entry[] entries = header.getEntries();
- for(final Entry entry : entries) {
- String path = entry.getValue();
- if ( path != null && !path.endsWith("/") ) {
- path += "/";
- }
- // check if we should ignore this
- final String ignoreValue = entry.getDirectiveValue("maven:mount");
- if ( ignoreValue != null && ignoreValue.equalsIgnoreCase("false") ) {
- getLog().debug("Ignoring " + path);
- continue;
- }
- String installPath = entry.getDirectiveValue("path");
- if ( installPath == null ) {
- installPath = "/";
- }
- // search the path in the resources (usually this should be the first resource
- // entry but this might be reconfigured
- File dir = null;
- final Iterator i = resources.iterator();
- while ( dir == null && i.hasNext() ) {
- final Resource rsrc = (Resource)i.next();
- String child = path;
- // if resource mapping defines a target path: remove target path from checked resource path
- String targetPath = rsrc.getTargetPath();
- if ( targetPath != null && !targetPath.endsWith("/") ) {
- targetPath = targetPath + "/";
- }
- if ( targetPath != null && path.startsWith(targetPath) ) {
- child = child.substring(targetPath.length());
- }
- dir = new File(rsrc.getDirectory(), child);
- if ( !dir.exists() ) {
- dir = null;
- }
- }
- if ( dir == null ) {
- throw new MojoExecutionException("No resource entry found containing " + path);
- }
- // check for root mapping - which we don't support atm
- if ( "/".equals(installPath) ) {
- throw new MojoExecutionException("Mapping to root path not supported by fs provider at the moment. Please adapt your initial content configuration.");
- }
- getLog().info("Mapping " + dir + " to " + installPath);
-
- // check if this is already configured
- boolean found = false;
- final Iterator<Map.Entry<String,String[]>> entryIterator = oldConfigs.entrySet().iterator();
- while ( !found && entryIterator.hasNext() ) {
- final Map.Entry<String,String[]> current = entryIterator.next();
- final String[] value = current.getValue();
- getLog().debug("Comparing " + dir.getAbsolutePath() + " with " + value[0] + " (" + value[1] + ")");
- if ( dir.getAbsolutePath().equals(value[0]) ) {
- if ( installPath.equals(value[1]) ) {
- getLog().debug("Using existing configuration for " + dir + " and " + installPath);
- found = true;
- } else {
- // remove old config
- getLog().debug("Removing old configuration for " + value[0] + " and " + value[1]);
- removeConfiguration(client, targetURL, current.getKey().toString());
- }
- entryIterator.remove();
- }
- }
- if ( !found ) {
- getLog().debug("Adding new configuration for " + dir + " and " + installPath);
- addConfiguration(client, targetURL, dir.getAbsolutePath(), installPath);
- }
- }
- // finally remove old configs
- final Iterator<Map.Entry<String,String[]>> entryIterator = oldConfigs.entrySet().iterator();
- while ( entryIterator.hasNext() ) {
- final Map.Entry<String,String[]> current = entryIterator.next();
- final String[] value = current.getValue();
- getLog().debug("Removing old configuration for " + value[0] + " and " + value[1]);
- // remove old config
- removeConfiguration(client, targetURL, current.getKey().toString());
- }
- }
-
- protected void removeConfiguration(final HttpClient client, final String targetURL, String pid)
- throws MojoExecutionException {
- final String postUrl = targetURL + "/configMgr/" + pid;
- final PostMethod post = new PostMethod(postUrl);
- post.addParameter("apply", "true");
- post.addParameter("delete", "true");
- try {
- final int status = client.executeMethod(post);
- // we get a moved temporarily back from the configMgr plugin
- if (status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_OK) {
- getLog().debug("Configuration removed.");
- } else {
- getLog().error(
- "Removing configuration failed, cause: "
- + HttpStatus.getStatusText(status));
- }
- } catch (HttpException ex) {
- throw new MojoExecutionException("Removing configuration at " + postUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } catch (IOException ex) {
- throw new MojoExecutionException("Removing configuration at " + postUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } finally {
- post.releaseConnection();
- }
- }
-
- /**
- * Add a new configuration for the file system provider
- * @throws MojoExecutionException
- */
- protected void addConfiguration(final HttpClient client, final String targetURL, String dir, String path)
- throws MojoExecutionException {
- final String postUrl = targetURL + "/configMgr/" + FS_FACTORY;
- final PostMethod post = new PostMethod(postUrl);
- post.addParameter("apply", "true");
- post.addParameter("factoryPid", FS_FACTORY);
- post.addParameter("pid", "[Temporary PID replaced by real PID upon save]");
- post.addParameter("provider.file", dir);
- // save property value to both "provider.roots" and "provider.root" because the name has changed since fsresource 1.1.6
- post.addParameter("provider.root", path);
- post.addParameter("provider.roots", path);
- post.addParameter("propertylist", "provider.root,provider.roots,provider.file");
- try {
- final int status = client.executeMethod(post);
- // we get a moved temporarily back from the configMgr plugin
- if (status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_OK) {
- getLog().info("Configuration created.");
- } else {
- getLog().error(
- "Configuration failed, cause: "
- + HttpStatus.getStatusText(status));
- }
- } catch (HttpException ex) {
- throw new MojoExecutionException("Configuration on " + postUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } catch (IOException ex) {
- throw new MojoExecutionException("Configuration on " + postUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } finally {
- post.releaseConnection();
- }
- }
-
- /**
- * Return all file provider configs for this project
- * @param targetURL The targetURL of the webconsole
- * @param client The http client
- * @return A map (may be empty) with the pids as keys and a string array
- * containing the path and the root
- * @throws MojoExecutionException
- */
- protected Map<String,String[]> getCurrentFileProviderConfigs(final String targetURL, final HttpClient client)
- throws MojoExecutionException {
- getLog().debug("Getting current file provider configurations.");
- final Map<String,String[]> result = new HashMap<>();
- final String getUrl = targetURL + "/configMgr/(service.factoryPid=" + FS_FACTORY + ").json";
- final GetMethod get = new GetMethod(getUrl);
-
- try {
- final int status = client.executeMethod(get);
- if ( status == 200 ) {
- String contentType = get.getResponseHeader(HEADER_CONTENT_TYPE).getValue();
- int pos = contentType.indexOf(';');
- if ( pos != -1 ) {
- contentType = contentType.substring(0, pos);
- }
- if ( !JSON_MIME_TYPE.equals(contentType) ) {
- getLog().debug("Response type from web console is not JSON, but " + contentType);
- throwWebConsoleTooOldException();
- }
- final String jsonText = get.getResponseBodyAsString();
- try {
- JsonArray array = JsonSupport.parseArray(jsonText);
- for(int i=0; i<array.size(); i++) {
- final JsonObject obj = array.getJsonObject(i);
- final String pid = obj.getString("pid");
- final JsonObject properties = obj.getJsonObject("properties");
- final String path = properties.getJsonObject("provider.file").getString("value", null);
- final String roots;
- if (properties.containsKey("provider.roots")) {
- roots = properties.getJsonObject("provider.roots").getString("value", null);
- }
- else {
- roots = properties.getJsonObject("provider.root").getString("value", null);
- }
- if ( path != null && path.startsWith(this.project.getBasedir().getAbsolutePath()) && roots != null ) {
- getLog().debug("Found configuration with pid: " + pid + ", path: " + path + ", roots: " + roots);
- result.put(pid, new String[] {path, roots});
- }
- }
- } catch (JsonException ex) {
- throw new MojoExecutionException("Reading configuration from " + getUrl
- + " failed, cause: " + ex.getMessage(), ex);
- }
- }
- } catch (HttpException ex) {
- throw new MojoExecutionException("Reading configuration from " + getUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } catch (IOException ex) {
- throw new MojoExecutionException("Reading configuration from " + getUrl
- + " failed, cause: " + ex.getMessage(), ex);
- } finally {
- get.releaseConnection();
- }
- return result;
- }
-
- /**
- * Get the manifest from the File.
- * @param bundleFile The bundle jar
- * @return The manifest.
- * @throws IOException
- */
- protected Manifest getManifest(final File bundleFile) throws IOException {
- JarFile file = null;
- try {
- file = new JarFile(bundleFile);
- return file.getManifest();
- } finally {
- if (file != null) {
- try {
- file.close();
- } catch (IOException ignore) {
- }
- }
- }
- }
-
- /**
- * Try to get the version of the web console
- * @return The version or <code>null</code> if version is not detectable.
- */
- protected String checkWebConsoleVersion(final String targetUrl) {
- getLog().debug("Checking web console version....");
- final String bundleUrl = targetUrl + "/bundles/org.apache.felix.webconsole.json";
- final HttpClient client = getHttpClient();
- final GetMethod gm = new GetMethod(bundleUrl);
- // if something goes wrong, we assume an older version!!
- try {
- final int status = client.executeMethod(gm);
- if ( status == 200 ) {
- if ( gm.getResponseContentLength() == 0 ) {
- getLog().debug("Response has zero length. Assuming older version of web console.");
- return null;
- }
- final String jsonText = gm.getResponseBodyAsString();
- try {
- final JsonObject obj = JsonSupport.parseObject(jsonText);
- final JsonArray props = obj.getJsonArray("props");
- for(int i=0; i<props.size(); i++) {
- final JsonObject property = props.getJsonObject(i);
- if ( "Version".equals(property.get("key")) ) {
- final String version = property.getString("value");
- getLog().debug("Found web console version " + version);
- return version;
- }
- }
- getLog().debug("Version property not found in response. Assuming older version.");
- return null;
- } catch (JsonException ex) {
- getLog().debug("Converting response to JSON failed. Assuming older version: " + ex.getMessage());
- return null;
- }
-
- }
- getLog().debug("Status code from web console: " + status);
- } catch (HttpException e) {
- getLog().debug("HttpException: " + e.getMessage());
- } catch (IOException e) {
- getLog().debug("IOException: " + e.getMessage());
- }
-
- getLog().debug("Unknown version.");
- return null;
- }
-}
\ No newline at end of file
+}
Modified: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundlePostMojo.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundlePostMojo.java?rev=1785998&r1=1785997&r2=1785998&view=diff
==============================================================================
--- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundlePostMojo.java (original)
+++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/AbstractBundlePostMojo.java Wed Mar 8 16:11:54 2017
@@ -23,12 +23,45 @@ import java.io.IOException;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.UsernamePasswordCredentials;
+import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Parameter;
abstract class AbstractBundlePostMojo extends AbstractMojo {
/**
+ * The URL of the running Sling instance. The default is only useful for <strong>WebConsole</strong> deployment (see {@link #deploymentMethod}).
+ */
+ @Parameter(property="sling.url", defaultValue="http://localhost:8080/system/console", required = true)
+ protected String slingUrl;
+
+ /**
+ * An optional url suffix which will be appended to the <code>sling.url</code>
+ * for use as the real target url. This allows to configure different target URLs
+ * in each POM, while using the same common <code>sling.url</code> in a parent
+ * POM (eg. <code>sling.url=http://localhost:8080</code> and
+ * <code>sling.urlSuffix=/project/specific/path</code>). This is typically used
+ * in conjunction with WebDAV or SlingPostServlet deployment methods.
+ */
+ @Parameter(property="sling.urlSuffix")
+ protected String slingUrlSuffix;
+
+ /**
+ * The user name to authenticate at the running Sling instance.
+ */
+ @Parameter(property="sling.user", defaultValue = "admin", required = true)
+ private String user;
+
+ /**
+ * The password to authenticate at the running Sling instance.
+ */
+ @Parameter(property="sling.password", defaultValue = "admin", required = true)
+ private String password;
+
+ /**
* Determines whether or not to fail the build if
* the HTTP POST or PUT returns an non-OK response code.
*/
@@ -98,4 +131,33 @@ abstract class AbstractBundlePostMojo ex
return null;
}
+ /**
+ * Returns the combination of <code>sling.url</code> and
+ * <code>sling.urlSuffix</code>.
+ */
+ protected String getTargetURL() {
+ String targetURL = slingUrl;
+ if (slingUrlSuffix != null) {
+ targetURL += slingUrlSuffix;
+ }
+ return targetURL;
+ }
+
+ /**
+ * Get the http client
+ */
+ protected HttpClient getHttpClient() {
+ final HttpClient client = new HttpClient();
+ client.getHttpConnectionManager().getParams().setConnectionTimeout(
+ 5000);
+
+ // authentication stuff
+ client.getParams().setAuthenticationPreemptive(true);
+ Credentials defaultcreds = new UsernamePasswordCredentials(user,
+ password);
+ client.getState().setCredentials(AuthScope.ANY, defaultcreds);
+
+ return client;
+ }
+
}
Modified: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/BundleUninstallMojo.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/BundleUninstallMojo.java?rev=1785998&r1=1785997&r2=1785998&view=diff
==============================================================================
--- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/BundleUninstallMojo.java (original)
+++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/BundleUninstallMojo.java Wed Mar 8 16:11:54 2017
@@ -19,10 +19,7 @@
package org.apache.sling.maven.bundlesupport;
import java.io.File;
-import java.util.Iterator;
-import java.util.Map;
-import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.PostMethod;
@@ -165,30 +162,10 @@ public class BundleUninstallMojo extends
}
}
- /**
- * Add configurations to a running OSGi instance for initial content.
- * @param targetURL The web console base url
- * @param file The artifact (bundle)
- * @throws MojoExecutionException
- */
@Override
- protected void configure(String targetURL, File file)
- throws MojoExecutionException {
- getLog().info("Removing file system provider configurations...");
-
- // now get current configurations
- final HttpClient client = this.getHttpClient();
- final Map oldConfigs = this.getCurrentFileProviderConfigs(targetURL, client);
-
-
- final Iterator entryIterator = oldConfigs.entrySet().iterator();
- while ( entryIterator.hasNext() ) {
- final Map.Entry current = (Map.Entry) entryIterator.next();
- final String[] value = (String[])current.getValue();
- getLog().debug("Removing old configuration for " + value[0] + " and " + value[1]);
- // remove old config
- removeConfiguration(client, targetURL, current.getKey().toString());
- }
+ protected void configure(final String targetURL, final File file) throws MojoExecutionException {
+ FsMountHelper fsMountHelper = new FsMountHelper(getLog(), getHttpClient(), project);
+ fsMountHelper.configureUninstall(targetURL, file);
}
-
+
}
\ No newline at end of file
Added: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java?rev=1785998&view=auto
==============================================================================
--- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java (added)
+++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java Wed Mar 8 16:11:54 2017
@@ -0,0 +1,393 @@
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.CharEncoding;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.apache.sling.commons.osgi.ManifestHeader;
+import org.apache.sling.commons.osgi.ManifestHeader.Entry;
+
+/**
+ * Manages OSGi configurations for File System Resource Provider.
+ */
+class FsMountHelper {
+
+ /** Header containing the sling initial content information. */
+ private static final String HEADER_INITIAL_CONTENT = "Sling-Initial-Content";
+ /** The fs resource provider factory. */
+ private static final String FS_FACTORY = "org.apache.sling.fsprovider.internal.FsResourceProvider";
+ /** Http header for content type. */
+ private static final String HEADER_CONTENT_TYPE = "Content-Type";
+
+ private final Log log;
+ private final HttpClient httpClient;
+ private final MavenProject project;
+
+ public FsMountHelper(Log log, HttpClient httpClient, MavenProject project) {
+ this.log = log;
+ this.httpClient = httpClient;
+ this.project = project;
+ }
+
+ /**
+ * Add configurations to a running OSGi instance for initial content.
+ * @param targetURL The web console base url
+ * @param file The artifact (bundle)
+ * @throws MojoExecutionException
+ */
+ public void configureInstall(final String targetURL, final File file) throws MojoExecutionException {
+ // first, let's get the manifest and see if initial content is configured
+ ManifestHeader header = null;
+ try {
+ final Manifest mf = getManifest(file);
+ final String value = mf.getMainAttributes().getValue(HEADER_INITIAL_CONTENT);
+ if ( value == null ) {
+ log.debug("Bundle has no initial content - no file system provider config created.");
+ return;
+ }
+ header = ManifestHeader.parse(value);
+ if ( header == null || header.getEntries().length == 0 ) {
+ log.warn("Unable to parse header or header is empty: " + value);
+ return;
+ }
+ } catch (IOException ioe) {
+ throw new MojoExecutionException("Unable to read manifest from file " + file, ioe);
+ }
+
+ log.info("Trying to configure file system provider...");
+ // quick check if resources are configured
+ final List resources = project.getResources();
+ if ( resources == null || resources.size() == 0 ) {
+ throw new MojoExecutionException("No resources configured for this project.");
+ }
+ // now get current configurations
+ final Map<String,String[]> oldConfigs = getCurrentFileProviderConfigs(targetURL);
+
+ final Entry[] entries = header.getEntries();
+ for (final Entry entry : entries) {
+ String path = entry.getValue();
+ if ( path != null && !path.endsWith("/") ) {
+ path += "/";
+ }
+ // check if we should ignore this
+ final String ignoreValue = entry.getDirectiveValue("maven:mount");
+ if ( ignoreValue != null && ignoreValue.equalsIgnoreCase("false") ) {
+ log.debug("Ignoring " + path);
+ continue;
+ }
+ String installPath = entry.getDirectiveValue("path");
+ if ( installPath == null ) {
+ installPath = "/";
+ }
+ // search the path in the resources (usually this should be the first resource
+ // entry but this might be reconfigured
+ File dir = null;
+ final Iterator i = resources.iterator();
+ while ( dir == null && i.hasNext() ) {
+ final Resource rsrc = (Resource)i.next();
+ String child = path;
+ // if resource mapping defines a target path: remove target path from checked resource path
+ String targetPath = rsrc.getTargetPath();
+ if ( targetPath != null && !targetPath.endsWith("/") ) {
+ targetPath = targetPath + "/";
+ }
+ if ( targetPath != null && path.startsWith(targetPath) ) {
+ child = child.substring(targetPath.length());
+ }
+ dir = new File(rsrc.getDirectory(), child);
+ if ( !dir.exists() ) {
+ dir = null;
+ }
+ }
+ if ( dir == null ) {
+ throw new MojoExecutionException("No resource entry found containing " + path);
+ }
+ // check for root mapping - which we don't support atm
+ if ( "/".equals(installPath) ) {
+ throw new MojoExecutionException("Mapping to root path not supported by fs provider at the moment. Please adapt your initial content configuration.");
+ }
+ log.info("Mapping " + dir + " to " + installPath);
+
+ // check if this is already configured
+ boolean found = false;
+ final Iterator<Map.Entry<String,String[]>> entryIterator = oldConfigs.entrySet().iterator();
+ while ( !found && entryIterator.hasNext() ) {
+ final Map.Entry<String,String[]> current = entryIterator.next();
+ final String[] value = current.getValue();
+ log.debug("Comparing " + dir.getAbsolutePath() + " with " + value[0] + " (" + value[1] + ")");
+ if ( dir.getAbsolutePath().equals(value[0]) ) {
+ if ( installPath.equals(value[1]) ) {
+ log.debug("Using existing configuration for " + dir + " and " + installPath);
+ found = true;
+ }
+ else {
+ // remove old config
+ log.debug("Removing old configuration for " + value[0] + " and " + value[1]);
+ removeConfiguration(targetURL, current.getKey().toString());
+ }
+ entryIterator.remove();
+ }
+ }
+ if ( !found ) {
+ log.debug("Adding new configuration for " + dir + " and " + installPath);
+ addConfiguration(targetURL, dir.getAbsolutePath(), installPath);
+ }
+ }
+ // finally remove old configs
+ final Iterator<Map.Entry<String,String[]>> entryIterator = oldConfigs.entrySet().iterator();
+ while ( entryIterator.hasNext() ) {
+ final Map.Entry<String,String[]> current = entryIterator.next();
+ final String[] value = current.getValue();
+ log.debug("Removing old configuration for " + value[0] + " and " + value[1]);
+ // remove old config
+ removeConfiguration(targetURL, current.getKey().toString());
+ }
+ }
+
+ /**
+ * Add configurations to a running OSGi instance for initial content.
+ * @param targetURL The web console base url
+ * @param file The artifact (bundle)
+ * @throws MojoExecutionException
+ */
+ public void configureUninstall(final String targetURL, final File file) throws MojoExecutionException {
+ log.info("Removing file system provider configurations...");
+
+ // now get current configurations
+ final Map oldConfigs = getCurrentFileProviderConfigs(targetURL);
+
+ final Iterator entryIterator = oldConfigs.entrySet().iterator();
+ while ( entryIterator.hasNext() ) {
+ final Map.Entry current = (Map.Entry) entryIterator.next();
+ final String[] value = (String[])current.getValue();
+ log.debug("Removing old configuration for " + value[0] + " and " + value[1]);
+ // remove old config
+ removeConfiguration(targetURL, current.getKey().toString());
+ }
+ }
+
+ public void removeConfiguration(final String targetURL, final String pid) throws MojoExecutionException {
+ final String postUrl = targetURL + "/configMgr/" + pid;
+ final PostMethod post = new PostMethod(postUrl);
+ post.addParameter("apply", "true");
+ post.addParameter("delete", "true");
+ try {
+ final int status = httpClient.executeMethod(post);
+ // we get a moved temporarily back from the configMgr plugin
+ if (status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_OK) {
+ log.debug("Configuration removed.");
+ }
+ else {
+ log.error("Removing configuration failed, cause: "+ HttpStatus.getStatusText(status));
+ }
+ }
+ catch (HttpException ex) {
+ throw new MojoExecutionException("Removing configuration at " + postUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new MojoExecutionException("Removing configuration at " + postUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ finally {
+ post.releaseConnection();
+ }
+ }
+
+ /**
+ * Add a new configuration for the file system provider
+ * @throws MojoExecutionException
+ */
+ private void addConfiguration(final String targetURL, String dir, String path) throws MojoExecutionException {
+ final String postUrl = targetURL + "/configMgr/" + FS_FACTORY;
+ final PostMethod post = new PostMethod(postUrl);
+ post.addParameter("apply", "true");
+ post.addParameter("factoryPid", FS_FACTORY);
+ post.addParameter("pid", "[Temporary PID replaced by real PID upon save]");
+ post.addParameter("provider.file", dir);
+ // save property value to both "provider.roots" and "provider.root" because the name has changed between fsresource 1.x and 2.x
+ post.addParameter("provider.root", path);
+ post.addParameter("provider.roots", path);
+ post.addParameter("propertylist", "provider.root,provider.roots,provider.file");
+ try {
+ final int status = httpClient.executeMethod(post);
+ // we get a moved temporarily back from the configMgr plugin
+ if (status == HttpStatus.SC_MOVED_TEMPORARILY || status == HttpStatus.SC_OK) {
+ log.info("Configuration created.");
+ }
+ else {
+ log.error(
+ "Configuration failed, cause: "
+ + HttpStatus.getStatusText(status));
+ }
+ }
+ catch (HttpException ex) {
+ throw new MojoExecutionException("Configuration on " + postUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new MojoExecutionException("Configuration on " + postUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ finally {
+ post.releaseConnection();
+ }
+ }
+
+ /**
+ * Return all file provider configs for this project
+ * @param targetURL The targetURL of the webconsole
+ * @return A map (may be empty) with the pids as keys and a string array
+ * containing the path and the root
+ * @throws MojoExecutionException
+ */
+ public Map<String,String[]> getCurrentFileProviderConfigs(final String targetURL) throws MojoExecutionException {
+ log.debug("Getting current file provider configurations.");
+ final Map<String,String[]> result = new HashMap<>();
+ final String getUrl = targetURL + "/configMgr/(service.factoryPid=" + FS_FACTORY + ").json";
+ final GetMethod get = new GetMethod(getUrl);
+
+ try {
+ final int status = httpClient.executeMethod(get);
+ if ( status == 200 ) {
+ String contentType = get.getResponseHeader(HEADER_CONTENT_TYPE).getValue();
+ int pos = contentType.indexOf(';');
+ if ( pos != -1 ) {
+ contentType = contentType.substring(0, pos);
+ }
+ if ( !AbstractBundleInstallMojo.JSON_MIME_TYPE.equals(contentType) ) {
+ log.debug("Response type from web console is not JSON, but " + contentType);
+ throwWebConsoleTooOldException();
+ }
+ final String jsonText;
+ try (InputStream jsonResponse = get.getResponseBodyAsStream()) {
+ jsonText = IOUtils.toString(jsonResponse, CharEncoding.UTF_8);
+ }
+ try {
+ JsonArray array = JsonSupport.parseArray(jsonText);
+ for(int i=0; i<array.size(); i++) {
+ final JsonObject obj = array.getJsonObject(i);
+ final String pid = obj.getString("pid");
+ final JsonObject properties = obj.getJsonObject("properties");
+ final String path = getConfigPropertyValue(properties, "provider.file");
+ String roots = getConfigPropertyValue(properties, "provider.roots");
+ if (roots == null) {
+ roots = getConfigPropertyValue(properties, "provider.root");
+ }
+ if ( path != null && path.startsWith(this.project.getBasedir().getAbsolutePath()) && roots != null ) {
+ log.debug("Found configuration with pid: " + pid + ", path: " + path + ", roots: " + roots);
+ result.put(pid, new String[] {path, roots});
+ }
+ }
+ } catch (JsonException ex) {
+ throw new MojoExecutionException("Reading configuration from " + getUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ }
+ }
+ catch (HttpException ex) {
+ throw new MojoExecutionException("Reading configuration from " + getUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ catch (IOException ex) {
+ throw new MojoExecutionException("Reading configuration from " + getUrl
+ + " failed, cause: " + ex.getMessage(), ex);
+ }
+ finally {
+ get.releaseConnection();
+ }
+ return result;
+ }
+
+ private String getConfigPropertyValue(JsonObject obj, String subKey) {
+ if (obj.containsKey(subKey)) {
+ JsonObject subObj = obj.getJsonObject(subKey);
+ if (subObj.containsKey("value")) {
+ return subObj.getString("value");
+ }
+ else if (subObj.containsKey("values")) {
+ JsonArray array = subObj.getJsonArray("values");
+ if (array.size() > 0) {
+ // use only first property value from array
+ return array.getString(0);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the manifest from the File.
+ * @param bundleFile The bundle jar
+ * @return The manifest.
+ * @throws IOException
+ */
+ private Manifest getManifest(final File bundleFile) throws IOException {
+ JarFile file = null;
+ try {
+ file = new JarFile(bundleFile);
+ return file.getManifest();
+ }
+ finally {
+ if (file != null) {
+ try {
+ file.close();
+ }
+ catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method to throw a meaningful exception for an outdated felix
+ * web console.
+ * @throws MojoExecutionException
+ */
+ private void throwWebConsoleTooOldException() throws MojoExecutionException {
+ throw new MojoExecutionException("The Apache Felix Web Console is too old to mount " +
+ "the initial content through file system provider configs. " +
+ "Either upgrade the web console or disable this feature.");
+ }
+
+}
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Wed Mar 8 16:11:54 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountHelper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java?rev=1785998&view=auto
==============================================================================
--- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java (added)
+++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java Wed Mar 8 16:11:54 2017
@@ -0,0 +1,57 @@
+/*
+ * 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.io.File;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Creates OSGi configurations for File System Resource provider.
+ */
+@Mojo(name = "fsmount", requiresProject = true)
+public class FsMountMojo extends AbstractBundlePostMojo {
+
+ /**
+ * The name of the generated JAR file.
+ */
+ @Parameter(property = "sling.file", defaultValue = "${project.build.directory}/${project.build.finalName}.jar", required = true)
+ private String bundleFileName;
+
+ /**
+ * The Maven project.
+ */
+ @Parameter(defaultValue = "${project}", required = true, readonly = true)
+ protected MavenProject project;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ File file = new File(bundleFileName);
+ if(!file.exists()) {
+ getLog().info(file + " does not exist, skipping.");
+ return;
+ }
+
+ FsMountHelper fsMountHelper = new FsMountHelper(getLog(), getHttpClient(), project);
+ fsMountHelper.configureInstall(getTargetURL(), file);
+ }
+
+}
\ No newline at end of file
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Wed Mar 8 16:11:54 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsMountMojo.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java?rev=1785998&view=auto
==============================================================================
--- sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java (added)
+++ sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java Wed Mar 8 16:11:54 2017
@@ -0,0 +1,57 @@
+/*
+ * 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.io.File;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+
+/**
+ * Removes OSGi configurations for File System Resource provider.
+ */
+@Mojo(name = "fsunmount", requiresProject = true)
+public class FsUnMountMojo extends AbstractBundlePostMojo {
+
+ /**
+ * The name of the generated JAR file.
+ */
+ @Parameter(property = "sling.file", defaultValue = "${project.build.directory}/${project.build.finalName}.jar", required = true)
+ private String bundleFileName;
+
+ /**
+ * The Maven project.
+ */
+ @Parameter(defaultValue = "${project}", required = true, readonly = true)
+ protected MavenProject project;
+
+ @Override
+ public void execute() throws MojoExecutionException {
+ File file = new File(bundleFileName);
+ if(!file.exists()) {
+ getLog().info(file + " does not exist, skipping.");
+ return;
+ }
+
+ FsMountHelper fsMountHelper = new FsMountHelper(getLog(), getHttpClient(), project);
+ fsMountHelper.configureUninstall(getTargetURL(), file);
+ }
+
+}
\ No newline at end of file
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Wed Mar 8 16:11:54 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/tooling/maven/maven-sling-plugin/src/main/java/org/apache/sling/maven/bundlesupport/FsUnMountMojo.java
------------------------------------------------------------------------------
svn:mime-type = text/plain