diff --git a/pom.xml b/pom.xml
index 4e6697e4..1082cd53 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,9 @@
1.72.2.1
+
3.6.0
+
false2.22.1
@@ -55,7 +57,6 @@
2000falsefalse
- \https://repository.apache.org/service/local/staging/deploy/maven2https://repository.apache.org/content/repositories/snapshots
@@ -73,8 +74,8 @@
20012008
- 7.0.59
- 8.0.14
+ 7.0.109
+ 8.5.100
@@ -324,6 +325,8 @@
tomcat7-maven-plugintomcat8-war-runnertomcat8-maven-plugin
+ tomcat9-war-runner
+ tomcat9-maven-plugintomcat-maven-archetype
@@ -678,7 +681,7 @@
maven-compiler-plugin
- 3.8.0
+ 3.8.1${project.build.sourceEncoding}${maven.compiler.source}
@@ -747,7 +750,10 @@
org.apache.maven.pluginsmaven-resources-plugin
- 3.1.0
+ 3.3.1
+
+ \
+ org.apache.maven.plugins
@@ -774,6 +780,7 @@
DEPENDENCIESREADME.txt*.sh
+ *.log.git/**.idea/**out/**
diff --git a/tomcat-maven-archetype/pom.xml b/tomcat-maven-archetype/pom.xml
index 128680b0..b1f406f2 100644
--- a/tomcat-maven-archetype/pom.xml
+++ b/tomcat-maven-archetype/pom.xml
@@ -36,7 +36,7 @@
Apache Tomcat Maven Plugin :: Archetype
- 2.2
+ 3.1.2${project.version}clean,pre-integration-testclean,install
diff --git a/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-api-impl/pom.xml b/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-api-impl/pom.xml
index 39f79ed1..330e773c 100644
--- a/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-api-impl/pom.xml
+++ b/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-api-impl/pom.xml
@@ -99,7 +99,17 @@
tomcat-jsp-apitest
-
-
-
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+ 2.3.3
+ test
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.3.3
+ test
+
+
diff --git a/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-webapp-it/src/test/context.xml b/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-webapp-it/src/test/context.xml
index d9b3ac84..65496ef2 100644
--- a/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-webapp-it/src/test/context.xml
+++ b/tomcat-maven-archetype/src/main/resources/archetype-resources/__rootArtifactId__-webapp-it/src/test/context.xml
@@ -17,4 +17,4 @@
specific language governing permissions and limitations
under the License.
-->
-
+
diff --git a/tomcat-maven-archetype/src/main/resources/archetype-resources/pom.xml b/tomcat-maven-archetype/src/main/resources/archetype-resources/pom.xml
index 9ae412cb..fa10ce83 100644
--- a/tomcat-maven-archetype/src/main/resources/archetype-resources/pom.xml
+++ b/tomcat-maven-archetype/src/main/resources/archetype-resources/pom.xml
@@ -37,10 +37,10 @@
org.apache.maven.pluginsmaven-compiler-plugin
- 3.1
+ 3.8.1
- 1.5
- 1.5
+ 1.7
+ 1.7
diff --git a/tomcat-maven-plugin-it/pom.xml b/tomcat-maven-plugin-it/pom.xml
index 7569339b..e3fff180 100644
--- a/tomcat-maven-plugin-it/pom.xml
+++ b/tomcat-maven-plugin-it/pom.xml
@@ -34,7 +34,6 @@
2000
- \
diff --git a/tomcat7-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat7/run/AbstractRunMojo.java b/tomcat7-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat7/run/AbstractRunMojo.java
index fff9cb53..092900c6 100644
--- a/tomcat7-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat7/run/AbstractRunMojo.java
+++ b/tomcat7-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat7/run/AbstractRunMojo.java
@@ -725,11 +725,11 @@ else if ( defaultContextFile.exists() )
}
// https://issues.apache.org/jira/browse/MTOMCAT-255
- if(context instanceof StandardContext)
+ if(context instanceof StandardContext && new File(project.getBuild().getOutputDirectory()).exists())
{
- ((StandardContext) context).setAddWebinfClassesResources(true);
- ((StandardContext) context).setAliases( "/WEB-INF/classes=" + project.getBuild().getOutputDirectory());
- }
+ ((StandardContext) context).setAddWebinfClassesResources(true);
+ ((StandardContext) context).setAliases("/WEB-INF/classes=" + project.getBuild().getOutputDirectory());
+ }
return context;
diff --git a/tomcat8-maven-plugin/pom.xml b/tomcat8-maven-plugin/pom.xml
index 6bb68859..083efe07 100644
--- a/tomcat8-maven-plugin/pom.xml
+++ b/tomcat8-maven-plugin/pom.xml
@@ -131,18 +131,18 @@
org.apache.tomcattomcat-juli
- ${tomcat8Version}
+ 8.5.57org.apache.tomcat.embedtomcat-embed-logging-juli
- ${tomcat8Version}
+ 8.0.14org.apache.tomcat.embedtomcat-embed-logging-log4j
- ${tomcat8Version}
+ 8.0.14
diff --git a/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/AbstractRunMojo.java b/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/AbstractRunMojo.java
index 225b3f76..94c1ead1 100644
--- a/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/AbstractRunMojo.java
+++ b/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/AbstractRunMojo.java
@@ -813,13 +813,13 @@ public WebResource getResource( String path )
log.debug( "MyDirContext#getResource: " + path );
if ( "/WEB-INF/classes".equals( path ) )
{
- return new FileResource( this, this.webAppPath, new File( this.buildOutputDirectory ), true );
+ return new FileResource( this, this.webAppPath, new File( this.buildOutputDirectory ), true, null );
}
File file = new File( path );
if ( file.exists() )
{
- return new FileResource( this, this.webAppPath, file, true );
+ return new FileResource( this, this.webAppPath, file, true, null );
}
WebResource webResource = super.getResource( path );
return webResource;
diff --git a/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/RunMojo.java b/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/RunMojo.java
index e4e40404..dc5cab8f 100644
--- a/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/RunMojo.java
+++ b/tomcat8-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat8/run/RunMojo.java
@@ -20,14 +20,13 @@
import org.apache.catalina.Context;
import org.apache.catalina.WebResource;
-import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.WebResourceSet;
-import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.webresources.EmptyResource;
import org.apache.catalina.webresources.FileResource;
import org.apache.catalina.webresources.FileResourceSet;
import org.apache.catalina.webresources.JarResource;
+import org.apache.catalina.webresources.JarResourceSet;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
@@ -479,18 +478,16 @@ private WebResource urlToWebResource( URL url, String path )
jarFile = new JarFile( filePath );
JarEntry jarEntry = jarFile.getJarEntry( StringUtils.removeStart( path, "/" ) );
+ final JarResourceSet jarResourceSet = new JarResourceSet(this, getPath(), jarFile.getName(), path);
- return new JarResource( this, //
+ return new JarResource( jarResourceSet, //
getPath(), //
- filePath, //
url.getPath().substring( 0, idx ), //
- jarEntry, //
- "", //
- null );
+ jarEntry);
}
else
{
- return new FileResource( this, webAppPath, new File( url.getFile() ), true );
+ return new FileResource( this, webAppPath, new File( url.getFile() ), true, null );
}
}
@@ -537,18 +534,18 @@ public WebResource getResource( String path )
if ( StringUtils.startsWithIgnoreCase( path, "/WEB-INF/LIB" ) )
{
File file = new File( StringUtils.removeStartIgnoreCase( path, "/WEB-INF/LIB" ) );
- return new FileResource( context.getResources(), getPath(), file, true );
+ return new FileResource( context.getResources(), getPath(), file, true, null );
}
if ( StringUtils.equalsIgnoreCase( path, "/WEB-INF/classes" ) )
{
return new FileResource( context.getResources(), getPath(),
- new File( project.getBuild().getOutputDirectory() ), true );
+ new File( project.getBuild().getOutputDirectory() ), true, null );
}
File file = new File( project.getBuild().getOutputDirectory(), path );
if ( file.exists() )
{
- return new FileResource( context.getResources(), getPath(), file, true );
+ return new FileResource( context.getResources(), getPath(), file, true, null );
}
//if ( StringUtils.endsWith( path, ".class" ) )
@@ -569,13 +566,11 @@ public WebResource getResource( String path )
(JarEntry) jarFile.getEntry( StringUtils.removeStart( path, "/" ) );
if ( jarEntry != null )
{
- return new JarResource( context.getResources(), //
+ final JarResourceSet jarResourceSet = new JarResourceSet(context.getResources(), getPath(), jarFile.getName(), path);
+ return new JarResource( jarResourceSet, //
getPath(), //
- jarFile.getName(), //
jar.toURI().toString(), //
- jarEntry, //
- path, //
- jarFile.getManifest() );
+ jarEntry);
}
}
catch ( IOException e )
diff --git a/tomcat8-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm b/tomcat8-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm
index 02f0e8b8..b3bb308d 100644
--- a/tomcat8-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm
+++ b/tomcat8-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm
@@ -35,7 +35,7 @@ Adjust Tomcat Version
[...]
- 7.0.50
+ 8.5.100
[...]
[...]
diff --git a/tomcat9-maven-plugin/pom.xml b/tomcat9-maven-plugin/pom.xml
new file mode 100644
index 00000000..0142a4ca
--- /dev/null
+++ b/tomcat9-maven-plugin/pom.xml
@@ -0,0 +1,378 @@
+
+
+
+
+ 4.0.0
+
+ tomcat-maven-plugin
+ org.apache.tomcat.maven
+ 3.0-SNAPSHOT
+
+ tomcat9-maven-plugin
+ maven-plugin
+ Apache Tomcat Maven Plugin :: Tomcat 9.x
+ The Tomcat Maven Plugin provides goals to manipulate WAR projects within the Tomcat 8.x servlet container.
+
+
+
+ ${basedir}/target/
+ ${basedir}/src/test/simple-war-project-1.0-SNAPSHOT.war
+ ${basedir}/src/test/keystore
+ 9.0.96
+
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-util
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-coyote
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-dbcp
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-servlet-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jsp-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper-el
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-el-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-tribes
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina-ha
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-annotations-api
+ ${tomcat9Version}
+
+
+
+
+
+
+ org.apache.tomcat
+ tomcat-juli
+ ${tomcat9Version}
+
+
+
+
+ org.eclipse.jdt.core.compiler
+ ecj
+
+
+
+
+
+
+ org.apache.tomcat.maven
+ common-tomcat-maven-plugin
+
+
+ org.apache.tomcat.maven
+ tomcat9-war-runner
+ ${project.version}
+
+
+ org.apache.maven
+ maven-plugin-api
+
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ provided
+
+
+ org.apache.maven
+ maven-archiver
+
+
+ org.codehaus.plexus
+ plexus-classworlds
+
+
+
+ commons-io
+ commons-io
+
+
+
+ commons-lang
+ commons-lang
+
+
+
+ org.apache.commons
+ commons-compress
+
+
+
+ org.codehaus.plexus
+ plexus-archiver
+
+
+
+ org.codehaus.plexus
+ plexus-utils
+
+
+
+ org.apache.maven.shared
+ maven-filtering
+
+
+ nekohtml
+ xercesMinimal
+
+
+
+
+
+ org.apache.tomcat.maven
+ tomcat-maven-plugin-it
+ test
+
+
+ junit
+ junit
+ test
+
+
+
+ org.slf4j
+ jcl-over-slf4j
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ test
+
+
+
+
+
+
+ src/test/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/**IT*
+
+
+ ${project.build.directory}
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ pre-integration-test
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ run-its
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ integration-test
+
+ integration-test
+
+
+
+ ${project.version}
+ ${verifier.maven.debug}
+ ${verifier.debugJvm}
+ ${its.http.port}
+ ${its.https.port}
+ ${its.ajp.port}
+ ${project.build.directory}
+
+ false
+ false
+
+
+
+ verify
+
+ verify
+
+
+
+
+
+
+
+
+
+
diff --git a/tomcat9-maven-plugin/src/it/settings.xml b/tomcat9-maven-plugin/src/it/settings.xml
new file mode 100644
index 00000000..b9b7e260
--- /dev/null
+++ b/tomcat9-maven-plugin/src/it/settings.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+ it-repo
+
+ true
+
+
+
+ local.central
+ @localRepositoryUrl@
+
+ true
+
+
+ true
+
+
+
+ local.snapshots
+ @localRepositoryUrl@
+
+ false
+
+
+ true
+
+
+
+
+
+ local.central
+ @localRepositoryUrl@
+
+ true
+
+
+ true
+
+
+
+ local.snapshots
+ @localRepositoryUrl@
+
+ false
+
+
+ true
+
+
+
+
+
+
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractCatalinaMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractCatalinaMojo.java
new file mode 100644
index 00000000..1d71f644
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractCatalinaMojo.java
@@ -0,0 +1,273 @@
+package org.apache.tomcat.maven.plugin.tomcat9;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.artifact.manager.WagonManager;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.tomcat.maven.common.deployer.TomcatManager;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerException;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractTomcat9Mojo;
+import org.codehaus.plexus.util.StringUtils;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.StringTokenizer;
+
+/**
+ * Abstract goal that provides common configuration for Catalina-based goals.
+ *
+ * @author Mark Hobson
+ */
+public abstract class AbstractCatalinaMojo
+ extends AbstractTomcat9Mojo
+{
+ // ----------------------------------------------------------------------
+ // Constants
+ // ----------------------------------------------------------------------
+
+ /**
+ * The name of this Maven plugin. Used to produce the user agent when communicating with Tomcat manager.
+ */
+ private String name = "Apache Tomcat Maven Plugin";
+
+ /**
+ * The default username to use when authenticating with Tomcat manager.
+ */
+ private static final String DEFAULT_USERNAME = "admin";
+
+ /**
+ * The default password to use when authenticating with Tomcat manager.
+ */
+ private static final String DEFAULT_PASSWORD = "";
+
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The Maven Wagon manager to use when obtaining server authentication details.
+ */
+ @Component
+ private WagonManager wagonManager;
+
+ /**
+ * The full URL of the Tomcat manager instance to use.
+ */
+ @Parameter( property = "maven.tomcat.url", defaultValue = "http://localhost:8080/manager/text", required = true )
+ private URL url;
+
+ /**
+ * The server id in settings.xml to use when authenticating with Tomcat manager, or null to use
+ * defaults of username admin and no password.
+ */
+ @Parameter( property = "maven.tomcat.server" )
+ private String server;
+
+ /**
+ * The URL encoding charset to use when communicating with Tomcat manager.
+ */
+ @Parameter( property = "maven.tomcat.charset", defaultValue = "ISO-8859-1", required = true )
+ private String charset;
+
+ /**
+ * The tomcat username to use for deployment
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter( property = "tomcat.username" )
+ private String username;
+
+ /**
+ * The password to use for deployment
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter( property = "tomcat.password" )
+ private String password;
+
+ @Parameter( defaultValue = "${plugin.version}", required = true, readonly = true )
+ private String version;
+
+ // ----------------------------------------------------------------------
+ // Fields
+ // ----------------------------------------------------------------------
+
+ /**
+ * The Tomcat manager wrapper object.
+ */
+ private TomcatManager manager;
+
+ // ----------------------------------------------------------------------
+ // Mojo Implementation
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void execute()
+ throws MojoExecutionException
+ {
+ try
+ {
+ invokeManager();
+ }
+ catch ( TomcatManagerException exception )
+ {
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "AbstractCatalinaMojo.managerError", exception.getMessage() ) );
+ }
+ catch ( IOException exception )
+ {
+ throw new MojoExecutionException( messagesProvider.getMessage( "AbstractCatalinaMojo.managerIOError" ),
+ exception );
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Invokes Tomcat manager when this Mojo is executed.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if there was a problem executing this goal
+ * @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
+ * if the Tomcat manager request fails
+ * @throws java.io.IOException if an i/o error occurs
+ */
+ protected abstract void invokeManager()
+ throws MojoExecutionException, TomcatManagerException, IOException;
+
+ /**
+ * Gets the Tomcat manager wrapper object configured for this goal.
+ *
+ * @return the Tomcat manager wrapper object
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if there was a problem obtaining the authentication details
+ */
+ protected TomcatManager getManager()
+ throws MojoExecutionException
+ {
+ // lazily instantiate when config values have been injected
+ if ( manager == null )
+ {
+ String userName;
+ String password;
+
+ if ( server == null )
+ {
+ // no server set, use defaults
+ getLog().debug( messagesProvider.getMessage( "AbstractCatalinaMojo.defaultAuth" ) );
+ userName = DEFAULT_USERNAME;
+ password = DEFAULT_PASSWORD;
+ }
+ else
+ {
+ // obtain authenication details for specified server from wagon
+ AuthenticationInfo info = wagonManager.getAuthenticationInfo( server );
+ if ( info == null )
+ {
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "AbstractCatalinaMojo.unknownServer", server ) );
+ }
+
+ // derive username
+ userName = info.getUserName();
+ if ( userName == null )
+ {
+ getLog().debug( messagesProvider.getMessage( "AbstractCatalinaMojo.defaultUserName" ) );
+ userName = DEFAULT_USERNAME;
+ }
+
+ // derive password
+ password = info.getPassword();
+ if ( password == null )
+ {
+ getLog().debug( messagesProvider.getMessage( "AbstractCatalinaMojo.defaultPassword" ) );
+ password = DEFAULT_PASSWORD;
+ }
+ }
+
+ // if userName/password are defined in the mojo or the cli they override
+ if ( !StringUtils.isEmpty( this.username ) )
+ {
+ userName = this.username;
+ password = this.password == null ? "" : this.password;
+ }
+
+ manager = new TomcatManager( url, userName, password, charset, settings.isInteractiveMode() );
+ manager.setUserAgent( name + "/" + version );
+ }
+
+ return manager;
+ }
+
+ /**
+ * Gets the full URL of the Tomcat manager instance.
+ *
+ * @return the full URL of the Tomcat manager instance to use
+ */
+ protected URL getURL()
+ {
+ return url;
+ }
+
+ /**
+ * Gets the webapp context path to use when communicating with Tomcat manager.
+ *
+ * @return the webapp context path to use
+ */
+ protected String getPath()
+ {
+ return path;
+ }
+
+ /**
+ * Gets the URL of the deployed webapp.
+ *
+ * @return the URL of the deployed webapp
+ * @throws java.net.MalformedURLException if the deployed webapp URL is invalid
+ */
+ protected URL getDeployedURL()
+ throws MalformedURLException
+ {
+ return new URL( getURL(), getPath() );
+ }
+
+ /**
+ * Splits the given string into lines and writes each one separately to the log at info level.
+ *
+ * @param string the string to write
+ */
+ protected void log( String string )
+ {
+ StringTokenizer tokenizer = new StringTokenizer( string, "\n\r" );
+
+ while ( tokenizer.hasMoreTokens() )
+ {
+ getLog().info( tokenizer.nextToken() );
+ }
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractTomcat9Mojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractTomcat9Mojo.java
new file mode 100644
index 00000000..401bacd4
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractTomcat9Mojo.java
@@ -0,0 +1,82 @@
+package org.apache.tomcat.maven.plugin.tomcat9;
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.settings.Settings;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerResponse;
+import org.apache.tomcat.maven.common.messages.MessagesProvider;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public abstract class AbstractTomcat9Mojo
+ extends AbstractMojo
+{
+ @Component
+ protected Settings settings;
+
+ @Component
+ protected MessagesProvider messagesProvider;
+
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The webapp context path to use for the web application being run. This must always start with a forward-slash
+ * ('/').
+ */
+ @Parameter(defaultValue = "/${project.artifactId}", property = "maven.tomcat.path", required = true)
+ protected String path;
+
+
+ protected String getPath()
+ {
+ return path;
+ }
+
+ /**
+ * Check response of Tomcat to know if ok or not.
+ *
+ * @param tomcatResponse response of tomcat return by TomcatManager class
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if HTTP status code greater than 400 (included)
+ */
+ protected void checkTomcatResponse( TomcatManagerResponse tomcatResponse )
+ throws MojoExecutionException
+ {
+ int statusCode = tomcatResponse.getStatusCode();
+
+ if ( statusCode >= 400 )
+ {
+ getLog().error( messagesProvider.getMessage( "tomcatHttpStatusError", statusCode,
+ tomcatResponse.getReasonPhrase() ) );
+
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "tomcatHttpStatusError", statusCode,
+ tomcatResponse.getReasonPhrase() ) + ": "
+ + tomcatResponse.getHttpResponseBody() );
+ }
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractWarCatalinaMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractWarCatalinaMojo.java
new file mode 100644
index 00000000..754b6d7f
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/AbstractWarCatalinaMojo.java
@@ -0,0 +1,84 @@
+package org.apache.tomcat.maven.plugin.tomcat9;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Parameter;
+
+/**
+ * Abstract goal that provides common configuration for Catalina-based goals.
+ *
+ * @author Mark Hobson
+ */
+public abstract class AbstractWarCatalinaMojo
+ extends AbstractCatalinaMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The packaging of the Maven project that this goal operates upon.
+ */
+ @Parameter( defaultValue = "${project.packaging}", required = true, readonly = true )
+ private String packaging;
+
+ /**
+ * If set to true ignore if packaging of project is not 'war'.
+ *
+ * @since 1.1
+ */
+ @Parameter( property = "tomcat.ignorePackaging", defaultValue = "false" )
+ private boolean ignorePackaging;
+
+ // ----------------------------------------------------------------------
+ // Mojo Implementation
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void execute()
+ throws MojoExecutionException
+ {
+ if ( !isWar() )
+ {
+ getLog().info( messagesProvider.getMessage( "AbstractWarCatalinaMojo.nonWar" ) );
+ return;
+ }
+
+ super.execute();
+ }
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Gets whether this project uses WAR packaging.
+ *
+ * @return whether this project uses WAR packaging
+ */
+ protected boolean isWar()
+ {
+ return "war".equals( packaging ) || ignorePackaging;
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployMojo.java
new file mode 100644
index 00000000..de933ce4
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployMojo.java
@@ -0,0 +1,226 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerException;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerResponse;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractWarCatalinaMojo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * Deploy a WAR to Tomcat.
+ *
+ * @author Mark Hobson
+ */
+public abstract class AbstractDeployMojo
+ extends AbstractWarCatalinaMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The deployment mode to use. This must be either war to deploy the war, context to
+ * deploy the context XML file, or both to deploy the war with the context XML file.
+ */
+ @Parameter( property = "maven.tomcat.mode", defaultValue = "war", required = true )
+ private String mode;
+
+ /**
+ * The path of the Tomcat context XML file. This is not used for war deployment mode.
+ */
+ @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}/META-INF/context.xml" )
+ private File contextFile;
+
+ /**
+ * Whether Tomcat should automatically undeploy webapps that already exist when deploying.
+ */
+ @Parameter( property = "maven.tomcat.update", defaultValue = "false", required = true )
+ private boolean update;
+
+ /**
+ * The Tomcat webapp tag name to use.
+ */
+ @Parameter( property = "maven.tomcat.tag" )
+ private String tag;
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invokeManager()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ if ( "war".equals( mode ) )
+ {
+ deployWar();
+ }
+ else if ( "context".equals( mode ) )
+ {
+ deployContext();
+ }
+ else if ( "both".equals( mode ) )
+ {
+ deployWarAndContext();
+ }
+ else
+ {
+ throw new MojoExecutionException( messagesProvider.getMessage( "AbstractDeployMojo.unknownMode", mode ) );
+ }
+ }
+
+ /**
+ * Gets the Tomcat WAR file. This may be a file or a directory depending on the deployment mode.
+ *
+ * @return the Tomcat WAR file.
+ */
+ protected abstract File getWarFile();
+
+ /**
+ * Ensures that the Tomcat WAR file exists and is the correct type for the deployment mode.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if the WAR file does not exist or is not the correct type for the deployment mode
+ */
+ protected abstract void validateWarFile()
+ throws MojoExecutionException;
+
+ /**
+ * Gets the Tomcat context XML file.
+ *
+ * @return the Tomcat context XML file.
+ */
+ protected File getContextFile()
+ {
+ return contextFile;
+ }
+
+ /**
+ * Ensures that the Tomcat context XML file exists and is indeed a file.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if the context file does not exist or is not a file
+ */
+ protected void validateContextFile()
+ throws MojoExecutionException
+ {
+ if ( !contextFile.exists() || !contextFile.isFile() )
+ {
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "AbstractDeployMojo.missingContext", contextFile.getPath() ) );
+ }
+ }
+
+ /**
+ * Gets whether Tomcat should automatically undeploy webapps that already exist when deploying.
+ *
+ * @return whether Tomcat should automatically undeploy webapps that already exist when deploying
+ */
+ protected boolean isUpdate()
+ {
+ return update;
+ }
+
+ /**
+ * Gets the Tomcat webapp tag name to use.
+ *
+ * @return the Tomcat webapp tag name to use
+ */
+ protected String getTag()
+ {
+ return tag;
+ }
+
+ /**
+ * Deploys the WAR to Tomcat.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if there was a problem locating the WAR
+ * @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
+ * if the Tomcat manager request fails
+ * @throws java.io.IOException if an i/o error occurs
+ */
+ protected void deployWar()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ validateWarFile();
+
+ getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWar", getDeployedURL() ) );
+
+ URL warURL = getWarFile().toURL();
+ log( getManager().deploy( getPath(), warURL, isUpdate(), getTag() ).getHttpResponseBody() );
+ }
+
+ /**
+ * Deploys the context XML file to Tomcat.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if there was a problem locating the context XML file
+ * @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
+ * if the Tomcat manager request fails
+ * @throws java.io.IOException if an i/o error occurs
+ */
+ protected void deployContext()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ validateContextFile();
+
+ getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingContext", getDeployedURL() ) );
+
+ URL contextURL = getContextFile().toURL();
+ log( getManager().deployContext( getPath(), contextURL, isUpdate(), getTag() ).getHttpResponseBody() );
+ }
+
+ /**
+ * Deploys the WAR and context XML file to Tomcat.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if there was a problem locating either the WAR or the context XML file
+ * @throws org.apache.tomcat.maven.common.deployer.TomcatManagerException
+ * if the Tomcat manager request fails
+ * @throws java.io.IOException if an i/o error occurs
+ */
+ protected void deployWarAndContext()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ validateWarFile();
+ validateContextFile();
+
+ getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWarContext", getDeployedURL() ) );
+
+ URL warURL = getWarFile().toURL();
+ URL contextURL = getContextFile().toURL();
+
+ TomcatManagerResponse tomcatResponse = getManager().deployContext( getPath(), contextURL, warURL, isUpdate(), getTag() );
+
+ checkTomcatResponse( tomcatResponse );
+
+ log( tomcatResponse.getHttpResponseBody() );
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployWarMojo.java
new file mode 100644
index 00000000..891ec496
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/AbstractDeployWarMojo.java
@@ -0,0 +1,95 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerException;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerResponse;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author olamy
+ * @since 1.0-alpha-2
+ */
+public class AbstractDeployWarMojo
+ extends AbstractDeployMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The path of the WAR file to deploy.
+ */
+ @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}.war", required = true )
+ private File warFile;
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected File getWarFile()
+ {
+ return warFile;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void validateWarFile()
+ throws MojoExecutionException
+ {
+ if ( !warFile.exists() || !warFile.isFile() )
+ {
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "DeployMojo.missingWar", warFile.getPath() ) );
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void deployWar()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ validateWarFile();
+
+ getLog().info( messagesProvider.getMessage( "AbstractDeployMojo.deployingWar", getDeployedURL() ) );
+
+ TomcatManagerResponse tomcatManagerResponse =
+ getManager().deploy( getPath(), warFile, isUpdate(), getTag(), warFile.length() );
+
+ checkTomcatResponse( tomcatManagerResponse );
+
+ getLog().info( "tomcatManager status code:" + tomcatManagerResponse.getStatusCode() + ", ReasonPhrase:"
+ + tomcatManagerResponse.getReasonPhrase() );
+
+ log( tomcatManagerResponse.getHttpResponseBody() );
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployMojo.java
new file mode 100644
index 00000000..fcf0395e
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployMojo.java
@@ -0,0 +1,37 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Deploy a WAR to Tomcat.
+ *
+ * @author Mark Hobson
+ */
+@Mojo(name = "deploy", threadSafe = true)
+@Execute(phase = LifecyclePhase.PACKAGE)
+public class DeployMojo
+ extends AbstractDeployWarMojo
+{
+ // no-op : only mojo metadata overriding
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployOnlyMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployOnlyMojo.java
new file mode 100644
index 00000000..aa7b56a3
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/DeployOnlyMojo.java
@@ -0,0 +1,35 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Deploy a WAR to Tomcat without forking the package lifecycle.
+ *
+ * @author olamy
+ * @since 1.0-alpha-2
+ */
+@Mojo(name = "deploy-only", threadSafe = true)
+public class DeployOnlyMojo
+ extends AbstractDeployWarMojo
+{
+ // no-op : only mojo metadata overriding
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployMojo.java
new file mode 100644
index 00000000..5c1e006b
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployMojo.java
@@ -0,0 +1,43 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Redeploy a WAR in Tomcat. (Alias for the deploy goal with its update parameter set to true.)
+ *
+ * @author Olivier Lamy
+ * @since 2.1
+ */
+@Mojo(name = "redeploy", threadSafe = true)
+@Execute(phase = LifecyclePhase.PACKAGE)
+public class RedeployMojo
+ extends DeployMojo
+{
+ @Override
+ protected boolean isUpdate()
+ {
+ return true;
+ }
+
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployOnlyMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployOnlyMojo.java
new file mode 100644
index 00000000..9bad94b5
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/RedeployOnlyMojo.java
@@ -0,0 +1,40 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Redeploy a WAR in Tomcat without forking the package lifecycle.
+ * (Alias for the deploy-only goal with its update parameter set to true.)
+ *
+ * @since 2.1
+ */
+@Mojo( name = "redeploy-only", threadSafe = true )
+public class RedeployOnlyMojo
+ extends DeployOnlyMojo
+{
+ @Override
+ protected boolean isUpdate()
+ {
+ return true;
+ }
+
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/UndeployMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/UndeployMojo.java
new file mode 100644
index 00000000..c2d2dd48
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/deploy/UndeployMojo.java
@@ -0,0 +1,84 @@
+package org.apache.tomcat.maven.plugin.tomcat9.deploy;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerException;
+import org.apache.tomcat.maven.common.deployer.TomcatManagerResponse;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractWarCatalinaMojo;
+
+import java.io.IOException;
+
+/**
+ * Undeploy a WAR from Tomcat.
+ *
+ * @since 2.1
+ */
+@Mojo( name = "undeploy", threadSafe = true )
+public class UndeployMojo
+ extends AbstractWarCatalinaMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * Whether to fail the build if the web application cannot be undeployed.
+ */
+ @Parameter( property = "maven.tomcat.failOnError", defaultValue = "true" )
+ private boolean failOnError;
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void invokeManager()
+ throws MojoExecutionException, TomcatManagerException, IOException
+ {
+ getLog().info( messagesProvider.getMessage( "UndeployMojo.undeployingApp", getDeployedURL() ) );
+
+ try
+ {
+
+ TomcatManagerResponse tomcatResponse = getManager().undeploy( getPath() );
+
+ checkTomcatResponse( tomcatResponse );
+
+ log( tomcatResponse.getHttpResponseBody() );
+
+ }
+ catch ( TomcatManagerException e )
+ {
+ if ( failOnError )
+ {
+ throw e;
+ }
+
+ getLog().warn( messagesProvider.getMessage( "UndeployMojo.undeployError", e.getMessage() ) );
+ }
+ }
+
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractExecWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractExecWarMojo.java
new file mode 100644
index 00000000..5ed0f444
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractExecWarMojo.java
@@ -0,0 +1,679 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractTomcat9Mojo;
+import org.apache.tomcat.maven.runner.Tomcat9Runner;
+import org.apache.tomcat.maven.runner.Tomcat9RunnerCli;
+import org.codehaus.plexus.archiver.jar.Manifest;
+import org.codehaus.plexus.archiver.jar.ManifestException;
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.SelectorUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public abstract class AbstractExecWarMojo
+ extends AbstractTomcat9Mojo
+{
+
+ @Parameter( defaultValue = "${project.artifact}", required = true, readonly = true )
+ protected Artifact projectArtifact;
+
+ /**
+ * The maven project.
+ */
+ @Parameter( defaultValue = "${project}", required = true, readonly = true )
+ protected MavenProject project;
+
+ @Parameter( defaultValue = "${plugin.artifacts}", required = true )
+ protected List pluginArtifacts;
+
+ @Parameter( defaultValue = "${project.build.directory}" )
+ protected File buildDirectory;
+
+ /**
+ * Path under {@link #buildDirectory} where this mojo may do temporary work.
+ */
+ @Parameter( defaultValue = "${project.build.directory}/tomcat9-maven-plugin-exec" )
+ private File pluginWorkDirectory;
+
+ @Parameter( property = "maven.tomcat.exec.war.tomcatConf", defaultValue = "src/main/tomcatconf" )
+ protected File tomcatConfigurationFilesDirectory;
+
+ @Parameter( defaultValue = "src/main/tomcatconf/server.xml", property = "maven.tomcat.exec.war.serverXml" )
+ protected File serverXml;
+
+ /**
+ * Name of the generated exec JAR.
+ */
+ @Parameter( property = "tomcat.jar.finalName",
+ defaultValue = "${project.artifactId}-${project.version}-war-exec.jar", required = true )
+ protected String finalName;
+
+ /**
+ * Skip the execution
+ *
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.skip", defaultValue = "false" )
+ private boolean skip;
+
+ /**
+ * The webapp context path to use for the web application being run.
+ * The name to store webapp in exec jar. Do not use /
+ */
+ @Parameter( property = "maven.tomcat.path", defaultValue = "${project.artifactId}", required = true )
+ protected String path;
+
+ @Parameter
+ protected List warRunDependencies;
+
+ @Component
+ protected ArtifactResolver artifactResolver;
+
+ /**
+ * Maven Artifact Factory component.
+ */
+ @Component
+ protected ArtifactFactory artifactFactory;
+
+ /**
+ * Location of the local repository.
+ */
+ @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
+ protected ArtifactRepository local;
+
+ /**
+ * List of Remote Repositories used by the resolver
+ */
+ @Parameter( defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true )
+ protected List remoteRepos;
+
+ @Component
+ protected MavenProjectHelper projectHelper;
+
+ /**
+ * Attach or not the generated artifact to the build (use true if you want to install or deploy it)
+ */
+ @Parameter( property = "maven.tomcat.exec.war.attachArtifact", defaultValue = "true", required = true )
+ protected boolean attachArtifact;
+
+
+ /**
+ * the classifier to use for the attached/generated artifact
+ */
+ @Parameter( property = "maven.tomcat.exec.war.attachArtifactClassifier", defaultValue = "exec-war",
+ required = true )
+ protected String attachArtifactClassifier;
+
+
+ /**
+ * the type to use for the attached/generated artifact
+ */
+ @Parameter( property = "maven.tomcat.exec.war.attachArtifactType", defaultValue = "jar", required = true )
+ protected String attachArtifactClassifierType;
+
+ /**
+ * to enable naming when starting tomcat
+ */
+ @Parameter( property = "maven.tomcat.exec.war.enableNaming", defaultValue = "false", required = true )
+ protected boolean enableNaming;
+
+ /**
+ * see http://tomcat.apache.org/tomcat-7.0-doc/config/valve.html
+ */
+ @Parameter( property = "maven.tomcat.exec.war.accessLogValveFormat", defaultValue = "%h %l %u %t %r %s %b %I %D",
+ required = true )
+ protected String accessLogValveFormat;
+
+ /**
+ * list of extra dependencies to add in the standalone tomcat jar: your jdbc driver, mail.jar etc..
+ * Those dependencies will be in root classloader.
+ */
+ @Parameter
+ protected List extraDependencies;
+
+ /**
+ * list of extra resources to add in the standalone tomcat jar: your logger configuration etc
+ */
+ @Parameter
+ protected List extraResources;
+
+ /**
+ * Main class to use for starting the standalone jar.
+ */
+ @Parameter( property = "maven.tomcat.exec.war.mainClass",
+ defaultValue = "org.apache.tomcat.maven.runner.Tomcat9RunnerCli", required = true )
+ protected String mainClass;
+
+ /**
+ * which connector protocol to use HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
+ */
+ @Parameter( property = "maven.tomcat.exec.war.connectorHttpProtocol", defaultValue = "HTTP/1.1", required = true )
+ protected String connectorHttpProtocol;
+
+ /**
+ * configure a default http port for the standalone jar
+ *
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.exec.war.httpPort" )
+ protected String httpPort;
+
+ /**
+ * File patterns to exclude from extraDependencies
+ *
+ * @since 2.2
+ */
+ @Parameter
+ protected String[] excludes;
+
+ public void execute()
+ throws MojoExecutionException, MojoFailureException
+ {
+ if ( this.skip )
+ {
+ getLog().info( "skip execution" );
+ return;
+ }
+ //project.addAttachedArtifact( );
+ File warExecFile = new File( buildDirectory, finalName );
+ if ( warExecFile.exists() )
+ {
+ warExecFile.delete();
+ }
+
+ File execWarJar = new File( buildDirectory, finalName );
+
+ FileOutputStream execWarJarOutputStream = null;
+ ArchiveOutputStream os = null;
+ File tmpPropertiesFile = null;
+ File tmpManifestFile = null;
+ FileOutputStream tmpPropertiesFileOutputStream = null;
+ PrintWriter tmpManifestWriter = null;
+
+ try
+ {
+
+ tmpPropertiesFile = new File( buildDirectory, "war-exec.properties" );
+ if ( tmpPropertiesFile.exists() )
+ {
+ tmpPropertiesFile.delete();
+ }
+ tmpPropertiesFile.getParentFile().mkdirs();
+
+ tmpManifestFile = new File( buildDirectory, "war-exec.manifest" );
+ if ( tmpManifestFile.exists() )
+ {
+ tmpManifestFile.delete();
+ }
+ tmpPropertiesFileOutputStream = new FileOutputStream( tmpPropertiesFile );
+ execWarJar.getParentFile().mkdirs();
+ execWarJar.createNewFile();
+ execWarJarOutputStream = new FileOutputStream( execWarJar );
+
+ tmpManifestWriter = new PrintWriter( tmpManifestFile );
+
+ // store :
+ //* wars in the root: foo.war
+ //* tomcat jars
+ //* file tomcat.standalone.properties with possible values :
+ // * useServerXml=true/false to use directly the one provided
+ // * enableNaming=true/false
+ // * wars=foo.war|contextpath;bar.war ( |contextpath is optionnal if empty use the war name )
+ // * accessLogValveFormat=
+ // * connectorhttpProtocol: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
+ //* optionnal: conf/ with usual tomcat configuration files
+ //* MANIFEST with Main-Class
+
+ Properties properties = new Properties();
+
+ properties.put(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY,
+ Long.toString( System.currentTimeMillis() ) );
+ properties.put(Tomcat9Runner.ENABLE_NAMING_KEY, Boolean.toString( enableNaming ) );
+ properties.put(Tomcat9Runner.ACCESS_LOG_VALVE_FORMAT_KEY, accessLogValveFormat );
+ properties.put(Tomcat9Runner.HTTP_PROTOCOL_KEY, connectorHttpProtocol );
+
+ if ( httpPort != null )
+ {
+ properties.put(Tomcat9Runner.HTTP_PORT_KEY, httpPort );
+ }
+
+ os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR,
+ execWarJarOutputStream );
+
+ if ( "war".equals( project.getPackaging() ) )
+ {
+
+ os.putArchiveEntry( new JarArchiveEntry( StringUtils.removeStart( path, "/" ) + ".war" ) );
+ IOUtils.copy( new FileInputStream( projectArtifact.getFile() ), os );
+ os.closeArchiveEntry();
+
+ properties.put(Tomcat9Runner.WARS_KEY, StringUtils.removeStart( path, "/" ) + ".war|" + path );
+ }
+ else if ( warRunDependencies != null && !warRunDependencies.isEmpty() )
+ {
+ for ( WarRunDependency warRunDependency : warRunDependencies )
+ {
+ if ( warRunDependency.dependency != null )
+ {
+ Dependency dependency = warRunDependency.dependency;
+ String version = dependency.getVersion();
+ if ( StringUtils.isEmpty( version ) )
+ {
+ version = findArtifactVersion( dependency );
+ }
+
+ if ( StringUtils.isEmpty( version ) )
+ {
+ throw new MojoExecutionException(
+ "Dependency '" + dependency.getGroupId() + "':'" + dependency.getArtifactId()
+ + "' does not have version specified" );
+ }
+ Artifact artifact = artifactFactory.createArtifactWithClassifier( dependency.getGroupId(), //
+ dependency.getArtifactId(), //
+ version, //
+ dependency.getType(), //
+ dependency.getClassifier() );
+
+ artifactResolver.resolve( artifact, this.remoteRepos, this.local );
+
+ File warFileToBundle = new File( resolvePluginWorkDir(), artifact.getFile().getName() );
+ FileUtils.copyFile( artifact.getFile(), warFileToBundle );
+
+ if ( warRunDependency.contextXml != null )
+ {
+ warFileToBundle = addContextXmlToWar( warRunDependency.contextXml, warFileToBundle );
+ }
+ final String warFileName = artifact.getFile().getName();
+ os.putArchiveEntry( new JarArchiveEntry( warFileName ) );
+ IOUtils.copy( new FileInputStream( warFileToBundle ), os );
+ os.closeArchiveEntry();
+ String propertyWarValue = properties.getProperty(Tomcat9Runner.WARS_KEY );
+ String contextPath =
+ StringUtils.isEmpty( warRunDependency.contextPath ) ? "/" : warRunDependency.contextPath;
+ if ( propertyWarValue != null )
+ {
+ properties.put(Tomcat9Runner.WARS_KEY,
+ propertyWarValue + ";" + warFileName + "|" + contextPath );
+ }
+ else
+ {
+ properties.put(Tomcat9Runner.WARS_KEY, warFileName + "|" + contextPath );
+ }
+ }
+ }
+ }
+
+ if ( serverXml != null && serverXml.exists() )
+ {
+ os.putArchiveEntry( new JarArchiveEntry( "conf/server.xml" ) );
+ IOUtils.copy( new FileInputStream( serverXml ), os );
+ os.closeArchiveEntry();
+ properties.put(Tomcat9Runner.USE_SERVER_XML_KEY, Boolean.TRUE.toString() );
+ }
+ else
+ {
+ properties.put(Tomcat9Runner.USE_SERVER_XML_KEY, Boolean.FALSE.toString() );
+ }
+
+ os.putArchiveEntry( new JarArchiveEntry( "conf/web.xml" ) );
+ IOUtils.copy( getClass().getResourceAsStream( "/conf/web.xml" ), os );
+ os.closeArchiveEntry();
+
+ properties.store( tmpPropertiesFileOutputStream, "created by Apache Tomcat Maven plugin" );
+
+ tmpPropertiesFileOutputStream.flush();
+ tmpPropertiesFileOutputStream.close();
+
+ os.putArchiveEntry(new JarArchiveEntry( Tomcat9RunnerCli.STAND_ALONE_PROPERTIES_FILENAME ) );
+ IOUtils.copy( new FileInputStream( tmpPropertiesFile ), os );
+ os.closeArchiveEntry();
+
+ // add tomcat classes
+ for ( Artifact pluginArtifact : pluginArtifacts )
+ {
+ if ( StringUtils.equals( "org.apache.tomcat", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "org.apache.tomcat.embed", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "org.eclipse.jdt.core.compiler", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "commons-cli", pluginArtifact.getArtifactId() ) //
+ || StringUtils.equals( "tomcat9-war-runner", pluginArtifact.getArtifactId() ) )
+ {
+ JarFile jarFile = new JarFile( pluginArtifact.getFile() );
+ extractJarToArchive( jarFile, os, null );
+ }
+ }
+
+ // add extra dependencies
+ if ( extraDependencies != null && !extraDependencies.isEmpty() )
+ {
+ for ( Dependency dependency : extraDependencies )
+ {
+ String version = dependency.getVersion();
+ if ( StringUtils.isEmpty( version ) )
+ {
+ version = findArtifactVersion( dependency );
+ }
+
+ if ( StringUtils.isEmpty( version ) )
+ {
+ throw new MojoExecutionException(
+ "Dependency '" + dependency.getGroupId() + "':'" + dependency.getArtifactId()
+ + "' does not have version specified" );
+ }
+
+ // String groupId, String artifactId, String version, String scope, String type
+ Artifact artifact = artifactFactory.createArtifact( dependency.getGroupId(), //
+ dependency.getArtifactId(), //
+ version, //
+ dependency.getScope(), //
+ dependency.getType() );
+
+ artifactResolver.resolve( artifact, this.remoteRepos, this.local );
+ JarFile jarFile = new JarFile( artifact.getFile() );
+ extractJarToArchive( jarFile, os, this.excludes );
+ }
+ }
+
+ Manifest manifest = new Manifest();
+
+ Manifest.Attribute mainClassAtt = new Manifest.Attribute();
+ mainClassAtt.setName( "Main-Class" );
+ mainClassAtt.setValue( mainClass );
+ manifest.addConfiguredAttribute( mainClassAtt );
+
+ manifest.write( tmpManifestWriter );
+ tmpManifestWriter.flush();
+ tmpManifestWriter.close();
+
+ os.putArchiveEntry( new JarArchiveEntry( "META-INF/MANIFEST.MF" ) );
+ IOUtils.copy( new FileInputStream( tmpManifestFile ), os );
+ os.closeArchiveEntry();
+
+ if ( attachArtifact )
+ {
+ //MavenProject project, String artifactType, String artifactClassifier, File artifactFile
+ projectHelper.attachArtifact( project, attachArtifactClassifierType, attachArtifactClassifier,
+ execWarJar );
+ }
+
+ if ( extraResources != null )
+ {
+ for ( ExtraResource extraResource : extraResources )
+ {
+
+ DirectoryScanner directoryScanner = new DirectoryScanner();
+ directoryScanner.setBasedir( extraResource.getDirectory() );
+ directoryScanner.addDefaultExcludes();
+ directoryScanner.setExcludes( toStringArray( extraResource.getExcludes() ) );
+ directoryScanner.setIncludes( toStringArray( extraResource.getIncludes() ) );
+ directoryScanner.scan();
+ for ( String includeFile : directoryScanner.getIncludedFiles() )
+ {
+ getLog().debug( "include file:" + includeFile );
+ os.putArchiveEntry( new JarArchiveEntry( includeFile ) );
+ IOUtils.copy( new FileInputStream( new File( extraResource.getDirectory(), includeFile ) ),
+ os );
+ os.closeArchiveEntry();
+ }
+ }
+ }
+
+ if ( tomcatConfigurationFilesDirectory != null && tomcatConfigurationFilesDirectory.exists() )
+ {
+ // Because its the tomcat default dir for configs
+ String aConfigOutputDir = "conf/";
+ copyDirectoryContentIntoArchive( tomcatConfigurationFilesDirectory, aConfigOutputDir, os );
+ }
+
+ }
+ catch ( ManifestException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( IOException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArchiveException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArtifactNotFoundException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArtifactResolutionException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ finally
+ {
+ IOUtils.closeQuietly( os );
+ IOUtils.closeQuietly( tmpManifestWriter );
+ IOUtils.closeQuietly( execWarJarOutputStream );
+ IOUtils.closeQuietly( tmpPropertiesFileOutputStream );
+ }
+ }
+
+ protected String findArtifactVersion( Dependency dependency )
+ {
+ // search in project.dependencies
+ for ( Dependency projectDependency : (List) this.project.getDependencies() )
+ {
+ if ( sameDependencyWithoutVersion( dependency, projectDependency ) )
+ {
+ return projectDependency.getVersion();
+ }
+ }
+
+ // search in project.dependencies
+ for ( Dependency projectDependency : this.project.getDependencyManagement().getDependencies() )
+ {
+ if ( sameDependencyWithoutVersion( dependency, projectDependency ) )
+ {
+ return projectDependency.getVersion();
+ }
+ }
+
+ return null;
+ }
+
+ protected boolean sameDependencyWithoutVersion( Dependency that, Dependency dependency )
+ {
+ return StringUtils.equals( that.getGroupId(), dependency.getGroupId() ) && StringUtils.equals(
+ that.getArtifactId(), dependency.getArtifactId() );
+ }
+
+ protected void copyDirectoryContentIntoArchive( File sourceFolder, String destinationPath,
+ ArchiveOutputStream archiveOutputStream )
+ throws IOException
+ {
+
+ // Scan the directory
+ DirectoryScanner directoryScanner = new DirectoryScanner();
+ directoryScanner.setBasedir( sourceFolder );
+ directoryScanner.addDefaultExcludes();
+ directoryScanner.scan();
+
+ // Each File
+ for ( String includeFileName : directoryScanner.getIncludedFiles() )
+ {
+ getLog().debug( "include configuration file : " + destinationPath + includeFileName );
+ File inputFile = new File( sourceFolder, includeFileName );
+
+ FileInputStream sourceFileInputStream = null;
+ try
+ {
+ sourceFileInputStream = new FileInputStream( inputFile );
+
+ archiveOutputStream.putArchiveEntry( new JarArchiveEntry( destinationPath + includeFileName ) );
+ IOUtils.copy( sourceFileInputStream, archiveOutputStream );
+ archiveOutputStream.closeArchiveEntry();
+ }
+ finally
+ {
+ IOUtils.closeQuietly( sourceFileInputStream );
+ }
+ }
+
+ }
+
+ /**
+ * Resolves the plugin work dir as a sub directory of {@link #buildDirectory}, creating it if it does not exist.
+ *
+ * @return File representing the resolved plugin work dir
+ * @throws MojoExecutionException if the plugin work dir cannot be created
+ */
+ protected File resolvePluginWorkDir()
+ throws MojoExecutionException
+ {
+ if ( !pluginWorkDirectory.exists() && !pluginWorkDirectory.mkdirs() )
+ {
+ throw new MojoExecutionException(
+ "Could not create plugin work directory at " + pluginWorkDirectory.getAbsolutePath() );
+ }
+
+ return pluginWorkDirectory;
+
+ }
+
+ protected String[] toStringArray( List list )
+ {
+ if ( list == null || list.isEmpty() )
+ {
+ return new String[0];
+ }
+ List res = new ArrayList( list.size() );
+
+ for ( Iterator ite = list.iterator(); ite.hasNext(); )
+ {
+ res.add( (String) ite.next() );
+ }
+ return res.toArray( new String[res.size()] );
+ }
+
+
+ /**
+ * return file can be deleted
+ */
+ protected File addContextXmlToWar( File contextXmlFile, File warFile )
+ throws IOException, ArchiveException
+ {
+ ArchiveOutputStream os = null;
+ OutputStream warOutputStream = null;
+ File tmpWar = File.createTempFile( "tomcat", "war-exec" );
+ tmpWar.deleteOnExit();
+
+ try
+ {
+ warOutputStream = new FileOutputStream( tmpWar );
+ os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR, warOutputStream );
+ os.putArchiveEntry( new JarArchiveEntry( "META-INF/context.xml" ) );
+ IOUtils.copy( new FileInputStream( contextXmlFile ), os );
+ os.closeArchiveEntry();
+
+ JarFile jarFile = new JarFile( warFile );
+ extractJarToArchive( jarFile, os, null );
+ os.flush();
+ }
+ finally
+ {
+ IOUtils.closeQuietly( os );
+ IOUtils.closeQuietly( warOutputStream );
+ }
+ return tmpWar;
+ }
+
+ /**
+ * Copy the contents of a jar file to another archive
+ *
+ * @param file The input jar file
+ * @param os The output archive
+ * @throws IOException
+ */
+ protected void extractJarToArchive( JarFile file, ArchiveOutputStream os, String[] excludes )
+ throws IOException
+ {
+ Enumeration extends JarEntry> entries = file.entries();
+ while ( entries.hasMoreElements() )
+ {
+ JarEntry j = entries.nextElement();
+
+ if ( excludes != null && excludes.length > 0 )
+ {
+ for ( String exclude : excludes )
+ {
+ if ( SelectorUtils.match( exclude, j.getName() ) )
+ {
+ continue;
+ }
+ }
+ }
+
+ if ( StringUtils.equalsIgnoreCase( j.getName(), "META-INF/MANIFEST.MF" ) )
+ {
+ continue;
+ }
+ os.putArchiveEntry( new JarArchiveEntry( j.getName() ) );
+ IOUtils.copy( file.getInputStream( j ), os );
+ os.closeArchiveEntry();
+ }
+ if ( file != null )
+ {
+ file.close();
+ }
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunMojo.java
new file mode 100644
index 00000000..1e9ffb07
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunMojo.java
@@ -0,0 +1,1563 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.realm.MemoryRealm;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.startup.Catalina;
+import org.apache.catalina.startup.CatalinaProperties;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.valves.AccessLogValve;
+import org.apache.catalina.webresources.StandardRoot;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.filtering.MavenFileFilter;
+import org.apache.maven.shared.filtering.MavenFileFilterRequest;
+import org.apache.maven.shared.filtering.MavenFilteringException;
+import org.apache.tomcat.JarScanner;
+import org.apache.tomcat.maven.common.config.AbstractWebapp;
+import org.apache.tomcat.maven.common.run.EmbeddedRegistry;
+import org.apache.tomcat.maven.common.run.ExternalRepositoriesReloadableWebappLoader;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractTomcat9Mojo;
+import org.apache.tomcat.util.scan.StandardJarScanner;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
+import org.codehaus.plexus.classworlds.ClassWorld;
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
+import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public abstract class AbstractRunMojo
+ extends AbstractTomcat9Mojo
+{
+ // ---------------------------------------------------------------------
+ // Mojo Components
+ // ---------------------------------------------------------------------
+
+ /**
+ * Used to look up Artifacts in the remote repository.
+ */
+ @Component
+ protected ArtifactFactory factory;
+
+ /**
+ * Location of the local repository.
+ */
+ @Parameter( defaultValue = "${localRepository}", required = true, readonly = true )
+ private ArtifactRepository local;
+
+ /**
+ * Used to look up Artifacts in the remote repository.
+ */
+ @Component
+ protected ArtifactResolver resolver;
+
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The packaging of the Maven project that this goal operates upon.
+ */
+ @Parameter( defaultValue = "${project.packaging}", required = true, readonly = true )
+ private String packaging;
+
+ /**
+ * The directory to create the Tomcat server configuration under.
+ */
+ @Parameter( defaultValue = "${project.build.directory}/tomcat" )
+ private File configurationDir;
+
+ /**
+ * The port to run the Tomcat server on.
+ * Will be exposed as System props and session.executionProperties with key tomcat.maven.http.port
+ */
+ @Parameter( property = "maven.tomcat.port", defaultValue = "8080" )
+ private int port;
+
+ /**
+ * this IP address will be used on all ports
+ *
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.address" )
+ private String address;
+
+ /**
+ * The AJP port to run the Tomcat server on.
+ * By default it's 0 this means won't be started.
+ * The ajp connector will be started only for value > 0.
+ * Will be exposed as System props and session.executionProperties with key tomcat.maven.ajp.port
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.ajp.port", defaultValue = "0" )
+ private int ajpPort;
+
+ /**
+ * The AJP protocol to run the Tomcat server on.
+ * By default it's ajp.
+ * NOTE The ajp connector will be started only if {@link #ajpPort} > 0.
+ * possible values are:
+ *
+ *
org.apache.coyote.ajp.AjpProtocol - new blocking Java connector that supports an executor
+ *
org.apache.coyote.ajp.AjpAprProtocol - the APR/native connector.
+ *
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.ajp.protocol", defaultValue = "org.apache.coyote.ajp.AjpProtocol" )
+ private String ajpProtocol;
+
+ /**
+ * The https port to run the Tomcat server on.
+ * By default it's 0 this means won't be started.
+ * The https connector will be started only for value > 0.
+ * Will be exposed as System props and session.executionProperties with key tomcat.maven.https.port
+ *
+ * @since 1.0
+ */
+ @Parameter( property = "maven.tomcat.httpsPort", defaultValue = "0" )
+ private int httpsPort;
+
+ /**
+ * The max post size to run the Tomcat server on.
+ * By default it's 2097152 bytes. That's the default Tomcat configuration.
+ * Set this value to 0 or less to disable the post size limit.
+ *
+ * @since 2.3
+ */
+ @Parameter( property = "maven.tomcat.maxPostSize", defaultValue = "2097152" )
+ private int maxPostSize;
+
+ /**
+ * The character encoding to use for decoding URIs.
+ *
+ * @since 1.0
+ */
+ @Parameter( property = "maven.tomcat.uriEncoding", defaultValue = "ISO-8859-1" )
+ private String uriEncoding;
+
+ /**
+ * List of System properties to pass to the Tomcat Server.
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter
+ private Map systemProperties;
+
+ /**
+ * The directory contains additional configuration Files that copied in the Tomcat conf Directory.
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter( property = "maven.tomcat.additionalConfigFilesDir", defaultValue = "${basedir}/src/main/tomcatconf" )
+ private File additionalConfigFilesDir;
+
+ /**
+ * server.xml to use Note if you use this you must configure in this file your webapp paths.
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter( property = "maven.tomcat.serverXml" )
+ private File serverXml;
+
+ /**
+ * overriding the providing web.xml to run tomcat
+ * This override the global Tomcat web.xml located in $CATALINA_HOME/conf/
+ *
+ * @since 1.0-alpha-2
+ */
+ @Parameter( property = "maven.tomcat.webXml" )
+ private File tomcatWebXml;
+
+ /**
+ * Set this to true to allow Maven to continue to execute after invoking
+ * the run goal.
+ *
+ * @since 1.0
+ */
+ @Parameter( property = "maven.tomcat.fork", defaultValue = "false" )
+ private boolean fork;
+
+ /**
+ * Will create a tomcat context for each dependencies of war type with 'scope' set to 'tomcat'.
+ * In other words, dependencies with:
+ *
+ * To preserve backward compatibility it's false by default.
+ *
+ * @since 1.0
+ * @deprecated use webapps instead
+ */
+ @Parameter( property = "maven.tomcat.addContextWarDependencies", defaultValue = "false" )
+ private boolean addContextWarDependencies;
+
+ /**
+ * The maven project.
+ *
+ * @since 1.0
+ */
+ @Component
+ protected MavenProject project;
+
+ /**
+ * The archive manager.
+ *
+ * @since 1.0
+ */
+ @Component
+ private ArchiverManager archiverManager;
+
+ /**
+ * if true a new classLoader separated from maven core will be created to start tomcat.
+ *
+ * @since 1.0
+ */
+ @Parameter( property = "tomcat.useSeparateTomcatClassLoader", defaultValue = "false" )
+ protected boolean useSeparateTomcatClassLoader;
+
+ /**
+ * @since 1.0
+ */
+ @Parameter( defaultValue = "${plugin.artifacts}", required = true )
+ private List pluginArtifacts;
+
+ /**
+ * If set to true ignore if packaging of project is not 'war'.
+ *
+ * @since 1.0
+ */
+ @Parameter( property = "tomcat.ignorePackaging", defaultValue = "false" )
+ private boolean ignorePackaging;
+
+ /**
+ * Override the default keystoreFile for the HTTPS connector (if enabled)
+ *
+ * @since 1.1
+ */
+ @Parameter
+ private String keystoreFile;
+
+ /**
+ * Override the default keystorePass for the HTTPS connector (if enabled)
+ *
+ * @since 1.1
+ */
+ @Parameter
+ private String keystorePass;
+
+ /**
+ * Override the type of keystore file to be used for the server certificate. If not specified, the default value is "JKS".
+ *
+ * @since 2.0
+ */
+ @Parameter( defaultValue = "JKS" )
+ private String keystoreType;
+
+ /**
+ *
+ * Enables or disables naming support for the embedded Tomcat server.
+ *
+ *
+ * Note: This setting is ignored if you provide a server.xml for your
+ * Tomcat. Instead please configure naming in the server.xml.
+ *
+ *
+ * @see org.apache.catalina.startup.Embedded
+ * @see org.apache.catalina.startup.Tomcat
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.useNaming", defaultValue = "true" )
+ private boolean useNaming;
+
+ /**
+ * Force context scanning if you don't use a context file with reloadable = "true".
+ * The other way to use contextReloadable is to add attribute reloadable = "true"
+ * in your context file.
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.contextReloadable", defaultValue = "false" )
+ protected boolean contextReloadable;
+
+ /**
+ * represents the delay in seconds between each classPathScanning change invocation
+ *
+ * @see http://tomcat.apache.org/tomcat-7.0-doc/config/context.html
+ */
+ @Parameter( property = "maven.tomcat.backgroundProcessorDelay", defaultValue = "-1" )
+ protected int backgroundProcessorDelay = -1;
+
+
+ /**
+ *
The path of the Tomcat context XML file.
+ *
Since release 2.0, the file is filtered as a maven resource so you can use
+ * interpolation tokens ${ }
+ */
+ @Parameter( property = "maven.tomcat.contextFile" )
+ protected File contextFile;
+
+ /**
+ * The default context file to check for if contextFile not configured.
+ * If no contextFile configured and the below default not present, no
+ * contextFile will be sent to Tomcat, resulting in the latter's default
+ * context configuration being used instead.
+ */
+ @Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}/META-INF/context.xml",
+ readonly = true )
+ private File defaultContextFile;
+
+ /**
+ * The protocol to run the Tomcat server on.
+ * By default it's HTTP/1.1.
+ * See possible values HTTP Connector
+ * protocol attribute
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.protocol", defaultValue = "HTTP/1.1" )
+ private String protocol;
+
+ /**
+ * The path of the Tomcat users XML file.
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.tomcatUsers.file" )
+ private File tomcatUsers;
+
+ /**
+ * The path of the Tomcat logging configuration.
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.tomcatLogging.file" )
+ private File tomcatLoggingFile;
+
+ /**
+ * Skip execution
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.skip", defaultValue = "false" )
+ protected boolean skip;
+
+ /**
+ * Collection of webapp artifacts to be deployed. Elements are <webapp> and contain
+ * usual GAVC plus contextPath and/or contextFile elements.
+ *
+ * @see {@link Webapp}
+ * @since 2.0
+ */
+ @Parameter
+ private List webapps;
+
+ /**
+ * The static context
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.staticContextPath", defaultValue = "/" )
+ private String staticContextPath;
+
+ /**
+ * The static context docroot base fully qualified path
+ * if null static context won't be added
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.staticContextDocbase" )
+ private String staticContextDocbase;
+
+ /**
+ * Class loader class to set.
+ *
+ * @since 2.0
+ */
+ @Parameter
+ protected String classLoaderClass;
+
+ @Parameter( defaultValue = "${session}", readonly = true, required = true )
+ protected MavenSession session;
+
+ /**
+ * Will dump port in a properties file (see ports for property names).
+ * If empty no file generated
+ */
+ @Parameter( property = "maven.tomcat.propertiesPortFilePath" )
+ protected String propertiesPortFilePath;
+
+ /**
+ * configure host name
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.hostName", defaultValue = "localhost" )
+ protected String hostName;
+
+ /**
+ * configure aliases
+ * see Host Name aliases
+ *
+ * @since 2.0
+ */
+ @Parameter
+ protected String[] aliases;
+
+ /**
+ * enable client authentication for https (if configured)
+ * see http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support_-_BIO_and_NIO
+ *
+ * @since 2.1
+ */
+ @Parameter( property = "maven.tomcat.https.clientAuth", defaultValue = "false" )
+ protected String clientAuth = "false";
+
+ @Component( role = MavenFileFilter.class, hint = "default" )
+ protected MavenFileFilter mavenFileFilter;
+
+
+ /**
+ * In case a module in your reactors has some web-fragments they will be read.
+ * If you don't need that for performance reasons, you can deactivate it.
+ *
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.jarScan.allDirectories", defaultValue = "true" )
+ protected boolean jarScanAllDirectories = true;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.useBodyEncodingForURI", defaultValue = "false" )
+ protected boolean useBodyEncodingForURI;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String trustManagerClassName;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String trustMaxCertLength;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String truststoreAlgorithm;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String truststoreFile;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String truststorePass;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String truststoreProvider;
+
+ /**
+ * @since 2.2
+ */
+ @Parameter
+ protected String truststoreType;
+
+ // ----------------------------------------------------------------------
+ // Fields
+ // ----------------------------------------------------------------------
+
+ /**
+ * @since 1.0
+ */
+ private ClassRealm tomcatRealm;
+
+ // ----------------------------------------------------------------------
+ // Mojo Implementation
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ public void execute()
+ throws MojoExecutionException, MojoFailureException
+ {
+ if ( skip )
+ {
+ getLog().info( "skip execution" );
+ return;
+ }
+ // ensure project is a web application
+ if ( !isWar() && !addContextWarDependencies && getAdditionalWebapps().isEmpty() )
+ {
+ getLog().info( messagesProvider.getMessage( "AbstractRunMojo.nonWar" ) );
+ return;
+ }
+ ClassLoader originalClassLoader = null;
+ if ( useSeparateTomcatClassLoader )
+ {
+ originalClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+ try
+ {
+ getLog().info( messagesProvider.getMessage( "AbstractRunMojo.runningWar", getWebappUrl() ) );
+
+ initConfiguration();
+ startContainer();
+ if ( !fork )
+ {
+ waitIndefinitely();
+ }
+ }
+ catch ( LifecycleException exception )
+ {
+ throw new MojoExecutionException( messagesProvider.getMessage( "AbstractRunMojo.cannotStart" ), exception );
+ }
+ catch ( IOException exception )
+ {
+ throw new MojoExecutionException(
+ messagesProvider.getMessage( "AbstractRunMojo.cannotCreateConfiguration" ), exception );
+ }
+ catch ( ServletException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( MavenFilteringException e )
+ {
+ throw new MojoExecutionException( "filtering issue: " + e.getMessage(), e );
+ }
+ finally
+ {
+ if ( useSeparateTomcatClassLoader )
+ {
+ Thread.currentThread().setContextClassLoader( originalClassLoader );
+ }
+ }
+ }
+
+ // ----------------------------------------------------------------------
+ // Protected Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Gets the webapp context path to use for the web application being run.
+ *
+ * @return the webapp context path
+ */
+ @Override
+ protected String getPath()
+ {
+ return path;
+ }
+
+ protected void enhanceContext( final Context context )
+ throws MojoExecutionException
+ {
+ // no op
+ }
+
+
+ /**
+ * Gets the context to run this web application under for the specified embedded Tomcat.
+ *
+ * @param container the embedded Tomcat container being used
+ * @return the context to run this web application under
+ * @throws IOException if the context could not be created
+ * @throws MojoExecutionException in case of an error creating the context
+ */
+ protected Context createContext( Tomcat container )
+ throws IOException, MojoExecutionException, ServletException
+ {
+ String contextPath = getPath();
+
+ String baseDir = getDocBase().getAbsolutePath();
+
+ File overriddenContextFile = getContextFile();
+
+ StandardContext standardContext = null;
+
+ if ( overriddenContextFile != null && overriddenContextFile.exists() )
+ {
+ getLog().info("Using dynamic context configuration file: " + overriddenContextFile.getAbsolutePath());
+ standardContext = parseContextFile( overriddenContextFile );
+ }
+ else if ( defaultContextFile.exists() )
+ {
+ getLog().info("Using default context configuration file: " + defaultContextFile.getAbsolutePath());
+ standardContext = parseContextFile( defaultContextFile );
+ }
+
+ if ( standardContext != null )
+ {
+ if ( standardContext.getPath() != null )
+ {
+ contextPath = standardContext.getPath();
+ }
+ if ( standardContext.getDocBase() != null )
+ {
+ baseDir = standardContext.getDocBase();
+ }
+ }
+
+ contextPath = "/".equals( contextPath ) ? "" : contextPath;
+
+ getLog().info( "create webapp with contextPath: " + contextPath );
+
+ Context context = container.addWebapp( contextPath, baseDir );
+
+ StandardRoot root = new StandardRoot(context);
+ context.setResources(root);
+
+ if ( useSeparateTomcatClassLoader )
+ {
+ context.setParentClassLoader( getTomcatClassLoader() );
+ }
+
+ enhanceContext( context );
+
+ final WebappLoader loader = createWebappLoader();
+
+ context.setLoader( loader );
+
+ if ( overriddenContextFile != null )
+ {
+ // here, send file to Tomcat for it to complain if missing
+ context.setConfigFile( overriddenContextFile.toURI().toURL() );
+ }
+ else if ( defaultContextFile.exists() )
+ {
+ // here, only sending default file if it indeed exists
+ // otherwise Tomcat will create a default context
+ context.setConfigFile( defaultContextFile.toURI().toURL() );
+ }
+
+ if ( classLoaderClass != null )
+ {
+ loader.setLoaderClass( classLoaderClass );
+ }
+
+ // https://issues.apache.org/jira/browse/MTOMCAT-239
+ // get the jar scanner to configure scanning directories as we can run a jar or a reactor project with a jar so
+ // the entries is a directory (target/classes)
+ JarScanner jarScanner = context.getJarScanner();
+
+ // normally this one only but just in case ...
+ if ( jarScanner instanceof StandardJarScanner )
+ {
+ ( (StandardJarScanner) jarScanner ).setScanAllDirectories( jarScanAllDirectories );
+ }
+
+ return context;
+
+ }
+
+ protected StandardContext parseContextFile( File file )
+ throws MojoExecutionException
+ {
+ try
+ {
+ StandardContext standardContext = new StandardContext();
+ XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader( new FileInputStream( file ) );
+
+ int tag = reader.next();
+
+ while ( true )
+ {
+ if ( tag == XMLStreamConstants.START_ELEMENT && StringUtils.equals( "Context", reader.getLocalName() ) )
+ {
+ String path = reader.getAttributeValue( null, "path" );
+ if ( StringUtils.isNotBlank( path ) )
+ {
+ standardContext.setPath( path );
+ }
+
+ String docBase = reader.getAttributeValue( null, "docBase" );
+ if ( StringUtils.isNotBlank( docBase ) )
+ {
+ standardContext.setDocBase( docBase );
+ }
+ }
+ if ( !reader.hasNext() )
+ {
+ break;
+ }
+ tag = reader.next();
+ }
+
+ return standardContext;
+ }
+ catch ( XMLStreamException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( FileNotFoundException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ }
+
+ /**
+ * Gets the webapp loader to run this web application under.
+ *
+ * @return the webapp loader to use
+ * @throws IOException if the webapp loader could not be created
+ * @throws MojoExecutionException in case of an error creating the webapp loader
+ */
+ protected WebappLoader createWebappLoader()
+ throws IOException, MojoExecutionException
+ {
+ WebappLoader webappLoader = null;
+ if ( useSeparateTomcatClassLoader )
+ {
+ if ( isContextReloadable() )
+ {
+ webappLoader = new ExternalRepositoriesReloadableWebappLoader( getTomcatClassLoader(), getLog() );
+ }
+ else
+ {
+ webappLoader = new WebappLoader( getTomcatClassLoader() );
+ }
+ }
+ else
+ {
+ if ( isContextReloadable() )
+ {
+ webappLoader =
+ new ExternalRepositoriesReloadableWebappLoader( Thread.currentThread().getContextClassLoader(),
+ getLog() );
+ }
+ else
+ {
+ webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
+ }
+ }
+ return webappLoader;
+ }
+
+ /**
+ * Determine whether the passed context.xml file declares the context as reloadable or not.
+ *
+ * @return false by default, true if reloadable="true" in context.xml.
+ */
+ protected boolean isContextReloadable()
+ throws MojoExecutionException
+ {
+ if ( contextReloadable || backgroundProcessorDelay > 0 )
+ {
+ return true;
+ }
+ // determine whether to use a reloadable Loader or not (default is false).
+ boolean reloadable = false;
+ try
+ {
+ if ( contextFile != null && contextFile.exists() )
+ {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = builderFactory.newDocumentBuilder();
+ Document contextDoc = builder.parse( contextFile );
+ contextDoc.getDocumentElement().normalize();
+
+ NamedNodeMap nodeMap = contextDoc.getDocumentElement().getAttributes();
+ Node reloadableAttribute = nodeMap.getNamedItem( "reloadable" );
+
+ reloadable =
+ ( reloadableAttribute != null ) ? Boolean.valueOf( reloadableAttribute.getNodeValue() ) : false;
+ }
+ getLog().debug( "context reloadable: " + reloadable );
+ }
+ catch ( IOException ioe )
+ {
+ getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", ioe );
+ }
+ catch ( ParserConfigurationException pce )
+ {
+ getLog().error( "Could not configure XML parser", pce );
+ }
+ catch ( SAXException se )
+ {
+ getLog().error( "Could not parse file: [" + contextFile.getAbsolutePath() + "]", se );
+ }
+
+ return reloadable;
+ }
+
+
+ /**
+ * Gets the webapp directory to run.
+ *
+ * @return the webapp directory
+ */
+ protected abstract File getDocBase()
+ throws IOException;
+
+ /**
+ * Gets the Tomcat context XML file to use.
+ *
+ * @return the context XML file
+ */
+ protected abstract File getContextFile()
+ throws MojoExecutionException;
+
+ // ----------------------------------------------------------------------
+ // Private Methods
+ // ----------------------------------------------------------------------
+
+ /**
+ * Gets whether this project uses WAR packaging.
+ *
+ * @return whether this project uses WAR packaging
+ */
+ protected boolean isWar()
+ {
+ return "war".equals( packaging ) || ignorePackaging;
+ }
+
+ /**
+ * Gets the URL of the running webapp.
+ *
+ * @return the URL of the running webapp
+ * @throws java.net.MalformedURLException if the running webapp URL is invalid
+ */
+ private URL getWebappUrl()
+ throws MalformedURLException
+ {
+ return new URL( "http", "localhost", port, getPath() );
+ }
+
+ /**
+ * FIXME not sure we need all of those files with tomcat9
+ * Creates the Tomcat configuration directory with the necessary resources.
+ *
+ * @throws IOException if the Tomcat configuration could not be created
+ * @throws MojoExecutionException if the Tomcat configuration could not be created
+ */
+ private void initConfiguration()
+ throws IOException, MojoExecutionException, MavenFilteringException
+ {
+ if ( configurationDir.exists() )
+ {
+ getLog().info( messagesProvider.getMessage( "AbstractRunMojo.usingConfiguration", configurationDir ) );
+ }
+ else
+ {
+ getLog().info( messagesProvider.getMessage( "AbstractRunMojo.creatingConfiguration", configurationDir ) );
+
+ configurationDir.mkdirs();
+
+ File confDir = new File( configurationDir, "conf" );
+ confDir.mkdir();
+
+ if ( tomcatLoggingFile != null )
+ {
+ FileUtils.copyFile( tomcatLoggingFile, new File( confDir, "logging.properties" ) );
+ }
+ else
+ {
+ copyFile( "/conf/logging.properties", new File( confDir, "logging.properties" ) );
+ }
+
+ copyFile( "/conf/tomcat-users.xml", new File( confDir, "tomcat-users.xml" ) );
+ if ( tomcatWebXml != null )
+ {
+ if ( !tomcatWebXml.exists() )
+ {
+ throw new MojoExecutionException( " tomcatWebXml " + tomcatWebXml.getPath() + " not exists" );
+ }
+ //MTOMCAT-42 here it's a real file resources not a one coming with the mojo
+ //MTOMCAT-128 apply filtering
+ MavenFileFilterRequest mavenFileFilterRequest = new MavenFileFilterRequest();
+ mavenFileFilterRequest.setFrom( tomcatWebXml );
+ mavenFileFilterRequest.setTo( new File( confDir, "web.xml" ) );
+ mavenFileFilterRequest.setMavenProject( project );
+ mavenFileFilterRequest.setMavenSession( session );
+ mavenFileFilterRequest.setFiltering( true );
+
+ mavenFileFilter.copyFile( mavenFileFilterRequest );
+
+ }
+ else
+ {
+ copyFile( "/conf/web.xml", new File( confDir, "web.xml" ) );
+ }
+
+ File logDir = new File( configurationDir, "logs" );
+ logDir.mkdir();
+
+ File webappsDir = new File( configurationDir, "webapps" );
+ webappsDir.mkdir();
+
+ if ( additionalConfigFilesDir != null && additionalConfigFilesDir.exists() )
+ {
+ DirectoryScanner scanner = new DirectoryScanner();
+ scanner.addDefaultExcludes();
+ scanner.setBasedir( additionalConfigFilesDir.getPath() );
+ scanner.scan();
+
+ String[] files = scanner.getIncludedFiles();
+
+ if ( files != null && files.length > 0 )
+ {
+ getLog().info( "Coping additional tomcat config files" );
+
+ for ( int i = 0; i < files.length; i++ )
+ {
+ File file = new File( additionalConfigFilesDir, files[i] );
+
+ getLog().info( " copy " + file.getName() );
+
+ FileUtils.copyFileToDirectory( file, confDir );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Copies the specified class resource to the specified file.
+ *
+ * @param fromPath the path of the class resource to copy
+ * @param toFile the file to copy to
+ * @throws IOException if the file could not be copied
+ */
+ private void copyFile( String fromPath, File toFile )
+ throws IOException
+ {
+ URL fromURL = getClass().getResource( fromPath );
+
+ if ( fromURL == null )
+ {
+ throw new FileNotFoundException( fromPath );
+ }
+
+ FileUtils.copyURLToFile( fromURL, toFile );
+ }
+
+ /**
+ * Starts the embedded Tomcat server.
+ *
+ * @throws IOException if the server could not be configured
+ * @throws LifecycleException if the server could not be started
+ * @throws MojoExecutionException if the server could not be configured
+ */
+ private void startContainer()
+ throws IOException, LifecycleException, MojoExecutionException, ServletException
+ {
+ String previousCatalinaBase = System.getProperty( "catalina.base" );
+
+ try
+ {
+ // Default to headless
+ System.setProperty("java.awt.headless", "true");
+ System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
+ // Set the system properties
+ setupSystemProperties();
+
+ System.setProperty( "catalina.base", configurationDir.getAbsolutePath() );
+
+ if ( serverXml != null )
+ {
+ if ( !serverXml.exists() )
+ {
+ throw new MojoExecutionException( serverXml.getPath() + " not exists" );
+ }
+
+ Catalina container = new Catalina();
+
+ if ( useSeparateTomcatClassLoader )
+ {
+ Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
+ container.setParentClassLoader( getTomcatClassLoader() );
+ }
+
+ container.setUseNaming( this.useNaming );
+ container.setConfigFile( serverXml.getAbsolutePath() );
+ container.start();
+ EmbeddedRegistry.getInstance().register( container );
+ }
+ else
+ {
+
+ System.setProperty( "java.util.logging.manager", "org.apache.juli.ClassLoaderLogManager" );
+ System.setProperty( "java.util.logging.config.file",
+ new File( configurationDir, "conf/logging.properties" ).toString() );
+
+ // Trigger loading of catalina.properties
+ CatalinaProperties.getProperty( "foo" );
+
+ Tomcat embeddedTomcat = new ExtendedTomcat( configurationDir );
+
+ embeddedTomcat.setBaseDir( configurationDir.getAbsolutePath() );
+ MemoryRealm memoryRealm = new MemoryRealm();
+
+ if ( tomcatUsers != null )
+ {
+ if ( !tomcatUsers.exists() )
+ {
+ throw new MojoExecutionException( " tomcatUsers " + tomcatUsers.getPath() + " not exists" );
+ }
+ getLog().info( "use tomcat-users.xml from " + tomcatUsers.getAbsolutePath() );
+ memoryRealm.setPathname( tomcatUsers.getAbsolutePath() );
+ }
+
+ embeddedTomcat.getEngine().setRealm( memoryRealm );
+
+ Context ctx = createContext( embeddedTomcat );
+
+ if ( useNaming )
+ {
+ embeddedTomcat.enableNaming();
+ }
+
+ embeddedTomcat.getHost().setAppBase( new File( configurationDir, "webapps" ).getAbsolutePath() );
+
+ if ( hostName != null )
+ {
+ embeddedTomcat.getHost().setName( hostName );
+ }
+ if ( aliases != null )
+ {
+ for ( String alias : aliases )
+ {
+ embeddedTomcat.getHost().addAlias( alias );
+ }
+
+ }
+ createStaticContext( embeddedTomcat, ctx, embeddedTomcat.getHost() );
+
+ Connector connector = new Connector( protocol );
+ connector.setPort( port );
+ connector.setMaxPostSize( maxPostSize );
+
+ if ( httpsPort > 0 )
+ {
+ connector.setRedirectPort( httpsPort );
+ }
+
+ if ( address != null )
+ {
+ connector.setAttribute( "address", address );
+ }
+
+ connector.setURIEncoding( uriEncoding );
+
+ connector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
+
+ embeddedTomcat.getService().addConnector( connector );
+
+ embeddedTomcat.setConnector( connector );
+
+ AccessLogValve alv = new AccessLogValve();
+ alv.setDirectory( new File( configurationDir, "logs" ).getAbsolutePath() );
+ alv.setPattern( "%h %l %u %t \"%r\" %s %b %I %D" );
+ embeddedTomcat.getHost().getPipeline().addValve( alv );
+
+ // create https connector
+ Connector httpsConnector = null;
+ if ( httpsPort > 0 )
+ {
+ httpsConnector = new Connector( protocol );
+ httpsConnector.setPort( httpsPort );
+ httpsConnector.setMaxPostSize( maxPostSize );
+ httpsConnector.setSecure( true );
+ httpsConnector.setProperty( "SSLEnabled", "true" );
+ // should be default but configure it anyway
+ httpsConnector.setProperty( "sslProtocol", "TLS" );
+ if ( keystoreFile != null )
+ {
+ httpsConnector.setAttribute( "keystoreFile", keystoreFile );
+ }
+ if ( keystorePass != null )
+ {
+ httpsConnector.setAttribute( "keystorePass", keystorePass );
+ }
+ if ( keystoreType != null )
+ {
+ httpsConnector.setAttribute( "keystoreType", keystoreType );
+ }
+
+ if ( trustManagerClassName != null )
+ {
+ httpsConnector.setAttribute( "trustManagerClassName", trustManagerClassName );
+ }
+
+ if ( trustMaxCertLength != null )
+ {
+ httpsConnector.setAttribute( "trustMaxCertLength", trustMaxCertLength );
+ }
+
+ if ( truststoreAlgorithm != null )
+ {
+ httpsConnector.setAttribute( "truststoreAlgorithm", truststoreAlgorithm );
+ }
+
+ if ( truststoreFile != null )
+ {
+ httpsConnector.setAttribute( "truststoreFile", truststoreFile );
+ }
+
+ if ( truststorePass != null )
+ {
+ httpsConnector.setAttribute( "truststorePass", truststorePass );
+ }
+
+ if ( truststoreProvider != null )
+ {
+ httpsConnector.setAttribute( "truststoreProvider", truststoreProvider );
+ }
+
+ if ( truststoreType != null )
+ {
+ httpsConnector.setAttribute( "truststoreType", truststoreType );
+ }
+
+ httpsConnector.setAttribute( "clientAuth", clientAuth );
+
+ httpsConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
+
+ if ( address != null )
+ {
+ httpsConnector.setAttribute( "address", address );
+ }
+
+ embeddedTomcat.getEngine().getService().addConnector( httpsConnector );
+
+ }
+
+ // create ajp connector
+ Connector ajpConnector = null;
+ if ( ajpPort > 0 )
+ {
+ ajpConnector = new Connector( ajpProtocol );
+ ajpConnector.setPort( ajpPort );
+ ajpConnector.setURIEncoding( uriEncoding );
+ ajpConnector.setUseBodyEncodingForURI( this.useBodyEncodingForURI );
+ if ( address != null )
+ {
+ ajpConnector.setAttribute( "address", address );
+ }
+ embeddedTomcat.getEngine().getService().addConnector( ajpConnector );
+ }
+
+ if ( addContextWarDependencies || !getAdditionalWebapps().isEmpty() )
+ {
+ createDependencyContexts( embeddedTomcat );
+ }
+
+ if ( useSeparateTomcatClassLoader )
+ {
+ Thread.currentThread().setContextClassLoader( getTomcatClassLoader() );
+ embeddedTomcat.getEngine().setParentClassLoader( getTomcatClassLoader() );
+ }
+
+ embeddedTomcat.start();
+
+ Properties portProperties = new Properties();
+
+ portProperties.put( "tomcat.maven.http.port", Integer.toString( connector.getLocalPort() ) );
+
+ session.getExecutionProperties().put( "tomcat.maven.http.port",
+ Integer.toString( connector.getLocalPort() ) );
+ System.setProperty( "tomcat.maven.http.port", Integer.toString( connector.getLocalPort() ) );
+
+ if ( httpsConnector != null )
+ {
+ session.getExecutionProperties().put( "tomcat.maven.https.port",
+ Integer.toString( httpsConnector.getLocalPort() ) );
+ portProperties.put( "tomcat.maven.https.port", Integer.toString( httpsConnector.getLocalPort() ) );
+ System.setProperty( "tomcat.maven.https.port", Integer.toString( httpsConnector.getLocalPort() ) );
+ }
+
+ if ( ajpConnector != null )
+ {
+ session.getExecutionProperties().put( "tomcat.maven.ajp.port",
+ Integer.toString( ajpConnector.getLocalPort() ) );
+ portProperties.put( "tomcat.maven.ajp.port", Integer.toString( ajpConnector.getLocalPort() ) );
+ System.setProperty( "tomcat.maven.ajp.port", Integer.toString( ajpConnector.getLocalPort() ) );
+ }
+ if ( propertiesPortFilePath != null )
+ {
+ File propertiesPortsFile = new File( propertiesPortFilePath );
+ if ( propertiesPortsFile.exists() )
+ {
+ propertiesPortsFile.delete();
+ }
+ FileOutputStream fileOutputStream = new FileOutputStream( propertiesPortsFile );
+ try
+ {
+ portProperties.store( fileOutputStream, "Apache Tomcat Maven plugin port used" );
+ }
+ finally
+ {
+ IOUtils.closeQuietly( fileOutputStream );
+ }
+ }
+
+ EmbeddedRegistry.getInstance().register( embeddedTomcat );
+
+ }
+
+
+ }
+ finally
+ {
+ if ( previousCatalinaBase != null )
+ {
+ System.setProperty( "catalina.base", previousCatalinaBase );
+ }
+ }
+ }
+
+ private List getAdditionalWebapps()
+ {
+ if ( webapps == null )
+ {
+ return Collections.emptyList();
+ }
+ return webapps;
+ }
+
+ protected ClassRealm getTomcatClassLoader()
+ throws MojoExecutionException
+ {
+ if ( this.tomcatRealm != null )
+ {
+ return tomcatRealm;
+ }
+ try
+ {
+ ClassWorld world = new ClassWorld();
+ ClassRealm root = world.newRealm( "tomcat", Thread.currentThread().getContextClassLoader() );
+
+ for ( Iterator i = pluginArtifacts.iterator(); i.hasNext(); )
+ {
+ Artifact pluginArtifact = i.next();
+ // add all plugin artifacts see https://issues.apache.org/jira/browse/MTOMCAT-122
+ if ( pluginArtifact.getFile() != null )
+ {
+ root.addURL( pluginArtifact.getFile().toURI().toURL() );
+ }
+
+ }
+ tomcatRealm = root;
+ return root;
+ }
+ catch ( DuplicateRealmException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( MalformedURLException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public Set getProjectArtifacts()
+ {
+ return project.getArtifacts();
+ }
+
+ /**
+ * Causes the current thread to wait indefinitely. This method does not return.
+ */
+ private void waitIndefinitely()
+ {
+ Object lock = new Object();
+
+ synchronized ( lock )
+ {
+ try
+ {
+ lock.wait();
+ }
+ catch ( InterruptedException exception )
+ {
+ getLog().warn( messagesProvider.getMessage( "AbstractRunMojo.interrupted" ), exception );
+ }
+ }
+ }
+
+
+ /**
+ * Set the SystemProperties from the configuration.
+ */
+ private void setupSystemProperties()
+ {
+ if ( systemProperties != null && !systemProperties.isEmpty() )
+ {
+ getLog().info( "setting SystemProperties:" );
+
+ for ( String key : systemProperties.keySet() )
+ {
+ String value = systemProperties.get( key );
+
+ if ( value != null )
+ {
+ getLog().info( " " + key + "=" + value );
+ System.setProperty( key, value );
+ }
+ else
+ {
+ getLog().info( "skip sysProps " + key + " with empty value" );
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Allows the startup of additional webapps in the tomcat container by declaration with scope
+ * "tomcat".
+ *
+ * @param container tomcat
+ * @return dependency tomcat contexts of warfiles in scope "tomcat"
+ */
+ private Collection createDependencyContexts( Tomcat container )
+ throws MojoExecutionException, MalformedURLException, ServletException, IOException
+ {
+ getLog().info( "Deploying dependency wars" );
+ // Let's add other modules
+ List contexts = new ArrayList();
+
+ ScopeArtifactFilter filter = new ScopeArtifactFilter( "tomcat" );
+ @SuppressWarnings( "unchecked" ) Set artifacts = project.getArtifacts();
+ for ( Artifact artifact : artifacts )
+ {
+
+ // Artifact is not yet registered and it has neither test, nor a
+ // provided scope, not is it optional
+ if ( "war".equals( artifact.getType() ) && !artifact.isOptional() && filter.include( artifact ) )
+ {
+ addContextFromArtifact( container, contexts, artifact, "/" + artifact.getArtifactId(), null, false );
+ }
+ }
+
+ for ( AbstractWebapp additionalWebapp : getAdditionalWebapps() )
+ {
+ String contextPath = additionalWebapp.getContextPath();
+ if ( !contextPath.startsWith( "/" ) )
+ {
+ contextPath = "/" + contextPath;
+ }
+ addContextFromArtifact( container, contexts, getArtifact( additionalWebapp ), contextPath,
+ additionalWebapp.getContextFile(), additionalWebapp.isAsWebapp() );
+ }
+ return contexts;
+ }
+
+
+ private void addContextFromArtifact( Tomcat container, List contexts, Artifact artifact,
+ String contextPath, File contextXml, boolean asWebApp )
+ throws MojoExecutionException, ServletException, IOException
+ {
+ getLog().info( "Deploy warfile: " + String.valueOf( artifact.getFile() ) + " to contextPath: " + contextPath );
+ File webapps = new File( configurationDir, "webapps" );
+ File artifactWarDir = new File( webapps, artifact.getArtifactId() );
+ if ( !artifactWarDir.exists() )
+ {
+ //dont extract if exists
+ artifactWarDir.mkdir();
+ try
+ {
+ UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
+ unArchiver.setSourceFile( artifact.getFile() );
+ unArchiver.setDestDirectory( artifactWarDir );
+
+ // Extract the module
+ unArchiver.extract();
+ }
+ catch ( NoSuchArchiverException e )
+ {
+ getLog().error( e );
+ return;
+ }
+ catch ( ArchiverException e )
+ {
+ getLog().error( e );
+ return;
+ }
+ }
+ // TODO make that configurable ?
+ //WebappLoader webappLoader = new WebappLoader( Thread.currentThread().getContextClassLoader() );
+ WebappLoader webappLoader = createWebappLoader();
+ Context context = null;
+ if ( asWebApp )
+ {
+ context = container.addWebapp( contextPath, artifactWarDir.getAbsolutePath() );
+ }
+ else
+ {
+ context = container.addContext( contextPath, artifactWarDir.getAbsolutePath() );
+ }
+ context.setLoader( webappLoader );
+
+ File contextFile = contextXml != null ? contextXml : getContextFile();
+ if ( contextFile != null )
+ {
+ context.setConfigFile( contextFile.toURI().toURL() );
+ }
+
+ contexts.add( context );
+// container.getHost().addChild(context);
+ }
+
+ private void createStaticContext( final Tomcat container, Context context, Host host )
+ {
+ if ( staticContextDocbase != null )
+ {
+ Context staticContext = container.addContext( staticContextPath, staticContextDocbase );
+ staticContext.setPrivileged( true );
+ Wrapper servlet = context.createWrapper();
+ servlet.setServletClass( DefaultServlet.class.getName() );
+ servlet.setName( "staticContent" );
+ servlet.addMapping("/");
+ staticContext.addChild( servlet );
+ //staticContext.addServletMapping( "/", "staticContent" );
+ // see https://issues.apache.org/jira/browse/MTOMCAT-238
+ //host.addChild( staticContext );
+ }
+ }
+
+
+ /**
+ * Resolves the Artifact from the remote repository if necessary. If no version is specified, it will be retrieved
+ * from the dependency list or from the DependencyManagement section of the pom.
+ *
+ * @param additionalWebapp containing information about artifact from plugin configuration.
+ * @return Artifact object representing the specified file.
+ * @throws MojoExecutionException with a message if the version can't be found in DependencyManagement.
+ */
+ protected Artifact getArtifact( AbstractWebapp additionalWebapp )
+ throws MojoExecutionException
+ {
+
+ Artifact artifact;
+ VersionRange vr;
+ try
+ {
+ vr = VersionRange.createFromVersionSpec( additionalWebapp.getVersion() );
+ }
+ catch ( InvalidVersionSpecificationException e )
+ {
+ getLog().warn( "fail to create versionRange from version: " + additionalWebapp.getVersion(), e );
+ vr = VersionRange.createFromVersion( additionalWebapp.getVersion() );
+ }
+
+ if ( StringUtils.isEmpty( additionalWebapp.getClassifier() ) )
+ {
+ artifact =
+ factory.createDependencyArtifact( additionalWebapp.getGroupId(), additionalWebapp.getArtifactId(), vr,
+ additionalWebapp.getType(), null, Artifact.SCOPE_COMPILE );
+ }
+ else
+ {
+ artifact =
+ factory.createDependencyArtifact( additionalWebapp.getGroupId(), additionalWebapp.getArtifactId(), vr,
+ additionalWebapp.getType(), additionalWebapp.getClassifier(),
+ Artifact.SCOPE_COMPILE );
+ }
+
+ try
+ {
+ resolver.resolve( artifact, project.getRemoteArtifactRepositories(), this.local );
+ }
+ catch ( ArtifactResolutionException e )
+ {
+ throw new MojoExecutionException( "Unable to resolve artifact.", e );
+ }
+ catch ( ArtifactNotFoundException e )
+ {
+ throw new MojoExecutionException( "Unable to find artifact.", e );
+ }
+
+ return artifact;
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunWarMojo.java
new file mode 100644
index 00000000..836f180c
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractRunWarMojo.java
@@ -0,0 +1,66 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Parameter;
+
+import java.io.File;
+
+/**
+ * Runs the current project as a packaged web application using an embedded Tomcat server.
+ *
+ * @author Mark Hobson
+ * @todo depend on war:exploded when MNG-1649 resolved
+ */
+public abstract class AbstractRunWarMojo
+ extends AbstractRunMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+ /**
+ * The path of the exploded WAR directory to run.
+ */
+ @Parameter( property = "maven.tomcat.warDirectory", defaultValue = "${project.build.directory}/${project.build.finalName}", required = true )
+ private File warDirectory;
+
+ // ----------------------------------------------------------------------
+ // AbstractRunMojo Implementation
+ // ----------------------------------------------------------------------
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected File getDocBase()
+ {
+ return warDirectory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected File getContextFile()
+ {
+ return contextFile;
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractStandaloneWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractStandaloneWarMojo.java
new file mode 100644
index 00000000..89f899d3
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/AbstractStandaloneWarMojo.java
@@ -0,0 +1,299 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.commons.compress.archivers.ArchiveException;
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
+import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.tomcat.maven.runner.Tomcat9Runner;
+import org.apache.tomcat.maven.runner.Tomcat9RunnerCli;
+import org.codehaus.plexus.archiver.jar.Manifest;
+import org.codehaus.plexus.archiver.jar.ManifestException;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Properties;
+import java.util.jar.JarFile;
+
+/**
+ * Abstract Mojo for building deployable and executable war files
+ *
+ * @since 2.1
+ */
+public abstract class AbstractStandaloneWarMojo
+ extends AbstractExecWarMojo
+{
+
+ /**
+ * Name of the generated WAR.
+ */
+ @Parameter( property = "tomcat.jar.finalName",
+ defaultValue = "${project.artifactId}-${project.version}-standalone.war", required = true )
+ protected String finalName;
+
+ /**
+ * the classifier to use for the attached/generated artifact
+ */
+ @Parameter( property = "maven.tomcat.exec.war.attachArtifactClassifier", defaultValue = "standalone",
+ required = true )
+ protected String attachArtifactClassifier;
+
+ /**
+ * the type to use for the attached/generated artifact
+ *
+ * @since 2.2
+ */
+ @Parameter( property = "maven.tomcat.exec.war.attachArtifactType", defaultValue = "war", required = true )
+ protected String attachArtifactClassifierType;
+
+ public void execute()
+ throws MojoExecutionException, MojoFailureException
+ {
+ if ( !"war".equals( project.getPackaging() ) )
+ {
+ throw new MojoFailureException( "Pacakaging must be of type war for standalone-war goal." );
+ }
+
+ File warExecFile = new File( buildDirectory, finalName );
+ if ( warExecFile.exists() )
+ {
+ warExecFile.delete();
+ }
+
+ File execWarJar = new File( buildDirectory, finalName );
+
+ FileOutputStream execWarJarOutputStream = null;
+ ArchiveOutputStream os = null;
+ File tmpPropertiesFile = null;
+ File tmpManifestFile = null;
+ FileOutputStream tmpPropertiesFileOutputStream = null;
+ PrintWriter tmpManifestWriter = null;
+
+ try
+ {
+ tmpPropertiesFile = new File( buildDirectory, "war-exec.properties" );
+ if ( tmpPropertiesFile.exists() )
+ {
+ tmpPropertiesFile.delete();
+ }
+ tmpPropertiesFile.getParentFile().mkdirs();
+
+ tmpManifestFile = new File( buildDirectory, "war-exec.manifest" );
+ if ( tmpManifestFile.exists() )
+ {
+ tmpManifestFile.delete();
+ }
+ tmpPropertiesFileOutputStream = new FileOutputStream( tmpPropertiesFile );
+ execWarJar.getParentFile().mkdirs();
+ execWarJar.createNewFile();
+ execWarJarOutputStream = new FileOutputStream( execWarJar );
+
+ tmpManifestWriter = new PrintWriter( tmpManifestFile );
+
+ // store :
+ //* wars in the root: foo.war
+ //* tomcat jars
+ //* file tomcat.standalone.properties with possible values :
+ // * useServerXml=true/false to use directly the one provided
+ // * enableNaming=true/false
+ // * wars=foo.war|contextpath;bar.war ( |contextpath is optionnal if empty use the war name )
+ // * accessLogValveFormat=
+ // * connectorhttpProtocol: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
+ // * codeSourceContextPath=path parameter, default is project.artifactId
+ //* optionnal: conf/ with usual tomcat configuration files
+ //* MANIFEST with Main-Class
+
+ Properties properties = new Properties();
+
+ properties.put(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY,
+ Long.toString( System.currentTimeMillis() ) );
+ properties.put(Tomcat9Runner.ENABLE_NAMING_KEY, Boolean.toString( enableNaming ) );
+ properties.put(Tomcat9Runner.ACCESS_LOG_VALVE_FORMAT_KEY, accessLogValveFormat );
+ properties.put(Tomcat9Runner.HTTP_PROTOCOL_KEY, connectorHttpProtocol );
+ properties.put(Tomcat9Runner.CODE_SOURCE_CONTEXT_PATH, path );
+
+ os = new ArchiveStreamFactory().createArchiveOutputStream( ArchiveStreamFactory.JAR,
+ execWarJarOutputStream );
+
+ extractJarToArchive( new JarFile( projectArtifact.getFile() ), os, null );
+
+ if ( serverXml != null && serverXml.exists() )
+ {
+ os.putArchiveEntry( new JarArchiveEntry( "conf/server.xml" ) );
+ IOUtils.copy( new FileInputStream( serverXml ), os );
+ os.closeArchiveEntry();
+ properties.put(Tomcat9Runner.USE_SERVER_XML_KEY, Boolean.TRUE.toString() );
+ }
+ else
+ {
+ properties.put(Tomcat9Runner.USE_SERVER_XML_KEY, Boolean.FALSE.toString() );
+ }
+
+ os.putArchiveEntry( new JarArchiveEntry( "conf/web.xml" ) );
+ IOUtils.copy( getClass().getResourceAsStream( "/conf/web.xml" ), os );
+ os.closeArchiveEntry();
+
+ properties.store( tmpPropertiesFileOutputStream, "created by Apache Tomcat Maven plugin" );
+
+ tmpPropertiesFileOutputStream.flush();
+ tmpPropertiesFileOutputStream.close();
+
+ os.putArchiveEntry(new JarArchiveEntry( Tomcat9RunnerCli.STAND_ALONE_PROPERTIES_FILENAME ) );
+ IOUtils.copy( new FileInputStream( tmpPropertiesFile ), os );
+ os.closeArchiveEntry();
+
+ // add tomcat classes
+ for ( Artifact pluginArtifact : pluginArtifacts )
+ {
+ if ( StringUtils.equals( "org.apache.tomcat", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "org.apache.tomcat.embed", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "org.eclipse.jdt.core.compiler", pluginArtifact.getGroupId() ) //
+ || StringUtils.equals( "commons-cli", pluginArtifact.getArtifactId() ) //
+ || StringUtils.equals( "tomcat9-war-runner", pluginArtifact.getArtifactId() ) )
+ {
+ JarFile jarFile = new JarFile( pluginArtifact.getFile() );
+ extractJarToArchive( jarFile, os, null );
+ }
+ }
+
+ // add extra dependencies
+ if ( extraDependencies != null && !extraDependencies.isEmpty() )
+ {
+ for ( Dependency dependency : extraDependencies )
+ {
+ String version = dependency.getVersion();
+ if ( StringUtils.isEmpty( version ) )
+ {
+ version = findArtifactVersion( dependency );
+ }
+
+ if ( StringUtils.isEmpty( version ) )
+ {
+ throw new MojoExecutionException(
+ "Dependency '" + dependency.getGroupId() + "':'" + dependency.getArtifactId()
+ + "' does not have version specified" );
+ }
+ // String groupId, String artifactId, String version, String scope, String type
+ Artifact artifact = artifactFactory.createArtifact( dependency.getGroupId(), //
+ dependency.getArtifactId(), //
+ version, //
+ dependency.getScope(), //
+ dependency.getType() );
+
+ artifactResolver.resolve( artifact, this.remoteRepos, this.local );
+ JarFile jarFile = new JarFile( artifact.getFile() );
+ extractJarToArchive( jarFile, os, excludes );
+ }
+ }
+
+ Manifest manifest = new Manifest();
+
+ Manifest.Attribute mainClassAtt = new Manifest.Attribute();
+ mainClassAtt.setName( "Main-Class" );
+ mainClassAtt.setValue( mainClass );
+ manifest.addConfiguredAttribute( mainClassAtt );
+
+ manifest.write( tmpManifestWriter );
+ tmpManifestWriter.flush();
+ tmpManifestWriter.close();
+
+ os.putArchiveEntry( new JarArchiveEntry( "META-INF/MANIFEST.MF" ) );
+ IOUtils.copy( new FileInputStream( tmpManifestFile ), os );
+ os.closeArchiveEntry();
+
+ if ( attachArtifact )
+ {
+ //MavenProject project, String artifactType, String artifactClassifier, File artifactFile
+ projectHelper.attachArtifact( project, attachArtifactClassifierType, attachArtifactClassifier,
+ execWarJar );
+ }
+
+ if ( extraResources != null )
+ {
+ for ( ExtraResource extraResource : extraResources )
+ {
+
+ DirectoryScanner directoryScanner = new DirectoryScanner();
+ directoryScanner.setBasedir( extraResource.getDirectory() );
+ directoryScanner.addDefaultExcludes();
+ directoryScanner.setExcludes( toStringArray( extraResource.getExcludes() ) );
+ directoryScanner.setIncludes( toStringArray( extraResource.getIncludes() ) );
+ directoryScanner.scan();
+ for ( String includeFile : directoryScanner.getIncludedFiles() )
+ {
+ getLog().debug( "include file:" + includeFile );
+ os.putArchiveEntry( new JarArchiveEntry( includeFile ) );
+ IOUtils.copy( new FileInputStream( new File( extraResource.getDirectory(), includeFile ) ),
+ os );
+ os.closeArchiveEntry();
+ }
+ }
+ }
+
+ if ( tomcatConfigurationFilesDirectory != null && tomcatConfigurationFilesDirectory.exists() )
+ {
+ // Because its the tomcat default dir for configs
+ String aConfigOutputDir = "conf/";
+ copyDirectoryContentIntoArchive( tomcatConfigurationFilesDirectory, aConfigOutputDir, os );
+ }
+ }
+ catch ( ManifestException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( IOException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArchiveException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArtifactNotFoundException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ catch ( ArtifactResolutionException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+ finally
+ {
+ IOUtils.closeQuietly( os );
+ IOUtils.closeQuietly( tmpManifestWriter );
+ IOUtils.closeQuietly( execWarJarOutputStream );
+ IOUtils.closeQuietly( tmpPropertiesFileOutputStream );
+ }
+
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarMojo.java
new file mode 100644
index 00000000..921f853f
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarMojo.java
@@ -0,0 +1,40 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Create a self executable jar file containing all necessary Apache Tomcat classes.
+ * This allows for using just java -jar mywebapp.jar to run your webapp without
+ * needing to install a Tomcat instance.
+ * More details here.
+ *
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+@Mojo( name = "exec-war", threadSafe = true )
+@Execute( phase = LifecyclePhase.PACKAGE )
+public class ExecWarMojo
+ extends AbstractExecWarMojo
+{
+ // no op only mojo metadatas
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarOnlyMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarOnlyMojo.java
new file mode 100644
index 00000000..686fe9d8
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExecWarOnlyMojo.java
@@ -0,0 +1,34 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * Same as exec-war goal without forking the package lifecycle.
+ *
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+@Mojo( name = "exec-war-only", threadSafe = true )
+public class ExecWarOnlyMojo
+ extends AbstractExecWarMojo
+{
+ // no op only mojo metadatas to not fork a lifecycle
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtendedTomcat.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtendedTomcat.java
new file mode 100644
index 00000000..ebd6a7ee
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtendedTomcat.java
@@ -0,0 +1,69 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.Tomcat;
+
+import java.io.File;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public class ExtendedTomcat
+ extends Tomcat
+{
+
+ private File configurationDir;
+
+ public ExtendedTomcat( File configurationDir )
+ {
+ super();
+ this.configurationDir = configurationDir;
+ }
+
+ public Context addWebapp( Host host, String url, String name, String path )
+ {
+
+ Context ctx = new StandardContext();
+ ctx.setName( name );
+ ctx.setPath( url );
+ ctx.setDocBase( path );
+
+ ContextConfig ctxCfg = new ContextConfig();
+ ctx.addLifecycleListener( ctxCfg );
+
+ ctxCfg.setDefaultWebXml( new File( configurationDir, "conf/web.xml" ).getAbsolutePath() );
+
+ if ( host == null )
+ {
+ getHost().addChild( ctx );
+ }
+ else
+ {
+ host.addChild( ctx );
+ }
+
+ return ctx;
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraDependency.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraDependency.java
new file mode 100644
index 00000000..7accec71
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraDependency.java
@@ -0,0 +1,31 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.model.Dependency;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public class ExtraDependency
+ extends Dependency
+{
+ // no op just here to support for maven 2.x
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraResource.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraResource.java
new file mode 100644
index 00000000..9a00bd04
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ExtraResource.java
@@ -0,0 +1,31 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.model.Resource;
+
+/**
+ * @author Olivier Lamy
+ */
+public class ExtraResource
+ extends Resource
+{
+ // no op just here to support for maven 2.x
+}
+
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunMojo.java
new file mode 100644
index 00000000..8b097d2f
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunMojo.java
@@ -0,0 +1,462 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.WebResource;
+import org.apache.catalina.loader.WebappLoader;
+import org.apache.catalina.webresources.DirResourceSet;
+import org.apache.catalina.webresources.EmptyResource;
+import org.apache.catalina.webresources.FileResourceSet;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.shared.filtering.MavenFileFilterRequest;
+import org.apache.maven.shared.filtering.MavenFilteringException;
+import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculator;
+import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorRequest;
+import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorResult;
+import org.apache.tomcat.maven.common.run.TomcatRunException;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.codehaus.plexus.util.xml.Xpp3DomWriter;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Runs the current project as a dynamic web application using an embedded Tomcat server.
+ *
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+@Mojo( name = "run", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true )
+@Execute( phase = LifecyclePhase.PROCESS_CLASSES )
+public class RunMojo
+ extends AbstractRunMojo
+{
+ // ----------------------------------------------------------------------
+ // Mojo Parameters
+ // ----------------------------------------------------------------------
+
+
+ /**
+ * The set of dependencies for the web application being run.
+ */
+ @Parameter( defaultValue = "${project.artifacts}", required = true, readonly = true )
+ private Set dependencies;
+
+ /**
+ * The web resources directory for the web application being run.
+ */
+ @Parameter( defaultValue = "${basedir}/src/main/webapp", property = "tomcat.warSourceDirectory" )
+ private File warSourceDirectory;
+
+
+ /**
+ * Set the "follow standard delegation model" flag used to configure our ClassLoader.
+ *
+ * @see http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/loader/WebappLoader.html#setDelegate(boolean)
+ * @since 1.0
+ */
+ @Parameter( property = "tomcat.delegate", defaultValue = "true" )
+ private boolean delegate = true;
+
+ /**
+ * @since 2.0
+ */
+ @Component
+ private ClassLoaderEntriesCalculator classLoaderEntriesCalculator;
+
+ /**
+ * will add /WEB-INF/lib/*.jar and /WEB-INF/classes from war dependencies in the webappclassloader
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.addWarDependenciesInClassloader", defaultValue = "true" )
+ private boolean addWarDependenciesInClassloader;
+
+ /**
+ * will use the test classpath rather than the compile one and will add test dependencies too
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.useTestClasspath", defaultValue = "false" )
+ private boolean useTestClasspath;
+
+ /**
+ * Additional optional directories to add to the embedded tomcat classpath.
+ *
+ * @since 2.0
+ */
+ @Parameter( alias = "additionalClassesDirs" )
+ private List additionalClasspathDirs;
+
+
+ public final File getWarSourceDirectory()
+ {
+ return warSourceDirectory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected File getDocBase()
+ throws IOException
+ {
+ // https://issues.apache.org/jira/browse/MTOMCAT-239
+ // when running a jar docBase doesn't exists so create a fake one
+ if ( !warSourceDirectory.exists() )
+ {
+ // we create a temporary file in build.directory
+ final File tempDocBase = createTempDirectory( new File( project.getBuild().getDirectory() ) );
+ Runtime.getRuntime().addShutdownHook( new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ FileUtils.deleteDirectory( tempDocBase );
+ }
+ catch ( Exception e )
+ {
+ // we can consider as safe to ignore as it's located in build directory
+ }
+ }
+ } );
+ return tempDocBase;
+ }
+ return warSourceDirectory;
+ }
+
+ private static File createTempDirectory( File baseTmpDirectory )
+ throws IOException
+ {
+ final File temp = File.createTempFile( "temp", Long.toString( System.nanoTime() ), baseTmpDirectory );
+
+ if ( !( temp.delete() ) )
+ {
+ throw new IOException( "Could not delete temp file: " + temp.getAbsolutePath() );
+ }
+
+ if ( !( temp.mkdir() ) )
+ {
+ throw new IOException( "Could not create temp directory: " + temp.getAbsolutePath() );
+ }
+
+ return temp;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected File getContextFile()
+ throws MojoExecutionException
+ {
+ File temporaryContextFile = null;
+
+ //----------------------------------------------------------------------------
+ // context attributes backgroundProcessorDelay reloadable cannot be modified at runtime.
+ // It looks only values from the file are used
+ // so here we create a temporary file with values modified
+ //----------------------------------------------------------------------------
+ FileReader fr = null;
+ FileWriter fw = null;
+ StringWriter sw = new StringWriter();
+ try
+ {
+ temporaryContextFile = File.createTempFile( "tomcat-maven-plugin", "temp-ctx-file" );
+ temporaryContextFile.deleteOnExit();
+
+ // format to modify/create
+ if ( contextFile != null && contextFile.exists() )
+ {
+ MavenFileFilterRequest mavenFileFilterRequest = new MavenFileFilterRequest();
+ mavenFileFilterRequest.setFrom( contextFile );
+ mavenFileFilterRequest.setTo( temporaryContextFile );
+ mavenFileFilterRequest.setMavenProject( project );
+ mavenFileFilterRequest.setMavenSession( session );
+ mavenFileFilterRequest.setFiltering( true );
+
+ mavenFileFilter.copyFile( mavenFileFilterRequest );
+
+ fr = new FileReader( temporaryContextFile );
+ Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( fr );
+ xpp3Dom.setAttribute( "backgroundProcessorDelay", Integer.toString( backgroundProcessorDelay ) );
+ xpp3Dom.setAttribute( "reloadable", Boolean.toString( isContextReloadable() ) );
+ fw = new FileWriter( temporaryContextFile );
+ Xpp3DomWriter.write( fw, xpp3Dom );
+ Xpp3DomWriter.write( sw, xpp3Dom );
+ getLog().debug( " generated context file " + sw.toString() );
+ }
+ else
+ {
+ if ( contextReloadable )
+ {
+ // don't care about using a complicated xml api to create one xml line :-)
+ StringBuilder sb = new StringBuilder( "" );
+
+ getLog().debug( " generated context file " + sb.toString() );
+ fw = new FileWriter( temporaryContextFile );
+ fw.write( sb.toString() );
+ }
+ else
+ {
+ // no user context file and contextReloadable false so no need about creating a hack one
+ return null;
+ }
+ }
+ }
+ catch ( IOException e )
+ {
+ getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
+ throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
+ }
+ catch ( XmlPullParserException e )
+ {
+ getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
+ throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
+ }
+ catch ( MavenFilteringException e )
+ {
+ getLog().error( "error filtering context.xml : " + e.getMessage(), e );
+ throw new MojoExecutionException( "error filtering context.xml : " + e.getMessage(), e );
+ }
+ finally
+ {
+ IOUtil.close( fw );
+ IOUtil.close( fr );
+ IOUtil.close( sw );
+ }
+
+ return temporaryContextFile;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws MojoExecutionException
+ */
+ @Override
+ protected WebappLoader createWebappLoader()
+ throws IOException, MojoExecutionException
+ {
+ WebappLoader loader = super.createWebappLoader();
+
+ if ( useSeparateTomcatClassLoader )
+ {
+ loader.setDelegate( delegate );
+ }
+
+ return loader;
+ }
+
+ @Override
+ protected void enhanceContext( final Context context )
+ throws MojoExecutionException
+ {
+ super.enhanceContext( context );
+
+ try
+ {
+ ClassLoaderEntriesCalculatorRequest request = new ClassLoaderEntriesCalculatorRequest() //
+ .setDependencies( dependencies ) //
+ .setLog( getLog() ) //
+ .setMavenProject( project ) //
+ .setAddWarDependenciesInClassloader( addWarDependenciesInClassloader ) //
+ .setUseTestClassPath( useTestClasspath );
+ final ClassLoaderEntriesCalculatorResult classLoaderEntriesCalculatorResult =
+ classLoaderEntriesCalculator.calculateClassPathEntries( request );
+ final List classLoaderEntries = classLoaderEntriesCalculatorResult.getClassPathEntries();
+ final List tmpDirectories = classLoaderEntriesCalculatorResult.getTmpDirectories();
+
+ /* Add jars */
+ final List jarPaths = extractJars( classLoaderEntries );
+ for (String jarPath : jarPaths) {
+ File f = new File(jarPath);
+ if (f.exists()) {
+ /* We add the jar as a file under /WEB-INF/lib and Tomcat takes care of creating a JarResourceSet for its classes and
+ for its resources in /META-INF/resources as appropriate. Basically it hooks into Tomcat's usual handling.
+ */
+ getLog().debug("Adding jar resource: " + f.getAbsolutePath());
+ FileResourceSet fileResourceSet = new FileResourceSet(context.getResources(), "/WEB-INF/lib/" + f.getName(), f.getAbsolutePath(), "/");
+ context.getResources().addPostResources(fileResourceSet);
+ }
+ }
+
+ /* Add build directories */
+ getLog().debug("Adding classes resource: " + new File(project.getBuild().getOutputDirectory()).getAbsolutePath());
+ DirResourceSet webinfClassesResources = new DirResourceSet(context.getResources(), "/WEB-INF/classes", new File(project.getBuild().getOutputDirectory()).getAbsolutePath(), "/") {
+
+ @Override
+ public WebResource getResource(String path) {
+ /* We need to juggle with /META-INF/beans.xml as Weld's WebAppBeanArchiveScanner has special handling
+ for /WEB-INF/classes that doesn't work with this.
+ That is because it first finds _all_ resources /META-INF/beans.xml and it ends up with the URLs to those
+ resources, which are all file-system URLs, and then looks for /WEB-INF/classes in the URL, which we don't
+ have as our files are in the Maven target directory.
+ */
+ if ("/WEB-INF/classes/META-INF/beans.xml".equals(path)) {
+ getLog().info("Rejecting request for /WEB-INF/classes/META-INF/beans.xml for Weld compatibility. beans.xml can be found at /WEB-INF/beans.xml");
+ return new EmptyResource(getRoot(), path);
+ } else if ("/WEB-INF/beans.xml".equals(path)) {
+ WebResource beans = super.getResource(path);
+ if (!beans.exists()) {
+ beans = super.getResource("/WEB-INF/classes/META-INF/beans.xml");
+ if (beans.exists()) {
+ getLog().info("Returning /WEB-INF/classes/META-INF/beans.xml for request of /WEB-INF/beans.xml for Weld compatibility");
+ }
+ }
+ return beans;
+ } else {
+ return super.getResource(path);
+ }
+ }
+
+ };
+ context.getResources().addPreResources(webinfClassesResources);
+
+ for (final String buildDirectory : classLoaderEntriesCalculatorResult.getBuildDirectories()) {
+ if (buildDirectory.equals(project.getBuild().getOutputDirectory())) {
+ continue;
+ }
+
+ final File buildDirectoryFile = new File(buildDirectory);
+ getLog().debug("Adding additional classes resource: " + buildDirectoryFile.getAbsolutePath());
+ DirResourceSet otherClassesResources = new DirResourceSet(context.getResources(), "/WEB-INF/classes", buildDirectoryFile.getAbsolutePath(), "/");
+ context.getResources().addPreResources(otherClassesResources);
+ }
+
+ /* Support the maven-war-plugin's webResources configuration to add resources */
+ final Plugin warPlugin = (Plugin) project.getBuild().getPluginsAsMap().get("org.apache.maven.plugins:maven-war-plugin");
+ if (warPlugin != null && warPlugin.getConfiguration() instanceof Xpp3Dom) {
+ final Xpp3Dom cfg = (Xpp3Dom) warPlugin.getConfiguration();
+ Xpp3Dom webResources = cfg.getChild("webResources");
+ if (webResources != null) {
+ Xpp3Dom[] resources = webResources.getChildren("resource");
+ for (Xpp3Dom resource : resources) {
+ Xpp3Dom directoryNode = resource.getChild("directory");
+ if (directoryNode == null) {
+ continue;
+ }
+ final String directory = directoryNode.getValue();
+
+ Xpp3Dom targetPathNode = resource.getChild("targetPath");
+ final String targetPath = targetPathNode != null ? "/" + targetPathNode.getValue() : "/";
+
+ Xpp3Dom filtering = resource.getChild("filtering");
+ if (filtering != null && "true".equals(filtering.getValue())) {
+ /* We don't support filtered resources */
+ getLog().warn("Cannot support filtered web-resource, not adding: " + directory);
+ continue;
+ }
+
+ File directoryFile = directory.startsWith(File.separator) ? new File(directory) : new File(project.getBasedir(), directory);
+ DirResourceSet dirResourceSet = new DirResourceSet(context.getResources(), targetPath, directoryFile.getAbsolutePath(), "/");
+ dirResourceSet.setStaticOnly(true);
+ context.getResources().addPostResources(dirResourceSet);
+
+ getLog().debug("Adding additional static resources at \"" + targetPath + "\": " + directoryFile.getAbsolutePath());
+ }
+ }
+ }
+
+ Runtime.getRuntime().addShutdownHook( new Thread()
+ {
+ @Override
+ public void run()
+ {
+ for ( File tmpDir : tmpDirectories )
+ {
+ try
+ {
+ FileUtils.deleteDirectory( tmpDir );
+ }
+ catch ( IOException e )
+ {
+ // ignore
+ }
+ }
+ }
+ } );
+ }
+ catch ( TomcatRunException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+
+ }
+
+
+ /**
+ * extract List of path which are jar files
+ *
+ * @param classLoaderEntries
+ * @return
+ */
+ private List extractJars( List classLoaderEntries )
+ throws MojoExecutionException
+ {
+
+ List jarPaths = new ArrayList();
+
+ try
+ {
+ for ( String classLoaderEntry : classLoaderEntries )
+ {
+ URI uri = new URI( classLoaderEntry );
+ File file = new File( uri );
+ if ( !file.isDirectory() && StringUtils.endsWithIgnoreCase(file.getName(), ".jar"))
+ {
+ jarPaths.add( file.getAbsolutePath() );
+ }
+ }
+ }
+ catch ( URISyntaxException e )
+ {
+ throw new MojoExecutionException( e.getMessage(), e );
+ }
+
+ return jarPaths;
+
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarMojo.java
new file mode 100644
index 00000000..7f617ecd
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarMojo.java
@@ -0,0 +1,40 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+
+/*
+ * 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.
+ */
+
+
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+/**
+ * Runs the current project as a packaged web application using an embedded Tomcat server.
+ *
+ * @author Mark Hobson
+ * @todo depend on war:exploded when MNG-1649 resolved
+ */
+@Mojo( name = "run-war", requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true )
+@Execute( phase = LifecyclePhase.PACKAGE )
+public class RunWarMojo
+ extends AbstractRunWarMojo
+{
+ // no-op : only mojo metadata overriding
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarOnlyMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarOnlyMojo.java
new file mode 100644
index 00000000..978821f0
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/RunWarOnlyMojo.java
@@ -0,0 +1,36 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+
+/*
+ * 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.
+ */
+
+
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+/**
+ * Same as run-war goal without forking the package cycle.
+ *
+ * @author vlatombe
+ */
+@Mojo( name = "run-war-only", requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true )
+public class RunWarOnlyMojo
+ extends AbstractRunWarMojo
+{
+ // no-op : only mojo metadata overriding
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ShutdownMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ShutdownMojo.java
new file mode 100644
index 00000000..6b0df953
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/ShutdownMojo.java
@@ -0,0 +1,90 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+
+/*
+* 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.
+*/
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.tomcat.maven.common.run.EmbeddedRegistry;
+import org.apache.tomcat.maven.plugin.tomcat9.AbstractTomcat9Mojo;
+
+
+/**
+ *
+ * Shuts down all possibly started embedded Tomcat servers. This will be automatically done
+ * through a shutdown hook or you may call this Mojo to shut them down explictly.
+ *
+ *
+ * By default the shutdown goal is not bound to any phase. For integration tests
+ * you might want to bind it to post-integration-test.
+ *
+ *
+ * @author Mark Michaelis
+ * @since 2.0
+ */
+@Mojo( name = "shutdown", requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true )
+public class ShutdownMojo
+ extends AbstractTomcat9Mojo
+{
+
+ /**
+ * Ignore error when shutdown
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.skipErrorOnShutdown", defaultValue = "false" )
+ protected boolean skipErrorOnShutdown;
+
+ /**
+ * Skip execution
+ *
+ * @since 2.0
+ */
+ @Parameter( property = "maven.tomcat.skipShutdown", defaultValue = "false" )
+ protected boolean skip;
+
+ /**
+ * Shuts down all embedded tomcats which got started up to now.
+ *
+ * @throws org.apache.maven.plugin.MojoExecutionException
+ * if shutting down one or all servers failed
+ */
+ public void execute()
+ throws MojoExecutionException
+ {
+ if ( skip )
+ {
+ getLog().info( "skip execution" );
+ return;
+ }
+ try
+ {
+ EmbeddedRegistry.getInstance().shutdownAll( getLog() );
+ }
+ catch ( Exception e )
+ {
+ if ( !skipErrorOnShutdown )
+ {
+ throw new MojoExecutionException( messagesProvider.getMessage( "ShutdownMojo.shutdownError" ), e );
+ }
+ }
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarMojo.java
new file mode 100644
index 00000000..00d0c54f
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarMojo.java
@@ -0,0 +1,36 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Execute;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * This Mojo will create an executable war file with embedded Tomcat that is also capable of being deployed elsewhere.
+ *
+ * @since 2.1
+ */
+@Mojo(name = "standalone-war", threadSafe = true)
+@Execute(phase = LifecyclePhase.PACKAGE)
+public class StandaloneWarMojo
+ extends AbstractStandaloneWarMojo
+{
+ // no op
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarOnlyMojo.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarOnlyMojo.java
new file mode 100644
index 00000000..fd93e586
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/StandaloneWarOnlyMojo.java
@@ -0,0 +1,33 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.plugins.annotations.Mojo;
+
+/**
+ * This Mojo will create an executable war file with embedded Tomcat that is also capable of being deployed elsewhere.
+ *
+ * @since 2.1
+ */
+@Mojo(name = "standalone-war-only", threadSafe = true)
+public class StandaloneWarOnlyMojo
+ extends AbstractStandaloneWarMojo
+{
+ // no op
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/WarRunDependency.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/WarRunDependency.java
new file mode 100644
index 00000000..882dbb87
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/WarRunDependency.java
@@ -0,0 +1,43 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+/*
+ * 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.
+ */
+
+import org.apache.maven.model.Dependency;
+
+import java.io.File;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public class WarRunDependency
+{
+
+ public Dependency dependency;
+
+ public String contextPath;
+
+ public File contextXml;
+
+ public WarRunDependency()
+ {
+ // no op
+ }
+
+}
diff --git a/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/Webapp.java b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/Webapp.java
new file mode 100644
index 00000000..7e75658f
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/java/org/apache/tomcat/maven/plugin/tomcat9/run/Webapp.java
@@ -0,0 +1,44 @@
+package org.apache.tomcat.maven.plugin.tomcat9.run;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.tomcat.maven.common.config.AbstractWebapp;
+
+/**
+ * Webapp represents information specified in the plugin configuration section
+ * for each webapp.
+ *
+ * @since 2.0
+ */
+public class Webapp
+ extends AbstractWebapp
+{
+
+ public Webapp()
+ {
+ // default constructor
+ }
+
+ public Webapp( Artifact artifact )
+ {
+ super( artifact );
+ }
+}
diff --git a/tomcat9-maven-plugin/src/main/resources/conf/logging.properties b/tomcat9-maven-plugin/src/main/resources/conf/logging.properties
new file mode 100644
index 00000000..6b6c8c00
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/resources/conf/logging.properties
@@ -0,0 +1,64 @@
+# 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.
+
+handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler
+
+.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler
+
+############################################################
+# Handler specific properties.
+# Describes specific configuration info for Handlers.
+############################################################
+
+1catalina.org.apache.juli.AsyncFileHandler.level = FINE
+1catalina.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
+1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.
+
+2localhost.org.apache.juli.AsyncFileHandler.level = FINE
+2localhost.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
+2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.
+
+3manager.org.apache.juli.AsyncFileHandler.level = FINE
+3manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
+3manager.org.apache.juli.AsyncFileHandler.prefix = manager.
+
+4host-manager.org.apache.juli.AsyncFileHandler.level = FINE
+4host-manager.org.apache.juli.AsyncFileHandler.directory = ${catalina.base}/logs
+4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager.
+
+java.util.logging.ConsoleHandler.level = FINE
+java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
+
+
+############################################################
+# Facility specific properties.
+# Provides extra control for each logger.
+############################################################
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler
+
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
+org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler
+
+# For example, set the org.apache.catalina.util.LifecycleBase logger to log
+# each component that extends LifecycleBase changing state:
+#org.apache.catalina.util.LifecycleBase.level = FINE
+
+# To see debug messages in TldLocationsCache, uncomment the following line:
+#org.apache.jasper.compiler.TldLocationsCache.level = FINE
diff --git a/tomcat9-maven-plugin/src/main/resources/conf/tomcat-users.xml b/tomcat9-maven-plugin/src/main/resources/conf/tomcat-users.xml
new file mode 100644
index 00000000..34e268d8
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/resources/conf/tomcat-users.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/tomcat9-maven-plugin/src/main/resources/conf/web.xml b/tomcat9-maven-plugin/src/main/resources/conf/web.xml
new file mode 100644
index 00000000..81a201f8
--- /dev/null
+++ b/tomcat9-maven-plugin/src/main/resources/conf/web.xml
@@ -0,0 +1,4614 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ default
+ org.apache.catalina.servlets.DefaultServlet
+
+ debug
+ 0
+
+
+ listings
+ false
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ jsp
+ org.apache.jasper.servlet.JspServlet
+
+ fork
+ false
+
+
+ xpoweredBy
+ false
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ default
+ /
+
+
+
+
+ jsp
+ *.jsp
+ *.jspx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 30
+
+
+
+
+
+
+
+
+
+
+
+
+ 123
+ application/vnd.lotus-1-2-3
+
+
+ 3dml
+ text/vnd.in3d.3dml
+
+
+ 3ds
+ image/x-3ds
+
+
+ 3g2
+ video/3gpp2
+
+
+ 3gp
+ video/3gpp
+
+
+ 7z
+ application/x-7z-compressed
+
+
+ aab
+ application/x-authorware-bin
+
+
+ aac
+ audio/x-aac
+
+
+ aam
+ application/x-authorware-map
+
+
+ aas
+ application/x-authorware-seg
+
+
+ abs
+ audio/x-mpeg
+
+
+ abw
+ application/x-abiword
+
+
+ ac
+ application/pkix-attr-cert
+
+
+ acc
+ application/vnd.americandynamics.acc
+
+
+ ace
+ application/x-ace-compressed
+
+
+ acu
+ application/vnd.acucobol
+
+
+ acutc
+ application/vnd.acucorp
+
+
+ adp
+ audio/adpcm
+
+
+ aep
+ application/vnd.audiograph
+
+
+ afm
+ application/x-font-type1
+
+
+ afp
+ application/vnd.ibm.modcap
+
+
+ ahead
+ application/vnd.ahead.space
+
+
+ ai
+ application/postscript
+
+
+ aif
+ audio/x-aiff
+
+
+ aifc
+ audio/x-aiff
+
+
+ aiff
+ audio/x-aiff
+
+
+ aim
+ application/x-aim
+
+
+ air
+ application/vnd.adobe.air-application-installer-package+zip
+
+
+ ait
+ application/vnd.dvb.ait
+
+
+ ami
+ application/vnd.amiga.ami
+
+
+ anx
+ application/annodex
+
+
+ apk
+ application/vnd.android.package-archive
+
+
+ appcache
+ text/cache-manifest
+
+
+ application
+ application/x-ms-application
+
+
+ apr
+ application/vnd.lotus-approach
+
+
+ arc
+ application/x-freearc
+
+
+ art
+ image/x-jg
+
+
+ asc
+ application/pgp-signature
+
+
+ asf
+ video/x-ms-asf
+
+
+ asm
+ text/x-asm
+
+
+ aso
+ application/vnd.accpac.simply.aso
+
+
+ asx
+ video/x-ms-asf
+
+
+ atc
+ application/vnd.acucorp
+
+
+ atom
+ application/atom+xml
+
+
+ atomcat
+ application/atomcat+xml
+
+
+ atomsvc
+ application/atomsvc+xml
+
+
+ atx
+ application/vnd.antix.game-component
+
+
+ au
+ audio/basic
+
+
+ avi
+ video/x-msvideo
+
+
+ avx
+ video/x-rad-screenplay
+
+
+ aw
+ application/applixware
+
+
+ axa
+ audio/annodex
+
+
+ axv
+ video/annodex
+
+
+ azf
+ application/vnd.airzip.filesecure.azf
+
+
+ azs
+ application/vnd.airzip.filesecure.azs
+
+
+ azw
+ application/vnd.amazon.ebook
+
+
+ bat
+ application/x-msdownload
+
+
+ bcpio
+ application/x-bcpio
+
+
+ bdf
+ application/x-font-bdf
+
+
+ bdm
+ application/vnd.syncml.dm+wbxml
+
+
+ bed
+ application/vnd.realvnc.bed
+
+
+ bh2
+ application/vnd.fujitsu.oasysprs
+
+
+ bin
+ application/octet-stream
+
+
+ blb
+ application/x-blorb
+
+
+ blorb
+ application/x-blorb
+
+
+ bmi
+ application/vnd.bmi
+
+
+ bmp
+ image/bmp
+
+
+ body
+ text/html
+
+
+ book
+ application/vnd.framemaker
+
+
+ box
+ application/vnd.previewsystems.box
+
+
+ boz
+ application/x-bzip2
+
+
+ bpk
+ application/octet-stream
+
+
+ btif
+ image/prs.btif
+
+
+ bz
+ application/x-bzip
+
+
+ bz2
+ application/x-bzip2
+
+
+ c
+ text/x-c
+
+
+ c11amc
+ application/vnd.cluetrust.cartomobile-config
+
+
+ c11amz
+ application/vnd.cluetrust.cartomobile-config-pkg
+
+
+ c4d
+ application/vnd.clonk.c4group
+
+
+ c4f
+ application/vnd.clonk.c4group
+
+
+ c4g
+ application/vnd.clonk.c4group
+
+
+ c4p
+ application/vnd.clonk.c4group
+
+
+ c4u
+ application/vnd.clonk.c4group
+
+
+ cab
+ application/vnd.ms-cab-compressed
+
+
+ caf
+ audio/x-caf
+
+
+ cap
+ application/vnd.tcpdump.pcap
+
+
+ car
+ application/vnd.curl.car
+
+
+ cat
+ application/vnd.ms-pki.seccat
+
+
+ cb7
+ application/x-cbr
+
+
+ cba
+ application/x-cbr
+
+
+ cbr
+ application/x-cbr
+
+
+ cbt
+ application/x-cbr
+
+
+ cbz
+ application/x-cbr
+
+
+ cc
+ text/x-c
+
+
+ cct
+ application/x-director
+
+
+ ccxml
+ application/ccxml+xml
+
+
+ cdbcmsg
+ application/vnd.contact.cmsg
+
+
+ cdf
+ application/x-cdf
+
+
+ cdkey
+ application/vnd.mediastation.cdkey
+
+
+ cdmia
+ application/cdmi-capability
+
+
+ cdmic
+ application/cdmi-container
+
+
+ cdmid
+ application/cdmi-domain
+
+
+ cdmio
+ application/cdmi-object
+
+
+ cdmiq
+ application/cdmi-queue
+
+
+ cdx
+ chemical/x-cdx
+
+
+ cdxml
+ application/vnd.chemdraw+xml
+
+
+ cdy
+ application/vnd.cinderella
+
+
+ cer
+ application/pkix-cert
+
+
+ cfs
+ application/x-cfs-compressed
+
+
+ cgm
+ image/cgm
+
+
+ chat
+ application/x-chat
+
+
+ chm
+ application/vnd.ms-htmlhelp
+
+
+ chrt
+ application/vnd.kde.kchart
+
+
+ cif
+ chemical/x-cif
+
+
+ cii
+ application/vnd.anser-web-certificate-issue-initiation
+
+
+ cil
+ application/vnd.ms-artgalry
+
+
+ cla
+ application/vnd.claymore
+
+
+ class
+ application/java
+
+
+ clkk
+ application/vnd.crick.clicker.keyboard
+
+
+ clkp
+ application/vnd.crick.clicker.palette
+
+
+ clkt
+ application/vnd.crick.clicker.template
+
+
+ clkw
+ application/vnd.crick.clicker.wordbank
+
+
+ clkx
+ application/vnd.crick.clicker
+
+
+ clp
+ application/x-msclip
+
+
+ cmc
+ application/vnd.cosmocaller
+
+
+ cmdf
+ chemical/x-cmdf
+
+
+ cml
+ chemical/x-cml
+
+
+ cmp
+ application/vnd.yellowriver-custom-menu
+
+
+ cmx
+ image/x-cmx
+
+
+ cod
+ application/vnd.rim.cod
+
+
+ com
+ application/x-msdownload
+
+
+ conf
+ text/plain
+
+
+ cpio
+ application/x-cpio
+
+
+ cpp
+ text/x-c
+
+
+ cpt
+ application/mac-compactpro
+
+
+ crd
+ application/x-mscardfile
+
+
+ crl
+ application/pkix-crl
+
+
+ crt
+ application/x-x509-ca-cert
+
+
+ cryptonote
+ application/vnd.rig.cryptonote
+
+
+ csh
+ application/x-csh
+
+
+ csml
+ chemical/x-csml
+
+
+ csp
+ application/vnd.commonspace
+
+
+ css
+ text/css
+
+
+ cst
+ application/x-director
+
+
+ csv
+ text/csv
+
+
+ cu
+ application/cu-seeme
+
+
+ curl
+ text/vnd.curl
+
+
+ cww
+ application/prs.cww
+
+
+ cxt
+ application/x-director
+
+
+ cxx
+ text/x-c
+
+
+ dae
+ model/vnd.collada+xml
+
+
+ daf
+ application/vnd.mobius.daf
+
+
+ dart
+ application/vnd.dart
+
+
+ dataless
+ application/vnd.fdsn.seed
+
+
+ davmount
+ application/davmount+xml
+
+
+ dbk
+ application/docbook+xml
+
+
+ dcr
+ application/x-director
+
+
+ dcurl
+ text/vnd.curl.dcurl
+
+
+ dd2
+ application/vnd.oma.dd2+xml
+
+
+ ddd
+ application/vnd.fujixerox.ddd
+
+
+ deb
+ application/x-debian-package
+
+
+ def
+ text/plain
+
+
+ deploy
+ application/octet-stream
+
+
+ der
+ application/x-x509-ca-cert
+
+
+ dfac
+ application/vnd.dreamfactory
+
+
+ dgc
+ application/x-dgc-compressed
+
+
+ dib
+ image/bmp
+
+
+ dic
+ text/x-c
+
+
+ dir
+ application/x-director
+
+
+ dis
+ application/vnd.mobius.dis
+
+
+ dist
+ application/octet-stream
+
+
+ distz
+ application/octet-stream
+
+
+ djv
+ image/vnd.djvu
+
+
+ djvu
+ image/vnd.djvu
+
+
+ dll
+ application/x-msdownload
+
+
+ dmg
+ application/x-apple-diskimage
+
+
+ dmp
+ application/vnd.tcpdump.pcap
+
+
+ dms
+ application/octet-stream
+
+
+ dna
+ application/vnd.dna
+
+
+ doc
+ application/msword
+
+
+ docm
+ application/vnd.ms-word.document.macroenabled.12
+
+
+ docx
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
+
+
+ dot
+ application/msword
+
+
+ dotm
+ application/vnd.ms-word.template.macroenabled.12
+
+
+ dotx
+ application/vnd.openxmlformats-officedocument.wordprocessingml.template
+
+
+ dp
+ application/vnd.osgi.dp
+
+
+ dpg
+ application/vnd.dpgraph
+
+
+ dra
+ audio/vnd.dra
+
+
+ dsc
+ text/prs.lines.tag
+
+
+ dssc
+ application/dssc+der
+
+
+ dtb
+ application/x-dtbook+xml
+
+
+ dtd
+ application/xml-dtd
+
+
+ dts
+ audio/vnd.dts
+
+
+ dtshd
+ audio/vnd.dts.hd
+
+
+ dump
+ application/octet-stream
+
+
+ dv
+ video/x-dv
+
+
+ dvb
+ video/vnd.dvb.file
+
+
+ dvi
+ application/x-dvi
+
+
+ dwf
+ model/vnd.dwf
+
+
+ dwg
+ image/vnd.dwg
+
+
+ dxf
+ image/vnd.dxf
+
+
+ dxp
+ application/vnd.spotfire.dxp
+
+
+ dxr
+ application/x-director
+
+
+ ecelp4800
+ audio/vnd.nuera.ecelp4800
+
+
+ ecelp7470
+ audio/vnd.nuera.ecelp7470
+
+
+ ecelp9600
+ audio/vnd.nuera.ecelp9600
+
+
+ ecma
+ application/ecmascript
+
+
+ edm
+ application/vnd.novadigm.edm
+
+
+ edx
+ application/vnd.novadigm.edx
+
+
+ efif
+ application/vnd.picsel
+
+
+ ei6
+ application/vnd.pg.osasli
+
+
+ elc
+ application/octet-stream
+
+
+ emf
+ application/x-msmetafile
+
+
+ eml
+ message/rfc822
+
+
+ emma
+ application/emma+xml
+
+
+ emz
+ application/x-msmetafile
+
+
+ eol
+ audio/vnd.digital-winds
+
+
+ eot
+ application/vnd.ms-fontobject
+
+
+ eps
+ application/postscript
+
+
+ epub
+ application/epub+zip
+
+
+ es3
+ application/vnd.eszigno3+xml
+
+
+ esa
+ application/vnd.osgi.subsystem
+
+
+ esf
+ application/vnd.epson.esf
+
+
+ et3
+ application/vnd.eszigno3+xml
+
+
+ etx
+ text/x-setext
+
+
+ eva
+ application/x-eva
+
+
+ evy
+ application/x-envoy
+
+
+ exe
+ application/octet-stream
+
+
+ exi
+ application/exi
+
+
+ ext
+ application/vnd.novadigm.ext
+
+
+ ez
+ application/andrew-inset
+
+
+ ez2
+ application/vnd.ezpix-album
+
+
+ ez3
+ application/vnd.ezpix-package
+
+
+ f
+ text/x-fortran
+
+
+ f4v
+ video/x-f4v
+
+
+ f77
+ text/x-fortran
+
+
+ f90
+ text/x-fortran
+
+
+ fbs
+ image/vnd.fastbidsheet
+
+
+ fcdt
+ application/vnd.adobe.formscentral.fcdt
+
+
+ fcs
+ application/vnd.isac.fcs
+
+
+ fdf
+ application/vnd.fdf
+
+
+ fe_launch
+ application/vnd.denovo.fcselayout-link
+
+
+ fg5
+ application/vnd.fujitsu.oasysgp
+
+
+ fgd
+ application/x-director
+
+
+ fh
+ image/x-freehand
+
+
+ fh4
+ image/x-freehand
+
+
+ fh5
+ image/x-freehand
+
+
+ fh7
+ image/x-freehand
+
+
+ fhc
+ image/x-freehand
+
+
+ fig
+ application/x-xfig
+
+
+ flac
+ audio/flac
+
+
+ fli
+ video/x-fli
+
+
+ flo
+ application/vnd.micrografx.flo
+
+
+ flv
+ video/x-flv
+
+
+ flw
+ application/vnd.kde.kivio
+
+
+ flx
+ text/vnd.fmi.flexstor
+
+
+ fly
+ text/vnd.fly
+
+
+ fm
+ application/vnd.framemaker
+
+
+ fnc
+ application/vnd.frogans.fnc
+
+
+ for
+ text/x-fortran
+
+
+ fpx
+ image/vnd.fpx
+
+
+ frame
+ application/vnd.framemaker
+
+
+ fsc
+ application/vnd.fsc.weblaunch
+
+
+ fst
+ image/vnd.fst
+
+
+ ftc
+ application/vnd.fluxtime.clip
+
+
+ fti
+ application/vnd.anser-web-funds-transfer-initiation
+
+
+ fvt
+ video/vnd.fvt
+
+
+ fxp
+ application/vnd.adobe.fxp
+
+
+ fxpl
+ application/vnd.adobe.fxp
+
+
+ fzs
+ application/vnd.fuzzysheet
+
+
+ g2w
+ application/vnd.geoplan
+
+
+ g3
+ image/g3fax
+
+
+ g3w
+ application/vnd.geospace
+
+
+ gac
+ application/vnd.groove-account
+
+
+ gam
+ application/x-tads
+
+
+ gbr
+ application/rpki-ghostbusters
+
+
+ gca
+ application/x-gca-compressed
+
+
+ gdl
+ model/vnd.gdl
+
+
+ geo
+ application/vnd.dynageo
+
+
+ gex
+ application/vnd.geometry-explorer
+
+
+ ggb
+ application/vnd.geogebra.file
+
+
+ ggt
+ application/vnd.geogebra.tool
+
+
+ ghf
+ application/vnd.groove-help
+
+
+ gif
+ image/gif
+
+
+ gim
+ application/vnd.groove-identity-message
+
+
+ gml
+ application/gml+xml
+
+
+ gmx
+ application/vnd.gmx
+
+
+ gnumeric
+ application/x-gnumeric
+
+
+ gph
+ application/vnd.flographit
+
+
+ gpx
+ application/gpx+xml
+
+
+ gqf
+ application/vnd.grafeq
+
+
+ gqs
+ application/vnd.grafeq
+
+
+ gram
+ application/srgs
+
+
+ gramps
+ application/x-gramps-xml
+
+
+ gre
+ application/vnd.geometry-explorer
+
+
+ grv
+ application/vnd.groove-injector
+
+
+ grxml
+ application/srgs+xml
+
+
+ gsf
+ application/x-font-ghostscript
+
+
+ gtar
+ application/x-gtar
+
+
+ gtm
+ application/vnd.groove-tool-message
+
+
+ gtw
+ model/vnd.gtw
+
+
+ gv
+ text/vnd.graphviz
+
+
+ gxf
+ application/gxf
+
+
+ gxt
+ application/vnd.geonext
+
+
+ gz
+ application/x-gzip
+
+
+ h
+ text/x-c
+
+
+ h261
+ video/h261
+
+
+ h263
+ video/h263
+
+
+ h264
+ video/h264
+
+
+ hal
+ application/vnd.hal+xml
+
+
+ hbci
+ application/vnd.hbci
+
+
+ hdf
+ application/x-hdf
+
+
+ hh
+ text/x-c
+
+
+ hlp
+ application/winhlp
+
+
+ hpgl
+ application/vnd.hp-hpgl
+
+
+ hpid
+ application/vnd.hp-hpid
+
+
+ hps
+ application/vnd.hp-hps
+
+
+ hqx
+ application/mac-binhex40
+
+
+ htc
+ text/x-component
+
+
+ htke
+ application/vnd.kenameaapp
+
+
+ htm
+ text/html
+
+
+ html
+ text/html
+
+
+ hvd
+ application/vnd.yamaha.hv-dic
+
+
+ hvp
+ application/vnd.yamaha.hv-voice
+
+
+ hvs
+ application/vnd.yamaha.hv-script
+
+
+ i2g
+ application/vnd.intergeo
+
+
+ icc
+ application/vnd.iccprofile
+
+
+ ice
+ x-conference/x-cooltalk
+
+
+ icm
+ application/vnd.iccprofile
+
+
+ ico
+ image/x-icon
+
+
+ ics
+ text/calendar
+
+
+ ief
+ image/ief
+
+
+ ifb
+ text/calendar
+
+
+ ifm
+ application/vnd.shana.informed.formdata
+
+
+ iges
+ model/iges
+
+
+ igl
+ application/vnd.igloader
+
+
+ igm
+ application/vnd.insors.igm
+
+
+ igs
+ model/iges
+
+
+ igx
+ application/vnd.micrografx.igx
+
+
+ iif
+ application/vnd.shana.informed.interchange
+
+
+ imp
+ application/vnd.accpac.simply.imp
+
+
+ ims
+ application/vnd.ms-ims
+
+
+ in
+ text/plain
+
+
+ ink
+ application/inkml+xml
+
+
+ inkml
+ application/inkml+xml
+
+
+ install
+ application/x-install-instructions
+
+
+ iota
+ application/vnd.astraea-software.iota
+
+
+ ipfix
+ application/ipfix
+
+
+ ipk
+ application/vnd.shana.informed.package
+
+
+ irm
+ application/vnd.ibm.rights-management
+
+
+ irp
+ application/vnd.irepository.package+xml
+
+
+ iso
+ application/x-iso9660-image
+
+
+ itp
+ application/vnd.shana.informed.formtemplate
+
+
+ ivp
+ application/vnd.immervision-ivp
+
+
+ ivu
+ application/vnd.immervision-ivu
+
+
+ jad
+ text/vnd.sun.j2me.app-descriptor
+
+
+ jam
+ application/vnd.jam
+
+
+ jar
+ application/java-archive
+
+
+ java
+ text/x-java-source
+
+
+ jisp
+ application/vnd.jisp
+
+
+ jlt
+ application/vnd.hp-jlyt
+
+
+ jnlp
+ application/x-java-jnlp-file
+
+
+ joda
+ application/vnd.joost.joda-archive
+
+
+ jpe
+ image/jpeg
+
+
+ jpeg
+ image/jpeg
+
+
+ jpg
+ image/jpeg
+
+
+ jpgm
+ video/jpm
+
+
+ jpgv
+ video/jpeg
+
+
+ jpm
+ video/jpm
+
+
+ js
+ application/javascript
+
+
+ jsf
+ text/plain
+
+
+ json
+ application/json
+
+
+ jsonml
+ application/jsonml+json
+
+
+ jspf
+ text/plain
+
+
+ kar
+ audio/midi
+
+
+ karbon
+ application/vnd.kde.karbon
+
+
+ kfo
+ application/vnd.kde.kformula
+
+
+ kia
+ application/vnd.kidspiration
+
+
+ kml
+ application/vnd.google-earth.kml+xml
+
+
+ kmz
+ application/vnd.google-earth.kmz
+
+
+ kne
+ application/vnd.kinar
+
+
+ knp
+ application/vnd.kinar
+
+
+ kon
+ application/vnd.kde.kontour
+
+
+ kpr
+ application/vnd.kde.kpresenter
+
+
+ kpt
+ application/vnd.kde.kpresenter
+
+
+ kpxx
+ application/vnd.ds-keypoint
+
+
+ ksp
+ application/vnd.kde.kspread
+
+
+ ktr
+ application/vnd.kahootz
+
+
+ ktx
+ image/ktx
+
+
+ ktz
+ application/vnd.kahootz
+
+
+ kwd
+ application/vnd.kde.kword
+
+
+ kwt
+ application/vnd.kde.kword
+
+
+ lasxml
+ application/vnd.las.las+xml
+
+
+ latex
+ application/x-latex
+
+
+ lbd
+ application/vnd.llamagraphics.life-balance.desktop
+
+
+ lbe
+ application/vnd.llamagraphics.life-balance.exchange+xml
+
+
+ les
+ application/vnd.hhe.lesson-player
+
+
+ lha
+ application/x-lzh-compressed
+
+
+ link66
+ application/vnd.route66.link66+xml
+
+
+ list
+ text/plain
+
+
+ list3820
+ application/vnd.ibm.modcap
+
+
+ listafp
+ application/vnd.ibm.modcap
+
+
+ lnk
+ application/x-ms-shortcut
+
+
+ log
+ text/plain
+
+
+ lostxml
+ application/lost+xml
+
+
+ lrf
+ application/octet-stream
+
+
+ lrm
+ application/vnd.ms-lrm
+
+
+ ltf
+ application/vnd.frogans.ltf
+
+
+ lvp
+ audio/vnd.lucent.voice
+
+
+ lwp
+ application/vnd.lotus-wordpro
+
+
+ lzh
+ application/x-lzh-compressed
+
+
+ m13
+ application/x-msmediaview
+
+
+ m14
+ application/x-msmediaview
+
+
+ m1v
+ video/mpeg
+
+
+ m21
+ application/mp21
+
+
+ m2a
+ audio/mpeg
+
+
+ m2v
+ video/mpeg
+
+
+ m3a
+ audio/mpeg
+
+
+ m3u
+ audio/x-mpegurl
+
+
+ m3u8
+ application/vnd.apple.mpegurl
+
+
+ m4a
+ audio/mp4
+
+
+ m4b
+ audio/mp4
+
+
+ m4r
+ audio/mp4
+
+
+ m4u
+ video/vnd.mpegurl
+
+
+ m4v
+ video/mp4
+
+
+ ma
+ application/mathematica
+
+
+ mac
+ image/x-macpaint
+
+
+ mads
+ application/mads+xml
+
+
+ mag
+ application/vnd.ecowin.chart
+
+
+ maker
+ application/vnd.framemaker
+
+
+ man
+ text/troff
+
+
+ mar
+ application/octet-stream
+
+
+ mathml
+ application/mathml+xml
+
+
+ mb
+ application/mathematica
+
+
+ mbk
+ application/vnd.mobius.mbk
+
+
+ mbox
+ application/mbox
+
+
+ mc1
+ application/vnd.medcalcdata
+
+
+ mcd
+ application/vnd.mcd
+
+
+ mcurl
+ text/vnd.curl.mcurl
+
+
+ mdb
+ application/x-msaccess
+
+
+ mdi
+ image/vnd.ms-modi
+
+
+ me
+ text/troff
+
+
+ mesh
+ model/mesh
+
+
+ meta4
+ application/metalink4+xml
+
+
+ metalink
+ application/metalink+xml
+
+
+ mets
+ application/mets+xml
+
+
+ mfm
+ application/vnd.mfmp
+
+
+ mft
+ application/rpki-manifest
+
+
+ mgp
+ application/vnd.osgeo.mapguide.package
+
+
+ mgz
+ application/vnd.proteus.magazine
+
+
+ mid
+ audio/midi
+
+
+ midi
+ audio/midi
+
+
+ mie
+ application/x-mie
+
+
+ mif
+ application/x-mif
+
+
+ mime
+ message/rfc822
+
+
+ mj2
+ video/mj2
+
+
+ mjp2
+ video/mj2
+
+
+ mk3d
+ video/x-matroska
+
+
+ mka
+ audio/x-matroska
+
+
+ mks
+ video/x-matroska
+
+
+ mkv
+ video/x-matroska
+
+
+ mlp
+ application/vnd.dolby.mlp
+
+
+ mmd
+ application/vnd.chipnuts.karaoke-mmd
+
+
+ mmf
+ application/vnd.smaf
+
+
+ mmr
+ image/vnd.fujixerox.edmics-mmr
+
+
+ mng
+ video/x-mng
+
+
+ mny
+ application/x-msmoney
+
+
+ mobi
+ application/x-mobipocket-ebook
+
+
+ mods
+ application/mods+xml
+
+
+ mov
+ video/quicktime
+
+
+ movie
+ video/x-sgi-movie
+
+
+ mp1
+ audio/mpeg
+
+
+ mp2
+ audio/mpeg
+
+
+ mp21
+ application/mp21
+
+
+ mp2a
+ audio/mpeg
+
+
+ mp3
+ audio/mpeg
+
+
+ mp4
+ video/mp4
+
+
+ mp4a
+ audio/mp4
+
+
+ mp4s
+ application/mp4
+
+
+ mp4v
+ video/mp4
+
+
+ mpa
+ audio/mpeg
+
+
+ mpc
+ application/vnd.mophun.certificate
+
+
+ mpe
+ video/mpeg
+
+
+ mpeg
+ video/mpeg
+
+
+ mpega
+ audio/x-mpeg
+
+
+ mpg
+ video/mpeg
+
+
+ mpg4
+ video/mp4
+
+
+ mpga
+ audio/mpeg
+
+
+ mpkg
+ application/vnd.apple.installer+xml
+
+
+ mpm
+ application/vnd.blueice.multipass
+
+
+ mpn
+ application/vnd.mophun.application
+
+
+ mpp
+ application/vnd.ms-project
+
+
+ mpt
+ application/vnd.ms-project
+
+
+ mpv2
+ video/mpeg2
+
+
+ mpy
+ application/vnd.ibm.minipay
+
+
+ mqy
+ application/vnd.mobius.mqy
+
+
+ mrc
+ application/marc
+
+
+ mrcx
+ application/marcxml+xml
+
+
+ ms
+ text/troff
+
+
+ mscml
+ application/mediaservercontrol+xml
+
+
+ mseed
+ application/vnd.fdsn.mseed
+
+
+ mseq
+ application/vnd.mseq
+
+
+ msf
+ application/vnd.epson.msf
+
+
+ msh
+ model/mesh
+
+
+ msi
+ application/x-msdownload
+
+
+ msl
+ application/vnd.mobius.msl
+
+
+ msty
+ application/vnd.muvee.style
+
+
+ mts
+ model/vnd.mts
+
+
+ mus
+ application/vnd.musician
+
+
+ musicxml
+ application/vnd.recordare.musicxml+xml
+
+
+ mvb
+ application/x-msmediaview
+
+
+ mwf
+ application/vnd.mfer
+
+
+ mxf
+ application/mxf
+
+
+ mxl
+ application/vnd.recordare.musicxml
+
+
+ mxml
+ application/xv+xml
+
+
+ mxs
+ application/vnd.triscape.mxs
+
+
+ mxu
+ video/vnd.mpegurl
+
+
+ n-gage
+ application/vnd.nokia.n-gage.symbian.install
+
+
+ n3
+ text/n3
+
+
+ nb
+ application/mathematica
+
+
+ nbp
+ application/vnd.wolfram.player
+
+
+ nc
+ application/x-netcdf
+
+
+ ncx
+ application/x-dtbncx+xml
+
+
+ nfo
+ text/x-nfo
+
+
+ ngdat
+ application/vnd.nokia.n-gage.data
+
+
+ nitf
+ application/vnd.nitf
+
+
+ nlu
+ application/vnd.neurolanguage.nlu
+
+
+ nml
+ application/vnd.enliven
+
+
+ nnd
+ application/vnd.noblenet-directory
+
+
+ nns
+ application/vnd.noblenet-sealer
+
+
+ nnw
+ application/vnd.noblenet-web
+
+
+ npx
+ image/vnd.net-fpx
+
+
+ nsc
+ application/x-conference
+
+
+ nsf
+ application/vnd.lotus-notes
+
+
+ ntf
+ application/vnd.nitf
+
+
+ nzb
+ application/x-nzb
+
+
+ oa2
+ application/vnd.fujitsu.oasys2
+
+
+ oa3
+ application/vnd.fujitsu.oasys3
+
+
+ oas
+ application/vnd.fujitsu.oasys
+
+
+ obd
+ application/x-msbinder
+
+
+ obj
+ application/x-tgif
+
+
+ oda
+ application/oda
+
+
+
+ odb
+ application/vnd.oasis.opendocument.database
+
+
+
+ odc
+ application/vnd.oasis.opendocument.chart
+
+
+
+ odf
+ application/vnd.oasis.opendocument.formula
+
+
+ odft
+ application/vnd.oasis.opendocument.formula-template
+
+
+
+ odg
+ application/vnd.oasis.opendocument.graphics
+
+
+
+ odi
+ application/vnd.oasis.opendocument.image
+
+
+
+ odm
+ application/vnd.oasis.opendocument.text-master
+
+
+
+ odp
+ application/vnd.oasis.opendocument.presentation
+
+
+
+ ods
+ application/vnd.oasis.opendocument.spreadsheet
+
+
+
+ odt
+ application/vnd.oasis.opendocument.text
+
+
+ oga
+ audio/ogg
+
+
+ ogg
+ audio/ogg
+
+
+ ogv
+ video/ogg
+
+
+
+ ogx
+ application/ogg
+
+
+ omdoc
+ application/omdoc+xml
+
+
+ onepkg
+ application/onenote
+
+
+ onetmp
+ application/onenote
+
+
+ onetoc
+ application/onenote
+
+
+ onetoc2
+ application/onenote
+
+
+ opf
+ application/oebps-package+xml
+
+
+ opml
+ text/x-opml
+
+
+ oprc
+ application/vnd.palm
+
+
+ org
+ application/vnd.lotus-organizer
+
+
+ osf
+ application/vnd.yamaha.openscoreformat
+
+
+ osfpvg
+ application/vnd.yamaha.openscoreformat.osfpvg+xml
+
+
+ otc
+ application/vnd.oasis.opendocument.chart-template
+
+
+ otf
+ application/x-font-otf
+
+
+
+ otg
+ application/vnd.oasis.opendocument.graphics-template
+
+
+
+ oth
+ application/vnd.oasis.opendocument.text-web
+
+
+ oti
+ application/vnd.oasis.opendocument.image-template
+
+
+
+ otp
+ application/vnd.oasis.opendocument.presentation-template
+
+
+
+ ots
+ application/vnd.oasis.opendocument.spreadsheet-template
+
+
+
+ ott
+ application/vnd.oasis.opendocument.text-template
+
+
+ oxps
+ application/oxps
+
+
+ oxt
+ application/vnd.openofficeorg.extension
+
+
+ p
+ text/x-pascal
+
+
+ p10
+ application/pkcs10
+
+
+ p12
+ application/x-pkcs12
+
+
+ p7b
+ application/x-pkcs7-certificates
+
+
+ p7c
+ application/pkcs7-mime
+
+
+ p7m
+ application/pkcs7-mime
+
+
+ p7r
+ application/x-pkcs7-certreqresp
+
+
+ p7s
+ application/pkcs7-signature
+
+
+ p8
+ application/pkcs8
+
+
+ pas
+ text/x-pascal
+
+
+ paw
+ application/vnd.pawaafile
+
+
+ pbd
+ application/vnd.powerbuilder6
+
+
+ pbm
+ image/x-portable-bitmap
+
+
+ pcap
+ application/vnd.tcpdump.pcap
+
+
+ pcf
+ application/x-font-pcf
+
+
+ pcl
+ application/vnd.hp-pcl
+
+
+ pclxl
+ application/vnd.hp-pclxl
+
+
+ pct
+ image/pict
+
+
+ pcurl
+ application/vnd.curl.pcurl
+
+
+ pcx
+ image/x-pcx
+
+
+ pdb
+ application/vnd.palm
+
+
+ pdf
+ application/pdf
+
+
+ pfa
+ application/x-font-type1
+
+
+ pfb
+ application/x-font-type1
+
+
+ pfm
+ application/x-font-type1
+
+
+ pfr
+ application/font-tdpfr
+
+
+ pfx
+ application/x-pkcs12
+
+
+ pgm
+ image/x-portable-graymap
+
+
+ pgn
+ application/x-chess-pgn
+
+
+ pgp
+ application/pgp-encrypted
+
+
+ pic
+ image/pict
+
+
+ pict
+ image/pict
+
+
+ pkg
+ application/octet-stream
+
+
+ pki
+ application/pkixcmp
+
+
+ pkipath
+ application/pkix-pkipath
+
+
+ plb
+ application/vnd.3gpp.pic-bw-large
+
+
+ plc
+ application/vnd.mobius.plc
+
+
+ plf
+ application/vnd.pocketlearn
+
+
+ pls
+ audio/x-scpls
+
+
+ pml
+ application/vnd.ctc-posml
+
+
+ png
+ image/png
+
+
+ pnm
+ image/x-portable-anymap
+
+
+ pnt
+ image/x-macpaint
+
+
+ portpkg
+ application/vnd.macports.portpkg
+
+
+ pot
+ application/vnd.ms-powerpoint
+
+
+ potm
+ application/vnd.ms-powerpoint.template.macroenabled.12
+
+
+ potx
+ application/vnd.openxmlformats-officedocument.presentationml.template
+
+
+ ppam
+ application/vnd.ms-powerpoint.addin.macroenabled.12
+
+
+ ppd
+ application/vnd.cups-ppd
+
+
+ ppm
+ image/x-portable-pixmap
+
+
+ pps
+ application/vnd.ms-powerpoint
+
+
+ ppsm
+ application/vnd.ms-powerpoint.slideshow.macroenabled.12
+
+
+ ppsx
+ application/vnd.openxmlformats-officedocument.presentationml.slideshow
+
+
+ ppt
+ application/vnd.ms-powerpoint
+
+
+ pptm
+ application/vnd.ms-powerpoint.presentation.macroenabled.12
+
+
+ pptx
+ application/vnd.openxmlformats-officedocument.presentationml.presentation
+
+
+ pqa
+ application/vnd.palm
+
+
+ prc
+ application/x-mobipocket-ebook
+
+
+ pre
+ application/vnd.lotus-freelance
+
+
+ prf
+ application/pics-rules
+
+
+ ps
+ application/postscript
+
+
+ psb
+ application/vnd.3gpp.pic-bw-small
+
+
+ psd
+ image/vnd.adobe.photoshop
+
+
+ psf
+ application/x-font-linux-psf
+
+
+ pskcxml
+ application/pskc+xml
+
+
+ ptid
+ application/vnd.pvi.ptid1
+
+
+ pub
+ application/x-mspublisher
+
+
+ pvb
+ application/vnd.3gpp.pic-bw-var
+
+
+ pwn
+ application/vnd.3m.post-it-notes
+
+
+ pya
+ audio/vnd.ms-playready.media.pya
+
+
+ pyv
+ video/vnd.ms-playready.media.pyv
+
+
+ qam
+ application/vnd.epson.quickanime
+
+
+ qbo
+ application/vnd.intu.qbo
+
+
+ qfx
+ application/vnd.intu.qfx
+
+
+ qps
+ application/vnd.publishare-delta-tree
+
+
+ qt
+ video/quicktime
+
+
+ qti
+ image/x-quicktime
+
+
+ qtif
+ image/x-quicktime
+
+
+ qwd
+ application/vnd.quark.quarkxpress
+
+
+ qwt
+ application/vnd.quark.quarkxpress
+
+
+ qxb
+ application/vnd.quark.quarkxpress
+
+
+ qxd
+ application/vnd.quark.quarkxpress
+
+
+ qxl
+ application/vnd.quark.quarkxpress
+
+
+ qxt
+ application/vnd.quark.quarkxpress
+
+
+ ra
+ audio/x-pn-realaudio
+
+
+ ram
+ audio/x-pn-realaudio
+
+
+ rar
+ application/x-rar-compressed
+
+
+ ras
+ image/x-cmu-raster
+
+
+ rcprofile
+ application/vnd.ipunplugged.rcprofile
+
+
+ rdf
+ application/rdf+xml
+
+
+ rdz
+ application/vnd.data-vision.rdz
+
+
+ rep
+ application/vnd.businessobjects
+
+
+ res
+ application/x-dtbresource+xml
+
+
+ rgb
+ image/x-rgb
+
+
+ rif
+ application/reginfo+xml
+
+
+ rip
+ audio/vnd.rip
+
+
+ ris
+ application/x-research-info-systems
+
+
+ rl
+ application/resource-lists+xml
+
+
+ rlc
+ image/vnd.fujixerox.edmics-rlc
+
+
+ rld
+ application/resource-lists-diff+xml
+
+
+ rm
+ application/vnd.rn-realmedia
+
+
+ rmi
+ audio/midi
+
+
+ rmp
+ audio/x-pn-realaudio-plugin
+
+
+ rms
+ application/vnd.jcp.javame.midlet-rms
+
+
+ rmvb
+ application/vnd.rn-realmedia-vbr
+
+
+ rnc
+ application/relax-ng-compact-syntax
+
+
+ roa
+ application/rpki-roa
+
+
+ roff
+ text/troff
+
+
+ rp9
+ application/vnd.cloanto.rp9
+
+
+ rpss
+ application/vnd.nokia.radio-presets
+
+
+ rpst
+ application/vnd.nokia.radio-preset
+
+
+ rq
+ application/sparql-query
+
+
+ rs
+ application/rls-services+xml
+
+
+ rsd
+ application/rsd+xml
+
+
+ rss
+ application/rss+xml
+
+
+ rtf
+ application/rtf
+
+
+ rtx
+ text/richtext
+
+
+ s
+ text/x-asm
+
+
+ s3m
+ audio/s3m
+
+
+ saf
+ application/vnd.yamaha.smaf-audio
+
+
+ sbml
+ application/sbml+xml
+
+
+ sc
+ application/vnd.ibm.secure-container
+
+
+ scd
+ application/x-msschedule
+
+
+ scm
+ application/vnd.lotus-screencam
+
+
+ scq
+ application/scvp-cv-request
+
+
+ scs
+ application/scvp-cv-response
+
+
+ scurl
+ text/vnd.curl.scurl
+
+
+ sda
+ application/vnd.stardivision.draw
+
+
+ sdc
+ application/vnd.stardivision.calc
+
+
+ sdd
+ application/vnd.stardivision.impress
+
+
+ sdkd
+ application/vnd.solent.sdkm+xml
+
+
+ sdkm
+ application/vnd.solent.sdkm+xml
+
+
+ sdp
+ application/sdp
+
+
+ sdw
+ application/vnd.stardivision.writer
+
+
+ see
+ application/vnd.seemail
+
+
+ seed
+ application/vnd.fdsn.seed
+
+
+ sema
+ application/vnd.sema
+
+
+ semd
+ application/vnd.semd
+
+
+ semf
+ application/vnd.semf
+
+
+ ser
+ application/java-serialized-object
+
+
+ setpay
+ application/set-payment-initiation
+
+
+ setreg
+ application/set-registration-initiation
+
+
+ sfd-hdstx
+ application/vnd.hydrostatix.sof-data
+
+
+ sfs
+ application/vnd.spotfire.sfs
+
+
+ sfv
+ text/x-sfv
+
+
+ sgi
+ image/sgi
+
+
+ sgl
+ application/vnd.stardivision.writer-global
+
+
+ sgm
+ text/sgml
+
+
+ sgml
+ text/sgml
+
+
+ sh
+ application/x-sh
+
+
+ shar
+ application/x-shar
+
+
+ shf
+ application/shf+xml
+
+
+
+ sid
+ image/x-mrsid-image
+
+
+ sig
+ application/pgp-signature
+
+
+ sil
+ audio/silk
+
+
+ silo
+ model/mesh
+
+
+ sis
+ application/vnd.symbian.install
+
+
+ sisx
+ application/vnd.symbian.install
+
+
+ sit
+ application/x-stuffit
+
+
+ sitx
+ application/x-stuffitx
+
+
+ skd
+ application/vnd.koan
+
+
+ skm
+ application/vnd.koan
+
+
+ skp
+ application/vnd.koan
+
+
+ skt
+ application/vnd.koan
+
+
+ sldm
+ application/vnd.ms-powerpoint.slide.macroenabled.12
+
+
+ sldx
+ application/vnd.openxmlformats-officedocument.presentationml.slide
+
+
+ slt
+ application/vnd.epson.salt
+
+
+ sm
+ application/vnd.stepmania.stepchart
+
+
+ smf
+ application/vnd.stardivision.math
+
+
+ smi
+ application/smil+xml
+
+
+ smil
+ application/smil+xml
+
+
+ smv
+ video/x-smv
+
+
+ smzip
+ application/vnd.stepmania.package
+
+
+ snd
+ audio/basic
+
+
+ snf
+ application/x-font-snf
+
+
+ so
+ application/octet-stream
+
+
+ spc
+ application/x-pkcs7-certificates
+
+
+ spf
+ application/vnd.yamaha.smaf-phrase
+
+
+ spl
+ application/x-futuresplash
+
+
+ spot
+ text/vnd.in3d.spot
+
+
+ spp
+ application/scvp-vp-response
+
+
+ spq
+ application/scvp-vp-request
+
+
+ spx
+ audio/ogg
+
+
+ sql
+ application/x-sql
+
+
+ src
+ application/x-wais-source
+
+
+ srt
+ application/x-subrip
+
+
+ sru
+ application/sru+xml
+
+
+ srx
+ application/sparql-results+xml
+
+
+ ssdl
+ application/ssdl+xml
+
+
+ sse
+ application/vnd.kodak-descriptor
+
+
+ ssf
+ application/vnd.epson.ssf
+
+
+ ssml
+ application/ssml+xml
+
+
+ st
+ application/vnd.sailingtracker.track
+
+
+ stc
+ application/vnd.sun.xml.calc.template
+
+
+ std
+ application/vnd.sun.xml.draw.template
+
+
+ stf
+ application/vnd.wt.stf
+
+
+ sti
+ application/vnd.sun.xml.impress.template
+
+
+ stk
+ application/hyperstudio
+
+
+ stl
+ application/vnd.ms-pki.stl
+
+
+ str
+ application/vnd.pg.format
+
+
+ stw
+ application/vnd.sun.xml.writer.template
+
+
+ sub
+ text/vnd.dvb.subtitle
+
+
+ sus
+ application/vnd.sus-calendar
+
+
+ susp
+ application/vnd.sus-calendar
+
+
+ sv4cpio
+ application/x-sv4cpio
+
+
+ sv4crc
+ application/x-sv4crc
+
+
+ svc
+ application/vnd.dvb.service
+
+
+ svd
+ application/vnd.svd
+
+
+ svg
+ image/svg+xml
+
+
+ svgz
+ image/svg+xml
+
+
+ swa
+ application/x-director
+
+
+ swf
+ application/x-shockwave-flash
+
+
+ swi
+ application/vnd.aristanetworks.swi
+
+
+ sxc
+ application/vnd.sun.xml.calc
+
+
+ sxd
+ application/vnd.sun.xml.draw
+
+
+ sxg
+ application/vnd.sun.xml.writer.global
+
+
+ sxi
+ application/vnd.sun.xml.impress
+
+
+ sxm
+ application/vnd.sun.xml.math
+
+
+ sxw
+ application/vnd.sun.xml.writer
+
+
+ t
+ text/troff
+
+
+ t3
+ application/x-t3vm-image
+
+
+ taglet
+ application/vnd.mynfc
+
+
+ tao
+ application/vnd.tao.intent-module-archive
+
+
+ tar
+ application/x-tar
+
+
+ tcap
+ application/vnd.3gpp2.tcap
+
+
+ tcl
+ application/x-tcl
+
+
+ teacher
+ application/vnd.smart.teacher
+
+
+ tei
+ application/tei+xml
+
+
+ teicorpus
+ application/tei+xml
+
+
+ tex
+ application/x-tex
+
+
+ texi
+ application/x-texinfo
+
+
+ texinfo
+ application/x-texinfo
+
+
+ text
+ text/plain
+
+
+ tfi
+ application/thraud+xml
+
+
+ tfm
+ application/x-tex-tfm
+
+
+ tga
+ image/x-tga
+
+
+ thmx
+ application/vnd.ms-officetheme
+
+
+ tif
+ image/tiff
+
+
+ tiff
+ image/tiff
+
+
+ tmo
+ application/vnd.tmobile-livetv
+
+
+ torrent
+ application/x-bittorrent
+
+
+ tpl
+ application/vnd.groove-tool-template
+
+
+ tpt
+ application/vnd.trid.tpt
+
+
+ tr
+ text/troff
+
+
+ tra
+ application/vnd.trueapp
+
+
+ trm
+ application/x-msterminal
+
+
+ tsd
+ application/timestamped-data
+
+
+ tsv
+ text/tab-separated-values
+
+
+ ttc
+ application/x-font-ttf
+
+
+ ttf
+ application/x-font-ttf
+
+
+ ttl
+ text/turtle
+
+
+ twd
+ application/vnd.simtech-mindmapper
+
+
+ twds
+ application/vnd.simtech-mindmapper
+
+
+ txd
+ application/vnd.genomatix.tuxedo
+
+
+ txf
+ application/vnd.mobius.txf
+
+
+ txt
+ text/plain
+
+
+ u32
+ application/x-authorware-bin
+
+
+ udeb
+ application/x-debian-package
+
+
+ ufd
+ application/vnd.ufdl
+
+
+ ufdl
+ application/vnd.ufdl
+
+
+ ulw
+ audio/basic
+
+
+ ulx
+ application/x-glulx
+
+
+ umj
+ application/vnd.umajin
+
+
+ unityweb
+ application/vnd.unity
+
+
+ uoml
+ application/vnd.uoml+xml
+
+
+ uri
+ text/uri-list
+
+
+ uris
+ text/uri-list
+
+
+ urls
+ text/uri-list
+
+
+ ustar
+ application/x-ustar
+
+
+ utz
+ application/vnd.uiq.theme
+
+
+ uu
+ text/x-uuencode
+
+
+ uva
+ audio/vnd.dece.audio
+
+
+ uvd
+ application/vnd.dece.data
+
+
+ uvf
+ application/vnd.dece.data
+
+
+ uvg
+ image/vnd.dece.graphic
+
+
+ uvh
+ video/vnd.dece.hd
+
+
+ uvi
+ image/vnd.dece.graphic
+
+
+ uvm
+ video/vnd.dece.mobile
+
+
+ uvp
+ video/vnd.dece.pd
+
+
+ uvs
+ video/vnd.dece.sd
+
+
+ uvt
+ application/vnd.dece.ttml+xml
+
+
+ uvu
+ video/vnd.uvvu.mp4
+
+
+ uvv
+ video/vnd.dece.video
+
+
+ uvva
+ audio/vnd.dece.audio
+
+
+ uvvd
+ application/vnd.dece.data
+
+
+ uvvf
+ application/vnd.dece.data
+
+
+ uvvg
+ image/vnd.dece.graphic
+
+
+ uvvh
+ video/vnd.dece.hd
+
+
+ uvvi
+ image/vnd.dece.graphic
+
+
+ uvvm
+ video/vnd.dece.mobile
+
+
+ uvvp
+ video/vnd.dece.pd
+
+
+ uvvs
+ video/vnd.dece.sd
+
+
+ uvvt
+ application/vnd.dece.ttml+xml
+
+
+ uvvu
+ video/vnd.uvvu.mp4
+
+
+ uvvv
+ video/vnd.dece.video
+
+
+ uvvx
+ application/vnd.dece.unspecified
+
+
+ uvvz
+ application/vnd.dece.zip
+
+
+ uvx
+ application/vnd.dece.unspecified
+
+
+ uvz
+ application/vnd.dece.zip
+
+
+ vcard
+ text/vcard
+
+
+ vcd
+ application/x-cdlink
+
+
+ vcf
+ text/x-vcard
+
+
+ vcg
+ application/vnd.groove-vcard
+
+
+ vcs
+ text/x-vcalendar
+
+
+ vcx
+ application/vnd.vcx
+
+
+ vis
+ application/vnd.visionary
+
+
+ viv
+ video/vnd.vivo
+
+
+ vob
+ video/x-ms-vob
+
+
+ vor
+ application/vnd.stardivision.writer
+
+
+ vox
+ application/x-authorware-bin
+
+
+ vrml
+ model/vrml
+
+
+ vsd
+ application/vnd.visio
+
+
+ vsf
+ application/vnd.vsf
+
+
+ vss
+ application/vnd.visio
+
+
+ vst
+ application/vnd.visio
+
+
+ vsw
+ application/vnd.visio
+
+
+ vtu
+ model/vnd.vtu
+
+
+ vxml
+ application/voicexml+xml
+
+
+ w3d
+ application/x-director
+
+
+ wad
+ application/x-doom
+
+
+ wav
+ audio/x-wav
+
+
+ wax
+ audio/x-ms-wax
+
+
+
+ wbmp
+ image/vnd.wap.wbmp
+
+
+ wbs
+ application/vnd.criticaltools.wbs+xml
+
+
+ wbxml
+ application/vnd.wap.wbxml
+
+
+ wcm
+ application/vnd.ms-works
+
+
+ wdb
+ application/vnd.ms-works
+
+
+ wdp
+ image/vnd.ms-photo
+
+
+ weba
+ audio/webm
+
+
+ webm
+ video/webm
+
+
+ webp
+ image/webp
+
+
+ wg
+ application/vnd.pmi.widget
+
+
+ wgt
+ application/widget
+
+
+ wks
+ application/vnd.ms-works
+
+
+ wm
+ video/x-ms-wm
+
+
+ wma
+ audio/x-ms-wma
+
+
+ wmd
+ application/x-ms-wmd
+
+
+ wmf
+ application/x-msmetafile
+
+
+
+ wml
+ text/vnd.wap.wml
+
+
+
+ wmlc
+ application/vnd.wap.wmlc
+
+
+
+ wmls
+ text/vnd.wap.wmlscript
+
+
+
+ wmlsc
+ application/vnd.wap.wmlscriptc
+
+
+ wmv
+ video/x-ms-wmv
+
+
+ wmx
+ video/x-ms-wmx
+
+
+ wmz
+ application/x-msmetafile
+
+
+ woff
+ application/x-font-woff
+
+
+ wpd
+ application/vnd.wordperfect
+
+
+ wpl
+ application/vnd.ms-wpl
+
+
+ wps
+ application/vnd.ms-works
+
+
+ wqd
+ application/vnd.wqd
+
+
+ wri
+ application/x-mswrite
+
+
+ wrl
+ model/vrml
+
+
+ wsdl
+ application/wsdl+xml
+
+
+ wspolicy
+ application/wspolicy+xml
+
+
+ wtb
+ application/vnd.webturbo
+
+
+ wvx
+ video/x-ms-wvx
+
+
+ x32
+ application/x-authorware-bin
+
+
+ x3d
+ model/x3d+xml
+
+
+ x3db
+ model/x3d+binary
+
+
+ x3dbz
+ model/x3d+binary
+
+
+ x3dv
+ model/x3d+vrml
+
+
+ x3dvz
+ model/x3d+vrml
+
+
+ x3dz
+ model/x3d+xml
+
+
+ xaml
+ application/xaml+xml
+
+
+ xap
+ application/x-silverlight-app
+
+
+ xar
+ application/vnd.xara
+
+
+ xbap
+ application/x-ms-xbap
+
+
+ xbd
+ application/vnd.fujixerox.docuworks.binder
+
+
+ xbm
+ image/x-xbitmap
+
+
+ xdf
+ application/xcap-diff+xml
+
+
+ xdm
+ application/vnd.syncml.dm+xml
+
+
+ xdp
+ application/vnd.adobe.xdp+xml
+
+
+ xdssc
+ application/dssc+xml
+
+
+ xdw
+ application/vnd.fujixerox.docuworks
+
+
+ xenc
+ application/xenc+xml
+
+
+ xer
+ application/patch-ops-error+xml
+
+
+ xfdf
+ application/vnd.adobe.xfdf
+
+
+ xfdl
+ application/vnd.xfdl
+
+
+ xht
+ application/xhtml+xml
+
+
+ xhtml
+ application/xhtml+xml
+
+
+ xhvml
+ application/xv+xml
+
+
+ xif
+ image/vnd.xiff
+
+
+ xla
+ application/vnd.ms-excel
+
+
+ xlam
+ application/vnd.ms-excel.addin.macroenabled.12
+
+
+ xlc
+ application/vnd.ms-excel
+
+
+ xlf
+ application/x-xliff+xml
+
+
+ xlm
+ application/vnd.ms-excel
+
+
+ xls
+ application/vnd.ms-excel
+
+
+ xlsb
+ application/vnd.ms-excel.sheet.binary.macroenabled.12
+
+
+ xlsm
+ application/vnd.ms-excel.sheet.macroenabled.12
+
+
+ xlsx
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
+
+
+ xlt
+ application/vnd.ms-excel
+
+
+ xltm
+ application/vnd.ms-excel.template.macroenabled.12
+
+
+ xltx
+ application/vnd.openxmlformats-officedocument.spreadsheetml.template
+
+
+ xlw
+ application/vnd.ms-excel
+
+
+ xm
+ audio/xm
+
+
+ xml
+ application/xml
+
+
+ xo
+ application/vnd.olpc-sugar
+
+
+ xop
+ application/xop+xml
+
+
+ xpi
+ application/x-xpinstall
+
+
+ xpl
+ application/xproc+xml
+
+
+ xpm
+ image/x-xpixmap
+
+
+ xpr
+ application/vnd.is-xpr
+
+
+ xps
+ application/vnd.ms-xpsdocument
+
+
+ xpw
+ application/vnd.intercon.formnet
+
+
+ xpx
+ application/vnd.intercon.formnet
+
+
+ xsl
+ application/xml
+
+
+ xslt
+ application/xslt+xml
+
+
+ xsm
+ application/vnd.syncml+xml
+
+
+ xspf
+ application/xspf+xml
+
+
+ xul
+ application/vnd.mozilla.xul+xml
+
+
+ xvm
+ application/xv+xml
+
+
+ xvml
+ application/xv+xml
+
+
+ xwd
+ image/x-xwindowdump
+
+
+ xyz
+ chemical/x-xyz
+
+
+ xz
+ application/x-xz
+
+
+ yang
+ application/yang
+
+
+ yin
+ application/yin+xml
+
+
+ z
+ application/x-compress
+
+
+ Z
+ application/x-compress
+
+
+ z1
+ application/x-zmachine
+
+
+ z2
+ application/x-zmachine
+
+
+ z3
+ application/x-zmachine
+
+
+ z4
+ application/x-zmachine
+
+
+ z5
+ application/x-zmachine
+
+
+ z6
+ application/x-zmachine
+
+
+ z7
+ application/x-zmachine
+
+
+ z8
+ application/x-zmachine
+
+
+ zaz
+ application/vnd.zzazz.deck+xml
+
+
+ zip
+ application/zip
+
+
+ zir
+ application/vnd.zul
+
+
+ zirz
+ application/vnd.zul
+
+
+ zmm
+ application/vnd.handheld-entertainment+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ index.html
+ index.htm
+ index.jsp
+
+
+
diff --git a/tomcat9-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm b/tomcat9-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm
new file mode 100644
index 00000000..01f7d5d0
--- /dev/null
+++ b/tomcat9-maven-plugin/src/site/apt/adjust-embedded-tomcat-version.apt.vm
@@ -0,0 +1,162 @@
+ ---
+ Adjust Tomcat Version
+ ---
+ Olivier Lamy
+ ---
+ 2012-04-11
+ ---
+
+ ~~ 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.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Adjust Tomcat Version
+
+ In case you need to adjust the version of the embedded tomcat to run you can do so by changing
+ the runtime-dependencies:
+
++--
+
+ [...]
+
+ 9.0.0
+ [...]
+
+ [...]
+
+
+
+
+ org.apache.tomcat.maven
+ tomcat9-maven-plugin
+ ${project.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${tomcat.version}
+
+
+ org.apache.tomcat
+ tomcat-util
+ ${tomcat.version}
+
+
+ org.apache.tomcat
+ tomcat-coyote
+ ${tomcat.version}
+
+
+ org.apache.tomcat
+ tomcat-api
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-dbcp
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-servlet-api
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-jsp-api
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper-el
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-el-api
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-tribes
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina-ha
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-annotations-api
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat
+ tomcat-juli
+ ${tomcat.version}
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-logging-juli
+ ${tomcat.version}
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-logging-log4j
+ ${tomcat.version}
+
+
+
+ [...]
+
+
+ [...]
+
+ [...]
+
++--
diff --git a/tomcat9-maven-plugin/src/site/apt/index.apt b/tomcat9-maven-plugin/src/site/apt/index.apt
new file mode 100644
index 00000000..476f1b67
--- /dev/null
+++ b/tomcat9-maven-plugin/src/site/apt/index.apt
@@ -0,0 +1,39 @@
+ ---
+ Introduction
+ ---
+ Olivier Lamy
+ ---
+ 2012-02-03
+ ---
+
+ ~~ 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.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Tomcat Maven Plugin
+
+ The Tomcat9 Maven Plugin provides goals to manipulate WAR projects within the {{{http://tomcat.apache.org/}Tomcat}} servlet container version 9.x
+
+* Goals Overview
+
+ The goals for this plugin are available here {{{./plugin-info.html}goals page}}.
+
+* Usage
+
+ Instructions on how to use the Tomcat Maven Plugin can be found on the {{{./usage.html}usage page}}.
diff --git a/tomcat9-maven-plugin/src/site/apt/usage.apt.vm b/tomcat9-maven-plugin/src/site/apt/usage.apt.vm
new file mode 100644
index 00000000..89201bde
--- /dev/null
+++ b/tomcat9-maven-plugin/src/site/apt/usage.apt.vm
@@ -0,0 +1,182 @@
+ ---
+ Usage
+ ---
+ Olivier Lamy
+ ---
+ 2012-02-03
+ ---
+
+ ~~ 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.
+
+ ~~ NOTE: For help with the syntax of this file, see:
+ ~~ http://maven.apache.org/doxia/references/apt-format.html
+
+Usage
+
+ If no explicit configuration is provided then the Tomcat9 Maven Plugin defaults to
+ the following:
+
+ * Tomcat manager URL of <<>>
+
+ * Authentication details of username <<>> and no password
+
+ * Context path of <<$\{project.artifactId\}>>>
+
+ []
+
+ These can be overridden as described below.
+
+* Using a different Tomcat manager URL
+
+ To configure the plugin for a different Tomcat instance, add a plugin
+ configuration block to your <<>> as follows:
+
++--
+
+ ...
+
+ ...
+
+ ...
+
+ org.apache.tomcat.maven
+ tomcat9-maven-plugin
+ ${project.version}
+
+ http://www.mydomain.com:1234/mymanager
+
+
+ ...
+
+ ...
+
+ ...
+
++--
+
+ The default Tomcat manager URL is <<>>.
+
+* Using different Tomcat manager authentication details
+
+ To specify a different username and password to use when authenticating with
+ Tomcat manager:
+
+ [[1]] Add a plugin configuration block to your <<>>:
+
++--
+
+ ...
+
+ ...
+
+ ...
+
+ org.apache.tomcat.maven
+ tomcat9-maven-plugin
+ ${project.version}
+
+ myserver
+
+
+ ...
+
+ ...
+
+ ...
+
++--
+
+ [[2]] Add a corresponding <<>> block to your <<>>:
+
++--
+
+ ...
+
+ ...
+
+ myserver
+ myusername
+ mypassword
+
+ ...
+
+ ...
+
++--
+
+ []
+
+ The default authentication details are username <<>> and no password.
+
+* Using a different context path
+
+ The default context path is <<$\{project.artifactId\}>>>.
+
+ To change the context path to <<>> configure the plugin like this:
+
++--
+
+ ...
+
+ ...
+
+ ...
+
+ org.apache.tomcat.maven
+ tomcat9-maven-plugin
+ ${project.version}
+
+ /mycontext
+
+
+ ...
+
+ ...
+
+ ...
+
++--
+
+ If you are using <<>> to change the name of your WAR
+ file, you can use that value when you configure the context path as well. To do
+ that add the following configuration block to your <<>>:
+
++--
+
+ ...
+
+ ...
+ mycontext
+ ...
+
+ ...
+
+ org.apache.tomcat.maven
+ tomcat9-maven-plugin
+ ${project.version}
+
+ /\${project.build.finalName}
+
+
+ ...
+
+ ...
+
+ ...
+
++--
diff --git a/tomcat9-maven-plugin/src/site/site.xml b/tomcat9-maven-plugin/src/site/site.xml
new file mode 100644
index 00000000..257830fb
--- /dev/null
+++ b/tomcat9-maven-plugin/src/site/site.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarOnlyProjectIT.java b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarOnlyProjectIT.java
new file mode 100644
index 00000000..ddb6d9ce
--- /dev/null
+++ b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarOnlyProjectIT.java
@@ -0,0 +1,31 @@
+package org.apache.tomcat.maven.it;
+/*
+ * 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.
+ */
+
+import org.apache.tomcat.maven.it.AbstractDeployWarOnlyProjectIT;
+
+/**
+ * @author Olivier Lamy
+ */
+public class Tomcat9DeployWarOnlyProjectIT
+ extends AbstractDeployWarOnlyProjectIT
+{
+ // no op
+
+}
diff --git a/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarProjectIT.java b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarProjectIT.java
new file mode 100644
index 00000000..cc20ce32
--- /dev/null
+++ b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9DeployWarProjectIT.java
@@ -0,0 +1,30 @@
+package org.apache.tomcat.maven.it;
+/*
+ * 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.
+ */
+
+import org.apache.tomcat.maven.it.AbstractDeployWarProjectIT;
+
+/**
+ * @author Olivier Lamy
+ */
+public class Tomcat9DeployWarProjectIT
+ extends AbstractDeployWarProjectIT
+{
+ // no op
+}
diff --git a/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9RunMultiConfigIT.java b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9RunMultiConfigIT.java
new file mode 100644
index 00000000..3ea09075
--- /dev/null
+++ b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9RunMultiConfigIT.java
@@ -0,0 +1,37 @@
+package org.apache.tomcat.maven.it;
+/*
+ * 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.
+ */
+
+import org.apache.maven.it.VerificationException;
+
+/**
+ * @author Olivier Lamy
+ */
+public class Tomcat9RunMultiConfigIT
+ extends AbstractTomcatRunMultiConfigIT
+{
+ @Override
+ protected void verifyConnectorsStarted()
+ throws VerificationException
+ {
+ verifier.verifyTextInLog("INFO: Starting ProtocolHandler [\"http-nio-" + getHttpItPort() + "\"]");
+ verifier.verifyTextInLog("INFO: Starting ProtocolHandler [\"http-nio-" + getHttpsItPort() + "\"]");
+ verifier.verifyTextInLog("INFO: Starting ProtocolHandler [\"ajp-bio-" + getAjpItPort() + "\"]");
+ }
+}
diff --git a/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9SimpleWarProjectIT.java b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9SimpleWarProjectIT.java
new file mode 100644
index 00000000..7efab2ca
--- /dev/null
+++ b/tomcat9-maven-plugin/src/test/java/org/apache/tomcat/maven/it/Tomcat9SimpleWarProjectIT.java
@@ -0,0 +1,43 @@
+package org.apache.tomcat.maven.it;
+/*
+ * 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.
+ */
+
+import org.apache.maven.it.VerificationException;
+
+/**
+ * @author Olivier Lamy
+ */
+public class Tomcat9SimpleWarProjectIT
+ extends AbstractSimpleWarProjectIT
+{
+ @Override
+ protected int getTimeout()
+ {
+ return 20000;
+ }
+
+ @Override
+ protected void verifyConnectorsStarted()
+ throws VerificationException
+ {
+ verifier.verifyTextInLog("INFO: Starting ProtocolHandler [\"http-nio-" + getHttpItPort() + "\"]");
+
+ verifier.verifyTextInLog( "INFO: Starting ProtocolHandler [\"ajp-bio-"+ getAjpItPort() +"\"]" );
+ }
+}
diff --git a/tomcat9-maven-plugin/src/test/keystore b/tomcat9-maven-plugin/src/test/keystore
new file mode 100644
index 00000000..016d5964
Binary files /dev/null and b/tomcat9-maven-plugin/src/test/keystore differ
diff --git a/tomcat9-maven-plugin/src/test/manager.war b/tomcat9-maven-plugin/src/test/manager.war
new file mode 100644
index 00000000..065cd9d8
Binary files /dev/null and b/tomcat9-maven-plugin/src/test/manager.war differ
diff --git a/tomcat9-maven-plugin/src/test/resources/log4j2-test.xml b/tomcat9-maven-plugin/src/test/resources/log4j2-test.xml
new file mode 100644
index 00000000..eef73d8a
--- /dev/null
+++ b/tomcat9-maven-plugin/src/test/resources/log4j2-test.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tomcat9-maven-plugin/src/test/simple-war-project-1.0-SNAPSHOT.war b/tomcat9-maven-plugin/src/test/simple-war-project-1.0-SNAPSHOT.war
new file mode 100644
index 00000000..b0315c91
Binary files /dev/null and b/tomcat9-maven-plugin/src/test/simple-war-project-1.0-SNAPSHOT.war differ
diff --git a/tomcat9-war-runner/NOTES.TXT b/tomcat9-war-runner/NOTES.TXT
new file mode 100644
index 00000000..20eeb881
--- /dev/null
+++ b/tomcat9-war-runner/NOTES.TXT
@@ -0,0 +1,39 @@
+ ~ 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.
+
+The generated standalone jar will contains:
+* wars in the root: foo.war
+* tomcat classes in jar
+* file tomcat.standalone.properties with possible values :
+ * useServerXml=true/false to use directly the one provided
+ in the maven plugin configuration
+ * wars=foo.war|contextpath;bar.war ( |contextpath is optionnal if empty use the war name)
+ * enableNaming=true/false
+ * accessLogValveFormat=
+ * connectorhttpProtocol: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
+
+* optionnal: conf/ with usual tomcat configuration files
+
+On start:
+1. create a .extract directory with all the tomcat configuration
+2. check if -serverXml is set: if yes use it
+ 2.1. check in tomcat.standalone.properties if useServerXml=true
+ if use extract it to .extract/conf and use it with
+ other files located in /conf (except if .extract already here )
+ 2.2. if not use data from cli: -httpPort, -httpsPort, -ajpPort
+3. now extract wars to .extract/webapps/ (except if .extract already here )
+4. start Tomcat embded with necessary configuration and add webapps.
diff --git a/tomcat9-war-runner/pom.xml b/tomcat9-war-runner/pom.xml
new file mode 100644
index 00000000..9bb24073
--- /dev/null
+++ b/tomcat9-war-runner/pom.xml
@@ -0,0 +1,137 @@
+
+
+
+
+ 4.0.0
+
+ tomcat-maven-plugin
+ org.apache.tomcat.maven
+ 3.0-SNAPSHOT
+
+ tomcat9-war-runner
+ 3.0-SNAPSHOT
+ Apache Tomcat Maven Plugin :: Tomcat 9.x War Runner
+
+
+ 9.0.96
+
+
+
+
+ commons-cli
+ commons-cli
+
+
+
+
+ org.eclipse.jdt.core.compiler
+ ecj
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-util
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-coyote
+ ${tomcat9Version}
+
+
+ org.apache.tomcat
+ tomcat-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jdbc
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-dbcp
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-servlet-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jsp-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-jasper-el
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-el-api
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-tribes
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-catalina-ha
+ ${tomcat9Version}
+
+
+
+ org.apache.tomcat
+ tomcat-annotations-api
+ ${tomcat9Version}
+
+
+
+
+
diff --git a/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/PasswordUtil.java b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/PasswordUtil.java
new file mode 100644
index 00000000..15d8af0a
--- /dev/null
+++ b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/PasswordUtil.java
@@ -0,0 +1,131 @@
+package org.apache.tomcat.maven.runner;
+
+/*
+ * 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.
+ */
+
+import java.util.Properties;
+
+
+/**
+ * Password obfuscate utility class. Lifted from Jetty org.mortbay.jetty.security.Password
+ *
+ *
+ * Passwords that begin with OBF: are de obfuscated.
+ *
+ * Passwords can be obfuscated by running Obfuscate as a main class. Obfuscated password are required if a system needs
+ * to recover the full password (eg. so that it may be passed to another system).
+ *
+ * They are not secure, but prevent casual observation.
+ *
+ * @see Jetty Source org.mortbay.jetty.security.Password
+ * @since 2.0
+ */
+public class PasswordUtil
+{
+ public static final String __OBFUSCATE = "OBF:";
+
+ /* ------------------------------------------------------------ */
+ public static String obfuscate( String s )
+ {
+ StringBuilder buf = new StringBuilder();
+ byte[] b = s.getBytes();
+
+ buf.append( __OBFUSCATE );
+ for ( int i = 0; i < b.length; i++ )
+ {
+ byte b1 = b[i];
+ byte b2 = b[s.length() - ( i + 1 )];
+ int i1 = 127 + b1 + b2;
+ int i2 = 127 + b1 - b2;
+ int i0 = i1 * 256 + i2;
+ String x = Integer.toString( i0, 36 );
+
+ switch ( x.length() )
+ {
+ case 1:
+ buf.append( '0' );
+ case 2:
+ buf.append( '0' );
+ case 3:
+ buf.append( '0' );
+ default:
+ buf.append( x );
+ }
+ }
+ return buf.toString();
+
+ }
+
+ /* ------------------------------------------------------------ */
+ public static String deobfuscate( String s )
+ {
+ if ( s.startsWith( __OBFUSCATE ) )
+ {
+ s = s.substring( __OBFUSCATE.length() );
+
+ byte[] b = new byte[s.length() / 2];
+ int l = 0;
+ for ( int i = 0; i < s.length(); i += 4 )
+ {
+ String x = s.substring( i, i + 4 );
+ int i0 = Integer.parseInt( x, 36 );
+ int i1 = ( i0 / 256 );
+ int i2 = ( i0 % 256 );
+ b[l++] = (byte) ( ( i1 + i2 - 254 ) / 2 );
+ }
+ return new String( b, 0, l );
+ }
+ else
+ {
+ return s;
+ }
+
+ }
+
+ public static void deobfuscateSystemProps()
+ {
+ Properties props = System.getProperties();
+ for ( Object obj : props.keySet() )
+ {
+ if ( obj instanceof String )
+ {
+ String key = (String) obj;
+ String value = (String) props.getProperty( key );
+ if ( value != null && value.startsWith( __OBFUSCATE ) )
+ {
+ System.setProperty( key, deobfuscate( value ) );
+ }
+ }
+ }
+ }
+
+ public static void main( String[] args )
+ {
+ if ( args[0].startsWith( __OBFUSCATE ) )
+ {
+ System.out.println( PasswordUtil.deobfuscate( args[1] ) );
+ }
+ else
+ {
+ System.out.println( PasswordUtil.obfuscate( args[1] ) );
+ }
+ }
+}
diff --git a/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9Runner.java b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9Runner.java
new file mode 100644
index 00000000..293ce0d3
--- /dev/null
+++ b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9Runner.java
@@ -0,0 +1,825 @@
+package org.apache.tomcat.maven.runner;
+/*
+ * 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.
+ */
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Host;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.startup.Catalina;
+import org.apache.catalina.startup.ContextConfig;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.valves.AccessLogValve;
+import org.apache.juli.ClassLoaderLogManager;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.http.fileupload.FileUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.logging.LogManager;
+
+/**
+ * FIXME add junit for that but when https://issues.apache.org/bugzilla/show_bug.cgi?id=52028 fixed
+ * Main class used to run the standalone wars in a Apache Tomcat instance.
+ *
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+public class Tomcat9Runner
+{
+ // true/false to use the server.xml located in the jar /conf/server.xml
+ public static final String USE_SERVER_XML_KEY = "useServerXml";
+
+ // contains war name wars=foo.war,bar.war
+ public static final String WARS_KEY = "wars";
+
+ public static final String ARCHIVE_GENERATION_TIMESTAMP_KEY = "generationTimestamp";
+
+ public static final String ENABLE_NAMING_KEY = "enableNaming";
+
+ public static final String ACCESS_LOG_VALVE_FORMAT_KEY = "accessLogValveFormat";
+
+ public static final String CODE_SOURCE_CONTEXT_PATH = "codeSourceContextPath";
+
+ /**
+ * key of the property which contains http protocol : HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
+ */
+ public static final String HTTP_PROTOCOL_KEY = "connectorhttpProtocol";
+
+ /**
+ * key for default http port defined in the plugin
+ */
+ public static final String HTTP_PORT_KEY = "httpPort";
+
+
+ public int httpPort;
+
+ public int httpsPort;
+
+ public int maxPostSize = 2097152;
+
+ public int ajpPort;
+
+ public String serverXmlPath;
+
+ public Properties runtimeProperties;
+
+ public boolean resetExtract;
+
+ public boolean debug = false;
+
+ public String clientAuth = "false";
+
+ public String keyAlias = null;
+
+ public String httpProtocol;
+
+ public String extractDirectory = ".extract";
+
+ public File extractDirectoryFile;
+
+ public String codeSourceContextPath = null;
+
+ public File codeSourceWar = null;
+
+ public String loggerName;
+
+ Catalina container;
+
+ Tomcat tomcat;
+
+ String uriEncoding = "ISO-8859-1";
+
+ /**
+ * key = context of the webapp, value = war path on file system
+ */
+ Map webappWarPerContext = new HashMap();
+
+ public Tomcat9Runner()
+ {
+ // no op
+ }
+
+ public void run()
+ throws Exception
+ {
+
+ PasswordUtil.deobfuscateSystemProps();
+
+ if ( loggerName != null && loggerName.length() > 0 )
+ {
+ installLogger( loggerName );
+ }
+
+ this.extractDirectoryFile = new File( this.extractDirectory );
+
+ debugMessage( "use extractDirectory:" + extractDirectoryFile.getPath() );
+
+ boolean archiveTimestampChanged = false;
+
+ // compare timestamp stored during previous run if exists
+ File timestampFile = new File( extractDirectoryFile, ".tomcat_executable_archive.timestamp" );
+
+ Properties timestampProps = loadProperties( timestampFile );
+
+ if ( timestampFile.exists() )
+ {
+ String timestampValue = timestampProps.getProperty(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY );
+ if ( timestampValue != null )
+ {
+ long timestamp = Long.parseLong( timestampValue );
+ archiveTimestampChanged =
+ Long.parseLong(runtimeProperties.getProperty(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY ) )
+ > timestamp;
+
+ debugMessage( "read timestamp from file " + timestampValue + ", archiveTimestampChanged: "
+ + archiveTimestampChanged );
+ }
+
+ }
+
+ codeSourceContextPath = runtimeProperties.getProperty( CODE_SOURCE_CONTEXT_PATH );
+ if ( codeSourceContextPath != null && !codeSourceContextPath.isEmpty() )
+ {
+ codeSourceWar = AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public File run()
+ {
+ try
+ {
+ File src =
+ new File( Tomcat9Runner.class.getProtectionDomain().getCodeSource().getLocation().toURI() );
+ if ( src.getName().endsWith( ".war" ) )
+ {
+ return src;
+ }
+ else
+ {
+ debugMessage( "ERROR: Code source is not a war file, ignoring." );
+ }
+ }
+ catch ( URISyntaxException e )
+ {
+ debugMessage( "ERROR: Could not find code source. " + e.getMessage() );
+
+ }
+ return null;
+ }
+ } );
+ }
+
+ // do we have to extract content
+ {
+ if ( !extractDirectoryFile.exists() || resetExtract || archiveTimestampChanged )
+ {
+ extract();
+ //if archiveTimestampChanged or timestamp file not exists store the last timestamp from the archive
+ if ( archiveTimestampChanged || !timestampFile.exists() )
+ {
+ timestampProps.put(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY, runtimeProperties.getProperty(Tomcat9Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY ) );
+ saveProperties( timestampProps, timestampFile );
+ }
+ }
+ else
+ {
+ String wars = runtimeProperties.getProperty( WARS_KEY );
+ populateWebAppWarPerContext( wars );
+ }
+ }
+
+ // create tomcat various paths
+ new File( extractDirectory, "conf" ).mkdirs();
+ new File( extractDirectory, "logs" ).mkdirs();
+ new File( extractDirectory, "webapps" ).mkdirs();
+ new File( extractDirectory, "work" ).mkdirs();
+ File tmpDir = new File( extractDirectory, "temp" );
+ tmpDir.mkdirs();
+
+ System.setProperty( "java.io.tmpdir", tmpDir.getAbsolutePath() );
+
+ System.setProperty( "catalina.base", extractDirectoryFile.getAbsolutePath() );
+ System.setProperty( "catalina.home", extractDirectoryFile.getAbsolutePath() );
+
+ // start with a server.xml
+ if ( serverXmlPath != null || useServerXml() )
+ {
+ container = new Catalina();
+ container.setUseNaming( this.enableNaming() );
+ if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
+ {
+ container.setConfigFile( serverXmlPath );
+ }
+ else
+ {
+ container.setConfigFile( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
+ }
+ container.start();
+ }
+ else
+ {
+ tomcat = new Tomcat()
+ {
+ public Context addWebapp( Host host, String url, String name, String path )
+ {
+
+ Context ctx = new StandardContext();
+ ctx.setName( name );
+ ctx.setPath( url );
+ ctx.setDocBase( path );
+
+ ContextConfig ctxCfg = new ContextConfig();
+ ctx.addLifecycleListener( ctxCfg );
+
+ ctxCfg.setDefaultWebXml( new File( extractDirectory, "conf/web.xml" ).getAbsolutePath() );
+
+ if ( host == null )
+ {
+ getHost().addChild( ctx );
+ }
+ else
+ {
+ host.addChild( ctx );
+ }
+
+ return ctx;
+ }
+ };
+
+ if ( this.enableNaming() )
+ {
+ System.setProperty( "catalina.useNaming", "true" );
+ tomcat.enableNaming();
+ }
+
+ tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );
+
+ String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );
+
+ if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
+ {
+ connectorHttpProtocol = httpProtocol;
+ }
+
+ debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );
+
+ if ( httpPort > 0 )
+ {
+ Connector connector = new Connector( connectorHttpProtocol );
+ connector.setPort( httpPort );
+ connector.setMaxPostSize( maxPostSize );
+
+ if ( httpsPort > 0 )
+ {
+ connector.setRedirectPort( httpsPort );
+ }
+ connector.setURIEncoding( uriEncoding );
+
+ tomcat.getService().addConnector( connector );
+
+ tomcat.setConnector( connector );
+ }
+
+ // add a default acces log valve
+ AccessLogValve alv = new AccessLogValve();
+ alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
+ alv.setPattern(runtimeProperties.getProperty(Tomcat9Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
+ tomcat.getHost().getPipeline().addValve( alv );
+
+ // create https connector
+ if ( httpsPort > 0 )
+ {
+ Connector httpsConnector = new Connector( connectorHttpProtocol );
+ httpsConnector.setPort( httpsPort );
+ httpsConnector.setMaxPostSize( maxPostSize );
+ httpsConnector.setSecure( true );
+ httpsConnector.setProperty( "SSLEnabled", "true" );
+ httpsConnector.setProperty( "sslProtocol", "TLS" );
+ httpsConnector.setURIEncoding( uriEncoding );
+
+ String keystoreFile = System.getProperty( "javax.net.ssl.keyStore" );
+ String keystorePass = System.getProperty( "javax.net.ssl.keyStorePassword" );
+ String keystoreType = System.getProperty( "javax.net.ssl.keyStoreType", "jks" );
+
+ if ( keystoreFile != null )
+ {
+ httpsConnector.setAttribute( "keystoreFile", keystoreFile );
+ }
+ if ( keystorePass != null )
+ {
+ httpsConnector.setAttribute( "keystorePass", keystorePass );
+ }
+ httpsConnector.setAttribute( "keystoreType", keystoreType );
+
+ String truststoreFile = System.getProperty( "javax.net.ssl.trustStore" );
+ String truststorePass = System.getProperty( "javax.net.ssl.trustStorePassword" );
+ String truststoreType = System.getProperty( "javax.net.ssl.trustStoreType", "jks" );
+ if ( truststoreFile != null )
+ {
+ httpsConnector.setAttribute( "truststoreFile", truststoreFile );
+ }
+ if ( truststorePass != null )
+ {
+ httpsConnector.setAttribute( "truststorePass", truststorePass );
+ }
+ httpsConnector.setAttribute( "truststoreType", truststoreType );
+
+ httpsConnector.setAttribute( "clientAuth", clientAuth );
+ httpsConnector.setAttribute( "keyAlias", keyAlias );
+
+ tomcat.getService().addConnector( httpsConnector );
+
+ if ( httpPort <= 0 )
+ {
+ tomcat.setConnector( httpsConnector );
+ }
+ }
+
+ // create ajp connector
+ if ( ajpPort > 0 )
+ {
+ Connector ajpConnector = new Connector( "org.apache.coyote.ajp.AjpProtocol" );
+ ajpConnector.setPort( ajpPort );
+ ajpConnector.setURIEncoding( uriEncoding );
+ tomcat.getService().addConnector( ajpConnector );
+ }
+
+ // add webapps
+ for ( Map.Entry entry : this.webappWarPerContext.entrySet() )
+ {
+ String baseDir = null;
+ Context context = null;
+ if ( entry.getKey().equals( "/" ) )
+ {
+ baseDir = new File( extractDirectory, "webapps/ROOT.war" ).getAbsolutePath();
+ context = tomcat.addWebapp( "", baseDir );
+ }
+ else
+ {
+ baseDir = new File( extractDirectory, "webapps/" + entry.getValue() ).getAbsolutePath();
+ context = tomcat.addWebapp( entry.getKey(), baseDir );
+ }
+
+ URL contextFileUrl = getContextXml( baseDir );
+ if ( contextFileUrl != null )
+ {
+ context.setConfigFile( contextFileUrl );
+ }
+ }
+
+ if ( codeSourceWar != null )
+ {
+ String baseDir = new File( extractDirectory, "webapps/" + codeSourceWar.getName() ).getAbsolutePath();
+ Context context = tomcat.addWebapp( codeSourceContextPath, baseDir );
+ URL contextFileUrl = getContextXml( baseDir );
+ if ( contextFileUrl != null )
+ {
+ context.setConfigFile( contextFileUrl );
+ }
+ }
+
+ tomcat.start();
+
+ Runtime.getRuntime().addShutdownHook( new TomcatShutdownHook() );
+
+ }
+
+ waitIndefinitely();
+
+ }
+
+ protected class TomcatShutdownHook
+ extends Thread
+ {
+
+ protected TomcatShutdownHook()
+ {
+ // no op
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ Tomcat9Runner.this.stop();
+ }
+ catch ( Throwable ex )
+ {
+ ExceptionUtils.handleThrowable( ex );
+ System.out.println( "fail to properly shutdown Tomcat:" + ex.getMessage() );
+ }
+ finally
+ {
+ // If JULI is used, shut JULI down *after* the server shuts down
+ // so log messages aren't lost
+ LogManager logManager = LogManager.getLogManager();
+ if ( logManager instanceof ClassLoaderLogManager )
+ {
+ ( (ClassLoaderLogManager) logManager ).shutdown();
+ }
+ }
+ }
+ }
+
+ private URL getContextXml( String warPath )
+ throws IOException
+ {
+ InputStream inputStream = null;
+ try
+ {
+ String urlStr = "jar:file:" + warPath + "!/META-INF/context.xml";
+ debugMessage( "search context.xml in url:'" + urlStr + "'" );
+ URL url = new URL( urlStr );
+ inputStream = url.openConnection().getInputStream();
+ if ( inputStream != null )
+ {
+ return url;
+ }
+ }
+ catch ( FileNotFoundException e )
+ {
+ return null;
+ }
+ finally
+ {
+ closeQuietly( inputStream );
+ }
+ return null;
+ }
+
+ private static void closeQuietly( InputStream inputStream )
+ {
+ if ( inputStream == null )
+ {
+ return;
+ }
+ try
+ {
+ inputStream.close();
+ }
+ catch ( IOException e )
+ {
+ // ignore exception here
+ }
+ }
+
+ private void waitIndefinitely()
+ {
+ Object lock = new Object();
+
+ synchronized ( lock )
+ {
+ try
+ {
+ lock.wait();
+ }
+ catch ( InterruptedException exception )
+ {
+ throw new Error( "InterruptedException on wait Indefinitely lock:" + exception.getMessage(),
+ exception );
+ }
+ }
+ }
+
+ public void stop()
+ throws Exception
+ {
+ if ( container != null )
+ {
+ container.stop();
+ }
+ if ( tomcat != null )
+ {
+ tomcat.stop();
+ }
+ }
+
+ protected void extract()
+ throws Exception
+ {
+
+ if ( extractDirectoryFile.exists() )
+ {
+ debugMessage( "delete extractDirectory:" + extractDirectoryFile.getAbsolutePath() );
+ FileUtils.deleteDirectory( extractDirectoryFile );
+ }
+
+ if ( !this.extractDirectoryFile.exists() )
+ {
+ boolean created = this.extractDirectoryFile.mkdirs();
+ if ( !created )
+ {
+ throw new Exception( "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() );
+ }
+ }
+
+ // ensure webapp dir is here
+ boolean created = new File( extractDirectory, "webapps" ).mkdirs();
+ if ( !created )
+ {
+ throw new Exception(
+ "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() + "/webapps" );
+
+ }
+
+ String wars = runtimeProperties.getProperty( WARS_KEY );
+ populateWebAppWarPerContext( wars );
+
+ for ( Map.Entry entry : webappWarPerContext.entrySet() )
+ {
+ debugMessage( "webappWarPerContext entry key/value: " + entry.getKey() + "/" + entry.getValue() );
+ InputStream inputStream = null;
+ try
+ {
+ File expandFile = null;
+ inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( entry.getValue() );
+ if ( !useServerXml() )
+ {
+ if ( entry.getKey().equals( "/" ) )
+ {
+ expandFile = new File( extractDirectory, "webapps/ROOT.war" );
+ }
+ else
+ {
+ expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
+ }
+ }
+ else
+ {
+ expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
+ }
+
+ debugMessage( "expand to file:" + expandFile.getPath() );
+
+ // MTOMCAT-211 ensure parent directories created
+ File parentFile = expandFile.getParentFile();
+ if ( !parentFile.mkdirs() && !parentFile.isDirectory() )
+ {
+ throw new Exception( "FATAL: impossible to create directories:" + parentFile );
+ }
+
+ expand( inputStream, expandFile );
+
+ }
+ finally
+ {
+ if ( inputStream != null )
+ {
+ inputStream.close();
+ }
+ }
+ }
+
+ //Copy code source to webapps folder
+ if ( codeSourceWar != null )
+ {
+ FileInputStream inputStream = null;
+ try
+ {
+ File expandFile = new File( extractDirectory, "webapps/" + codeSourceContextPath + ".war" );
+ inputStream = new FileInputStream( codeSourceWar );
+ debugMessage( "move code source to file:" + expandFile.getPath() );
+ expand( inputStream, expandFile );
+ }
+ finally
+ {
+ if ( inputStream != null )
+ {
+ inputStream.close();
+ }
+ }
+ }
+
+ // expand tomcat configuration files if there
+ expandConfigurationFile( "catalina.properties", extractDirectoryFile );
+ expandConfigurationFile( "logging.properties", extractDirectoryFile );
+ expandConfigurationFile( "tomcat-users.xml", extractDirectoryFile );
+ expandConfigurationFile( "catalina.policy", extractDirectoryFile );
+ expandConfigurationFile( "context.xml", extractDirectoryFile );
+ expandConfigurationFile( "server.xml", extractDirectoryFile );
+ expandConfigurationFile( "web.xml", extractDirectoryFile );
+
+ }
+
+ private static void expandConfigurationFile( String fileName, File extractDirectory )
+ throws Exception
+ {
+ InputStream inputStream = null;
+ try
+ {
+ inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( "conf/" + fileName );
+ if ( inputStream != null )
+ {
+ File confDirectory = new File( extractDirectory, "conf" );
+ if ( !confDirectory.exists() )
+ {
+ confDirectory.mkdirs();
+ }
+ expand( inputStream, new File( confDirectory, fileName ) );
+ }
+ }
+ finally
+ {
+ if ( inputStream != null )
+ {
+ inputStream.close();
+ }
+ }
+
+ }
+
+ /**
+ * @param warsValue we can value in format: wars=foo.war|contextpath;bar.war ( |contextpath is optionnal if empty use the war name)
+ * so here we return war file name and populate webappWarPerContext
+ */
+ private void populateWebAppWarPerContext( String warsValue )
+ {
+ if ( warsValue == null )
+ {
+ return;
+ }
+
+ StringTokenizer st = new StringTokenizer( warsValue, ";" );
+ while ( st.hasMoreTokens() )
+ {
+ String warValue = st.nextToken();
+ debugMessage( "populateWebAppWarPerContext warValue:" + warValue );
+ String warFileName = "";
+ String contextValue = "";
+ int separatorIndex = warValue.indexOf( "|" );
+ if ( separatorIndex >= 0 )
+ {
+ warFileName = warValue.substring( 0, separatorIndex );
+ contextValue = warValue.substring( separatorIndex + 1, warValue.length() );
+
+ }
+ else
+ {
+ warFileName = contextValue;
+ }
+ debugMessage( "populateWebAppWarPerContext contextValue/warFileName:" + contextValue + "/" + warFileName );
+ this.webappWarPerContext.put( contextValue, warFileName );
+ }
+ }
+
+
+ /**
+ * Expand the specified input stream into the specified file.
+ *
+ * @param input InputStream to be copied
+ * @param file The file to be created
+ * @throws java.io.IOException if an input/output error occurs
+ */
+ private static void expand( InputStream input, File file )
+ throws IOException
+ {
+ BufferedOutputStream output = null;
+ try
+ {
+ output = new BufferedOutputStream( new FileOutputStream( file ) );
+ byte buffer[] = new byte[2048];
+ while ( true )
+ {
+ int n = input.read( buffer );
+ if ( n <= 0 )
+ {
+ break;
+ }
+ output.write( buffer, 0, n );
+ }
+ }
+ finally
+ {
+ if ( output != null )
+ {
+ try
+ {
+ output.close();
+ }
+ catch ( IOException e )
+ {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ public boolean useServerXml()
+ {
+ return Boolean.parseBoolean( runtimeProperties.getProperty( USE_SERVER_XML_KEY, Boolean.FALSE.toString() ) );
+ }
+
+
+ public void debugMessage( String message )
+ {
+ if ( debug )
+ {
+ System.out.println( message );
+ }
+ }
+
+
+ public boolean enableNaming()
+ {
+ return Boolean.parseBoolean( runtimeProperties.getProperty( ENABLE_NAMING_KEY, Boolean.FALSE.toString() ) );
+ }
+
+ private void installLogger( String loggerName )
+ throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
+ InvocationTargetException
+ {
+ if ( "slf4j".equals( loggerName ) )
+ {
+
+ try
+ {
+ // Check class is available
+
+ //final Class> clazz = Class.forName( "org.slf4j.bridge.SLF4JBridgeHandler" );
+ final Class> clazz =
+ Thread.currentThread().getContextClassLoader().loadClass( "org.slf4j.bridge.SLF4JBridgeHandler" );
+
+ // Remove all JUL handlers
+ java.util.logging.LogManager.getLogManager().reset();
+
+ // Install slf4j bridge handler
+ final Method method = clazz.getMethod( "install", null );
+ method.invoke( null );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ System.out.println( "WARNING: issue configuring slf4j jul bridge, skip it" );
+ }
+ }
+ else
+ {
+ System.out.println( "WARNING: loggerName " + loggerName + " not supported, skip it" );
+ }
+ }
+
+ private Properties loadProperties( File file )
+ throws FileNotFoundException, IOException
+ {
+ Properties properties = new Properties();
+ if ( file.exists() )
+ {
+
+ FileInputStream fileInputStream = new FileInputStream( file );
+ try
+ {
+ properties.load( fileInputStream );
+ }
+ finally
+ {
+ fileInputStream.close();
+ }
+
+ }
+ return properties;
+ }
+
+ private void saveProperties( Properties properties, File file )
+ throws FileNotFoundException, IOException
+ {
+ FileOutputStream fileOutputStream = new FileOutputStream( file );
+ try
+ {
+ properties.store( fileOutputStream, "Timestamp file for executable war/jar" );
+ }
+ finally
+ {
+ fileOutputStream.close();
+ }
+ }
+}
diff --git a/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9RunnerCli.java b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9RunnerCli.java
new file mode 100644
index 00000000..07bcfe33
--- /dev/null
+++ b/tomcat9-war-runner/src/main/java/org/apache/tomcat/maven/runner/Tomcat9RunnerCli.java
@@ -0,0 +1,250 @@
+package org.apache.tomcat.maven.runner;
+/*
+ * 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.
+ */
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Olivier Lamy
+ * @since 2.0
+ */
+@SuppressWarnings( "static-access" )
+public class Tomcat9RunnerCli
+{
+
+ public static final String STAND_ALONE_PROPERTIES_FILENAME = "tomcat.standalone.properties";
+
+ static Option httpPort =
+ OptionBuilder.withArgName( "httpPort" ).hasArg().withDescription( "http port to use" ).create( "httpPort" );
+
+ static Option httpsPort =
+ OptionBuilder.withArgName( "httpsPort" ).hasArg().withDescription( "https port to use" ).create( "httpsPort" );
+
+ static Option maxPostSize =
+ OptionBuilder.withArgName( "maxPostSize" ).hasArg().withDescription( "max post size to use" ).create(
+ "maxPostSize" );
+
+ static Option ajpPort =
+ OptionBuilder.withArgName( "ajpPort" ).hasArg().withDescription( "ajp port to use" ).create( "ajpPort" );
+
+ static Option serverXmlPath =
+ OptionBuilder.withArgName( "serverXmlPath" ).hasArg().withDescription( "server.xml to use, optional" ).create(
+ "serverXmlPath" );
+
+ static Option resetExtract =
+ OptionBuilder.withArgName( "resetExtract" ).withDescription( "clean previous extract directory" ).create(
+ "resetExtract" );
+
+ static Option help = OptionBuilder.withLongOpt( "help" ).withDescription( "help" ).create( 'h' );
+
+ static Option debug = OptionBuilder.withLongOpt( "debug" ).withDescription( "debug" ).create( 'X' );
+
+ static Option sysProps = OptionBuilder.withDescription( "use value for given property" ).hasArgs().withDescription(
+ "key=value" ).withValueSeparator().create( 'D' );
+
+ static Option clientAuth =
+ OptionBuilder.withArgName( "clientAuth" ).withDescription( "enable client authentication for https" ).create(
+ "clientAuth" );
+
+ static Option keyAlias =
+ OptionBuilder.withArgName( "keyAlias" ).hasArgs().withDescription( "alias from keystore for ssl" ).create(
+ "keyAlias" );
+
+ static Option obfuscate =
+ OptionBuilder.withArgName( "password" ).hasArgs().withDescription( "obfuscate the password and exit" ).create(
+ "obfuscate" );
+
+ static Option httpProtocol = OptionBuilder.withArgName( "httpProtocol" ).hasArg().withDescription(
+ "http protocol to use: HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol" ).create( "httpProtocol" );
+
+ static Option extractDirectory = OptionBuilder.withArgName( "extractDirectory" ).hasArg().withDescription(
+ "path to extract war content, default value: .extract" ).create( "extractDirectory" );
+
+ static Option loggerName = OptionBuilder.withArgName( "loggerName" ).hasArg().withDescription(
+ "logger to use: slf4j to use slf4j bridge on top of jul" ).create( "loggerName" );
+
+ static Option uriEncoding = OptionBuilder.withArgName( "uriEncoding" ).hasArg().withDescription(
+ "connector uriEncoding default ISO-8859-1" ).create( "uriEncoding" );
+
+ static Options options = new Options();
+
+ static
+ {
+ options.addOption( httpPort ) //
+ .addOption( httpsPort ) //
+ .addOption( ajpPort ) //
+ .addOption( serverXmlPath ) //
+ .addOption( resetExtract ) //
+ .addOption( help ) //
+ .addOption( debug ) //
+ .addOption( sysProps ) //
+ .addOption( httpProtocol ) //
+ .addOption( clientAuth ) //
+ .addOption( keyAlias ) //
+ .addOption( obfuscate ) //
+ .addOption( extractDirectory ) //
+ .addOption( loggerName ) //
+ .addOption( uriEncoding ) //
+ .addOption( maxPostSize );
+ }
+
+
+ public static void main( String[] args )
+ throws Exception
+ {
+ CommandLineParser parser = new GnuParser();
+ CommandLine line = null;
+ try
+ {
+ line = parser.parse(Tomcat9RunnerCli.options, args );
+ }
+ catch ( ParseException e )
+ {
+ System.err.println( "Parsing failed. Reason: " + e.getMessage() );
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp(getCmdLineSyntax(), Tomcat9RunnerCli.options );
+ System.exit( 1 );
+ }
+
+ if ( line.hasOption( help.getOpt() ) )
+ {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp(getCmdLineSyntax(), Tomcat9RunnerCli.options );
+ System.exit( 0 );
+ }
+
+ if ( line.hasOption( obfuscate.getOpt() ) )
+ {
+ System.out.println( PasswordUtil.obfuscate( line.getOptionValue( obfuscate.getOpt() ) ) );
+ System.exit( 0 );
+ }
+ Tomcat9Runner tomcat9Runner = new Tomcat9Runner();
+
+ tomcat9Runner.runtimeProperties = buildStandaloneProperties();
+
+ if ( line.hasOption( serverXmlPath.getOpt() ) )
+ {
+ tomcat9Runner.serverXmlPath = line.getOptionValue( serverXmlPath.getOpt() );
+ }
+
+ String port = tomcat9Runner.runtimeProperties.getProperty( Tomcat9Runner.HTTP_PORT_KEY );
+ if ( port != null )
+ {
+ tomcat9Runner.httpPort = Integer.parseInt( port );
+ }
+
+ // cli win for the port
+ if ( line.hasOption( httpPort.getOpt() ) )
+ {
+ tomcat9Runner.httpPort = Integer.parseInt( line.getOptionValue( httpPort.getOpt() ) );
+ }
+
+ if ( line.hasOption( maxPostSize.getOpt() ) )
+ {
+ tomcat9Runner.maxPostSize = Integer.parseInt( line.getOptionValue( maxPostSize.getOpt() ) );
+ }
+
+ if ( line.hasOption( httpsPort.getOpt() ) )
+ {
+ tomcat9Runner.httpsPort = Integer.parseInt( line.getOptionValue( httpsPort.getOpt() ) );
+ }
+ if ( line.hasOption( ajpPort.getOpt() ) )
+ {
+ tomcat9Runner.ajpPort = Integer.parseInt( line.getOptionValue( ajpPort.getOpt() ) );
+ }
+ if ( line.hasOption( resetExtract.getOpt() ) )
+ {
+ tomcat9Runner.resetExtract = true;
+ }
+ if ( line.hasOption( debug.getOpt() ) )
+ {
+ tomcat9Runner.debug = true;
+ }
+
+ if ( line.hasOption( httpProtocol.getOpt() ) )
+ {
+ tomcat9Runner.httpProtocol = line.getOptionValue( httpProtocol.getOpt() );
+ }
+
+ if ( line.hasOption( sysProps.getOpt() ) )
+ {
+ Properties systemProperties = line.getOptionProperties( sysProps.getOpt() );
+ if ( systemProperties != null && !systemProperties.isEmpty() )
+ {
+ for ( Map.Entry