Java tutorial
package org.apache.tomcat.maven.plugin.tomcat7.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.naming.NamingException; 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.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.naming.NamingEntry; import org.apache.naming.resources.FileDirContext; 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.tomcat7.AbstractTomcat7Mojo; 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 AbstractTomcat7Mojo { // --------------------------------------------------------------------- // 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: * <ul> * <li>org.apache.coyote.ajp.AjpProtocol - new blocking Java connector that supports an executor</li> * <li>org.apache.coyote.ajp.AjpAprProtocol - the APR/native connector.</li> * </ul> * * @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<String, String> 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 <b>Note if you use this you must configure in this file your webapp paths</b>. * * @since 1.0-alpha-2 */ @Parameter(property = "maven.tomcat.serverXml") private File serverXml; /** * overriding the providing web.xml to run tomcat * <b>This override the global Tomcat web.xml located in $CATALINA_HOME/conf/</b> * * @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: * <pre> * <type>war</type> * <scope>tomcat</scope> * </pre> * 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 <code>true</code> 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<Artifact> 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; /** * <p> * Enables or disables naming support for the embedded Tomcat server. * </p> * <p> * <strong>Note:</strong> This setting is ignored if you provide a <code>server.xml</code> for your * Tomcat. Instead please configure naming in the <code>server.xml</code>. * </p> * * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/startup/Embedded.html">org.apache.catalina.startup.Embedded</a> * @see <a href="http://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/startup/Tomcat.html">org.apache.catalina.startup.Tomcat</a> * @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 <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/context.html">http://tomcat.apache.org/tomcat-7.0-doc/config/context.html</a> */ @Parameter(property = "maven.tomcat.backgroundProcessorDelay", defaultValue = "-1") protected int backgroundProcessorDelay = -1; /** * <p>The path of the Tomcat context XML file.</p> * <p>Since release 2.0, the file is filtered as a maven resource so you can use * interpolation tokens ${ }</p> */ @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 <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html">HTTP Connector</a> * 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.<p> * * @see {@link Webapp} * @since 2.0 */ @Parameter private List<Webapp> 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 <code>null</code> 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 <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/host.html#Host_Name_Aliases">Host Name aliases</a> * * @since 2.0 */ @Parameter protected String[] aliases; /** * enable client authentication for https (if configured) * see <a href="http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support_-_BIO_and_NIO">http://tomcat.apache.org/tomcat-7.0-doc/config/http.html#SSL_Support_-_BIO_and_NIO</a> * * @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 */ protected String getPath() { return path; } /** * 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()) { standardContext = parseContextFile(overriddenContextFile); } else if (defaultContextFile.exists()) { 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); context.setResources(new MyDirContext(new File(project.getBuild().getOutputDirectory()).getAbsolutePath())); if (useSeparateTomcatClassLoader) { context.setParentClassLoader(getTomcatClassLoader()); } 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); } } private static class MyDirContext extends FileDirContext { String buildOutputDirectory; MyDirContext(String buildOutputDirectory) { this.buildOutputDirectory = buildOutputDirectory; } @Override protected List<NamingEntry> doListBindings(String name) throws NamingException { if ("/WEB-INF/classes".equals(name)) { if (!new File(buildOutputDirectory).exists()) { return Collections.emptyList(); } FileDirContext fileDirContext = new FileDirContext(); fileDirContext.setDocBase(buildOutputDirectory); NamingEntry namingEntry = new NamingEntry("/WEB-INF/classes", fileDirContext, -1); return Collections.singletonList(namingEntry); } return super.doListBindings(name); } } /** * 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 { if (useSeparateTomcatClassLoader) { return (isContextReloadable()) ? new ExternalRepositoriesReloadableWebappLoader(getTomcatClassLoader(), getLog()) : new WebappLoader(getTomcatClassLoader()); } return (isContextReloadable()) ? new ExternalRepositoriesReloadableWebappLoader(Thread.currentThread().getContextClassLoader(), getLog()) : new WebappLoader(Thread.currentThread().getContextClassLoader()); } /** * 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 tomcat7 * 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 { // 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.setConfig(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.setDefaultRealm(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<Webapp> 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 (@SuppressWarnings("rawtypes") Iterator i = pluginArtifacts.iterator(); i.hasNext();) { Artifact pluginArtifact = (Artifact) 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<Artifact> 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<Context> createDependencyContexts(Tomcat container) throws MojoExecutionException, MalformedURLException, ServletException, IOException { getLog().info("Deploying dependency wars"); // Let's add other modules List<Context> contexts = new ArrayList<Context>(); ScopeArtifactFilter filter = new ScopeArtifactFilter("tomcat"); @SuppressWarnings("unchecked") Set<Artifact> 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<Context> 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"); 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; } }