You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2016/11/13 09:34:10 UTC
svn commit: r1769479 [2/16] - in /openwebbeans/microwave/trunk: ./
meecrowave-arquillian/ meecrowave-arquillian/src/
meecrowave-arquillian/src/main/ meecrowave-arquillian/src/main/java/
meecrowave-arquillian/src/main/java/org/ meecrowave-arquillian/src...
Added: openwebbeans/microwave/trunk/meecrowave-core/pom.xml
URL: http://svn.apache.org/viewvc/openwebbeans/microwave/trunk/meecrowave-core/pom.xml?rev=1769479&view=auto
==============================================================================
--- openwebbeans/microwave/trunk/meecrowave-core/pom.xml (added)
+++ openwebbeans/microwave/trunk/meecrowave-core/pom.xml Sun Nov 13 09:34:07 2016
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>meecrowave</artifactId>
+ <groupId>org.apache.meecrowave</groupId>
+ <version>0.0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>meecrowave-core</artifactId>
+ <name>Meecrowave :: Core</name>
+
+ <properties>
+ <tomcat.version>9.0.0.M13</tomcat.version>
+ <openwebbeans.version>1.7.1-SNAPSHOT</openwebbeans.version>
+ <cxf.version>3.1.9-SNAPSHOT</cxf.version>
+ <johnzon.version>0.9.5</johnzon.version>
+ <log4j2.version>2.7</log4j2.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.2_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jcdi_1.1_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-atinject_1.0_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-interceptor_1.2_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-json_1.0_spec</artifactId>
+ <version>1.0-alpha-1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jaxrs_2.0_spec</artifactId>
+ <version>1.0-alpha-1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>jsonb-api</artifactId>
+ <version>${johnzon.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-jaspic-api</artifactId>
+ <!--<version>${tomcat.version}</version>-->
+ <version>9.0-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.2</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.xbean</groupId>
+ <artifactId>xbean-reflect</artifactId>
+ <version>4.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-impl</artifactId>
+ <version>${openwebbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-web</artifactId>
+ <version>${openwebbeans.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-catalina</artifactId>
+ <version>${tomcat.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-annotations-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-jsp-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-el-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.annotation</groupId>
+ <artifactId>javax.annotation-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.codehaus.woodstox</groupId>
+ <artifactId>woodstox-core-asl</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.ws.xmlschema</groupId>
+ <artifactId>xmlschema-core</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-integration-cdi</artifactId>
+ <version>${cxf.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.enterprise</groupId>
+ <artifactId>cdi-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-client</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-jaxrs</artifactId>
+ <version>${johnzon.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-jsonb</artifactId>
+ <version>${johnzon.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-api</artifactId>
+ <version>${log4j2.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-core</artifactId>
+ <version>${log4j2.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-jul</artifactId>
+ <version>${log4j2.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>process-test-classes</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.deltaspike.core</groupId>
+ <artifactId>deltaspike-core-api</artifactId>
+ <version>1.7.1</version>
+ <type>jar</type>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/shared-test</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.deltaspike.core</groupId>
+ <artifactId>deltaspike-core-impl</artifactId>
+ <version>1.7.1</version>
+ <type>jar</type>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/shared-test</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.5.1</version>
+ <executions>
+ <execution>
+ <id>log4j-plugin-processor</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <phase>process-classes</phase>
+ <configuration>
+ <proc>only</proc>
+ <annotationProcessors>
+ <annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor>
+ </annotationProcessors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.4.3</version>
+ <executions>
+ <execution>
+ <id>bundle</id>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <shadedClassifierName>runner</shadedClassifierName>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <dependencyReducedPomLocation>${project.build.directory}/reduced-pom-bundle.xml</dependencyReducedPomLocation>
+ <transformers>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>org.apache.meecrowave.runner.Cli</mainClass>
+ </transformer>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>META-INF/cxf/bus-extensions.txt</resource>
+ </transformer>
+ <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+ <resource>log4j2.component.properties</resource>
+ </transformer>
+ <transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
+ <transformer implementation="org.apache.openwebbeans.maven.shade.OpenWebBeansPropertiesTransformer" />
+ </transformers>
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ <exclude>META-INF/LICENSE.txt</exclude>
+ <exclude>META-INF/LICENSE</exclude>
+ <exclude>META-INF/NOTICE.txt</exclude>
+ <exclude>META-INF/NOTICE</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <relocations>
+ <relocation>
+ <pattern>org.apache.commons</pattern>
+ <shadedPattern>org.apache.meecrowave.shaded.commons</shadedPattern>
+ </relocation>
+ </relocations>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.openwebbeans</groupId>
+ <artifactId>openwebbeans-maven</artifactId>
+ <version>1.7.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.github.edwgiz</groupId>
+ <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
+ <version>2.1</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
Added: openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/catalina/startup/MeecrowaveContextConfig.java
URL: http://svn.apache.org/viewvc/openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/catalina/startup/MeecrowaveContextConfig.java?rev=1769479&view=auto
==============================================================================
--- openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/catalina/startup/MeecrowaveContextConfig.java (added)
+++ openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/catalina/startup/MeecrowaveContextConfig.java Sun Nov 13 09:34:07 2016
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.catalina.startup;
+
+import org.apache.catalina.WebResource;
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
+import org.apache.meecrowave.openwebbeans.OWBTomcatWebScannerService;
+import org.apache.tomcat.util.descriptor.web.WebXml;
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.corespi.scanner.xbean.CdiArchive;
+import org.apache.webbeans.corespi.scanner.xbean.OwbAnnotationFinder;
+import org.xml.sax.InputSource;
+
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.annotation.HandlesTypes;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.annotation.WebListener;
+import javax.servlet.annotation.WebServlet;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static java.util.Optional.ofNullable;
+
+public class MeecrowaveContextConfig extends ContextConfig {
+ private static final byte[] DEFAULT_WEB_XML = "<web-app version=\"3.1\" />".getBytes(StandardCharsets.UTF_8);
+
+ private final Meecrowave.Builder configuration;
+ private final Map<String, Collection<Class<?>>> webClasses = new HashMap<>();
+ private OwbAnnotationFinder finder;
+
+ public MeecrowaveContextConfig(final Meecrowave.Builder configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void webConfig() {
+ if (!configuration.isTomcatScanning()) {
+ super.webConfig();
+ return;
+ }
+
+ // eagerly start CDI to scan only once and not twice (tomcat+CDI)
+ final ClassLoader loader = context.getLoader().getClassLoader(); // should already be started at that point
+ final Thread thread = Thread.currentThread();
+ final ClassLoader old = thread.getContextClassLoader();
+ thread.setContextClassLoader(loader);
+ try {
+ final OWBTomcatWebScannerService scannerService = OWBTomcatWebScannerService.class.cast(WebBeansContext.getInstance().getScannerService());
+ ofNullable(context.getJarScanner()).ifPresent(s -> scannerService.setFilter(s.getJarScanFilter()));
+ scannerService.setDocBase(context.getDocBase());
+ scannerService.setShared(configuration.getSharedLibraries());
+ scannerService.scan();
+ finder = scannerService.getFinder();
+ finder.link();
+ final CdiArchive archive = CdiArchive.class.cast(finder.getArchive());
+ Stream.of(WebServlet.class, WebFilter.class, WebListener.class)
+ .forEach(marker -> finder.findAnnotatedClasses(marker).stream()
+ .filter(c -> !Modifier.isAbstract(c.getModifiers()) && Modifier.isPublic(c.getModifiers()))
+ .forEach(webComponent -> webClasses.computeIfAbsent(
+ archive.classesByUrl().entrySet().stream()
+ .filter(e -> e.getValue().getClassNames().contains(webComponent.getName()))
+ .findFirst().get().getKey(), k -> new HashSet<>())
+ .add(webComponent)));
+ } finally {
+ thread.setContextClassLoader(old);
+ }
+ try {
+ super.webConfig();
+ } finally {
+ webClasses.clear();
+ finder = null;
+ }
+ }
+
+ @Override // just to avoid an info log pretty useless for us
+ protected InputSource getGlobalWebXmlSource() {
+ return ofNullable(super.getGlobalWebXmlSource()).orElse(new InputSource(new ByteArrayInputStream(DEFAULT_WEB_XML)));
+ }
+
+ @Override
+ protected void processAnnotationsWebResource(final WebResource webResource, final WebXml fragment,
+ final boolean handlesTypesOnly,
+ final Map<String, JavaClassCacheEntry> javaClassCache) {
+ if (configuration.isTomcatScanning()) {
+ webClasses.keySet().stream().filter(k -> k.endsWith("/WEB-INF/classes"))
+ .forEach(k -> processClasses(fragment, handlesTypesOnly, javaClassCache, k));
+ }
+ }
+
+ @Override
+ protected void processAnnotationsUrl(final URL url, final WebXml fragment, final boolean handlesTypesOnly,
+ final Map<String, JavaClassCacheEntry> javaClassCache) { // use our finder
+ if (!configuration.isTomcatScanning()) {
+ return;
+ }
+ processClasses(fragment, handlesTypesOnly, javaClassCache, url.toExternalForm());
+ }
+
+ @Override
+ protected void processServletContainerInitializers() { // use our finder
+ if (!configuration.isTomcatScanning()) {
+ return;
+ }
+
+ try {
+ new WebappServiceLoader<ServletContainerInitializer>(context).load(ServletContainerInitializer.class).forEach(sci -> {
+ final Set<Class<?>> classes = new HashSet<>();
+ initializerClassMap.put(sci, classes);
+
+ final HandlesTypes ht;
+ try {
+ ht = sci.getClass().getAnnotation(HandlesTypes.class);
+ } catch (final Exception | NoClassDefFoundError e) {
+ return;
+ }
+ if (ht == null) {
+ return;
+ }
+ Stream.of(ht.value()).forEach(t -> {
+ if (t.isAnnotation()) {
+ final Class<? extends Annotation> annotation = Class.class.cast(t);
+ finder.findAnnotatedClasses(annotation).forEach(classes::add);
+ } else if (t.isInterface()) {
+ finder.findImplementations(t).forEach(classes::add);
+ } else {
+ finder.findSubclasses(t).forEach(classes::add);
+ }
+ });
+ });
+ } catch (final IOException e) {
+ ok = false;
+ }
+ }
+
+ private void processClasses(final WebXml fragment, final boolean handlesTypesOnly,
+ final Map<String, JavaClassCacheEntry> javaClassCache, final String key) {
+ final Collection<Class<?>> classes = webClasses.remove(key);
+ if (classes != null && !classes.isEmpty()) {
+ final ClassLoader loader = context.getLoader().getClassLoader();
+ classes.forEach(c -> {
+ try (final InputStream stream = loader.getResourceAsStream(c.getName().replace('.', '/') + ".class")) {
+ super.processAnnotationsStream(stream, fragment, handlesTypesOnly, javaClassCache);
+ } catch (final IOException e) {
+ new LogFacade(MeecrowaveContextConfig.class.getName()).error("Can't parse " + c);
+ }
+ });
+ }
+ }
+}
Added: openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
URL: http://svn.apache.org/viewvc/openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java?rev=1769479&view=auto
==============================================================================
--- openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java (added)
+++ openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java Sun Nov 13 09:34:07 2016
@@ -0,0 +1,1693 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.meecrowave;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.Globals;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
+import org.apache.catalina.Server;
+import org.apache.catalina.Service;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.session.ManagerBase;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.startup.Catalina;
+import org.apache.catalina.startup.MeecrowaveContextConfig;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.commons.lang3.text.StrLookup;
+import org.apache.commons.lang3.text.StrSubstitutor;
+import org.apache.coyote.http2.Http2Protocol;
+import org.apache.meecrowave.cxf.CxfCdiAutoSetup;
+import org.apache.meecrowave.io.IO;
+import org.apache.meecrowave.logging.jul.Log4j2Logger;
+import org.apache.meecrowave.logging.openwebbeans.Log4j2LoggerFactory;
+import org.apache.meecrowave.logging.tomcat.Log4j2Log;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
+import org.apache.meecrowave.openwebbeans.OWBAutoSetup;
+import org.apache.meecrowave.runner.cli.CliOption;
+import org.apache.meecrowave.tomcat.CDIInstanceManager;
+import org.apache.meecrowave.tomcat.OWBJarScanner;
+import org.apache.meecrowave.tomcat.ProvidedLoader;
+import org.apache.meecrowave.tomcat.TomcatAutoInitializer;
+import org.apache.tomcat.JarScanFilter;
+import org.apache.tomcat.util.descriptor.web.LoginConfig;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.xbean.finder.ResourceFinder;
+import org.apache.xbean.recipe.ObjectRecipe;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+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.io.OutputStream;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptySet;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+
+public class Meecrowave implements AutoCloseable {
+ private final Builder configuration;
+ protected File base;
+ protected final File ownedTempDir;
+ protected InternalTomcat tomcat;
+ protected volatile Thread hook;
+
+ // we can undeploy webapps with that later
+ private final Map<String, Runnable> contexts = new HashMap<>();
+ private Runnable postTask;
+ private boolean clearCatalinaSystemProperties;
+
+ public Meecrowave() {
+ this(new Builder());
+ }
+
+ public Meecrowave(final Builder builder) {
+ this.configuration = builder;
+ this.ownedTempDir = new File(configuration.tempDir, "meecrowave_" + System.nanoTime());
+ }
+
+ public Builder getConfiguration() {
+ return configuration;
+ }
+
+ public File getBase() {
+ return base;
+ }
+
+ public Tomcat getTomcat() {
+ return tomcat;
+ }
+
+ public void undeploy(final String root) {
+ ofNullable(this.contexts.remove(root)).ifPresent(Runnable::run);
+ }
+
+ public Meecrowave deployClasspath(final DeploymentMeta meta) {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ final ClassLoader parentLoader = tomcat.getServer().getParentClassLoader();
+ if (parentLoader.getParent() == classLoader) {
+ classLoader = parentLoader;
+ }
+
+ final ProvidedLoader loader = new ProvidedLoader(classLoader, configuration.isTomcatWrapLoader());
+ final Consumer<Context> builtInCustomizer = c -> c.setLoader(loader);
+ return deployWebapp(new DeploymentMeta(meta.context, meta.docBase, ofNullable(meta.consumer).map(c -> (Consumer<Context>) ctx -> {
+ builtInCustomizer.accept(ctx);
+ c.accept(ctx);
+ }).orElse(builtInCustomizer)));
+ }
+
+ // shortcut
+ public Meecrowave deployClasspath() {
+ return deployClasspath("");
+ }
+
+ // shortcut (used by plugins)
+ public Meecrowave deployClasspath(final String context) {
+ return deployClasspath(new DeploymentMeta(context, null, null));
+ }
+
+ // shortcut
+ public Meecrowave deployWebapp(final File warOrDir) {
+ return deployWebapp("", warOrDir);
+ }
+
+ // shortcut (used by plugins)
+ public Meecrowave deployWebapp(final String context, final File warOrDir) {
+ return deployWebapp(new DeploymentMeta(context, warOrDir, null));
+ }
+
+ public Meecrowave deployWebapp(final DeploymentMeta meta) {
+ if (contexts.containsKey(meta.context)) {
+ throw new IllegalArgumentException("Already deployed: '" + meta.context + "'");
+ }
+ // always nice to see the deployment with something else than internals
+ new LogFacade(Meecrowave.class.getName())
+ .info("--------------- " + configuration.getActiveProtocol() + "://"
+ + tomcat.getHost().getName() + ':' + configuration.getActivePort() + meta.context);
+
+
+ final File dir = ofNullable(meta.docBase).orElseGet(() -> {
+ final File d = new File(ownedTempDir, "classpath/fake-" + meta.context.replace("/", ""));
+ IO.mkdirs(d);
+ return d;
+ });
+
+ final StandardContext ctx = new StandardContext();
+ ctx.setPath(meta.context);
+ ctx.setName(meta.context);
+ ctx.setJarScanner(new OWBJarScanner());
+ ctx.setInstanceManager(new CDIInstanceManager());
+ try {
+ ctx.setDocBase(dir.getCanonicalPath());
+ } catch (final IOException e) {
+ ctx.setDocBase(dir.getAbsolutePath());
+ }
+ ofNullable(configuration.tomcatFilter).ifPresent(filter -> {
+ final OWBJarScanner jarScanner = new OWBJarScanner();
+ try {
+ jarScanner.setJarScanFilter(JarScanFilter.class.cast(Thread.currentThread().getContextClassLoader().loadClass(filter).newInstance()));
+ } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+ ctx.setJarScanner(jarScanner);
+ });
+ ctx.addLifecycleListener(new MeecrowaveContextConfig(configuration));
+ ctx.addLifecycleListener(event -> {
+ switch (event.getType()) {
+ case Lifecycle.AFTER_START_EVENT:
+ ctx.getResources().setCachingAllowed(configuration.webResourceCached);
+ break;
+ case Lifecycle.BEFORE_INIT_EVENT:
+ if (configuration.loginConfig != null) {
+ ctx.setLoginConfig(configuration.loginConfig.build());
+ }
+ for (final SecurityConstaintBuilder sc : configuration.securityConstraints) {
+ ctx.addConstraint(sc.build());
+ }
+ if (configuration.webXml != null) {
+ ctx.getServletContext().setAttribute(Globals.ALT_DD_ATTR, configuration.webXml);
+ }
+ break;
+ default:
+ }
+
+ });
+ ctx.addLifecycleListener(new Tomcat.FixContextListener()); // after having configured the security!!!
+
+ ctx.addServletContainerInitializer((c, ctx1) -> {
+ ctx.getServletContext().setAttribute("meecrowave.configuration", configuration);
+
+ new OWBAutoSetup().onStartup(c, ctx1);
+ new CxfCdiAutoSetup().onStartup(c, ctx1);
+ new TomcatAutoInitializer().onStartup(c, ctx1);
+ }, emptySet());
+
+ if (configuration.isUseTomcatDefaults()) {
+ ctx.setSessionTimeout(30);
+ ctx.addWelcomeFile("index.html");
+ ctx.addWelcomeFile("index.htm");
+ try {
+ final Field mimesField = Tomcat.class.getDeclaredField("DEFAULT_MIME_MAPPINGS");
+ if (!mimesField.isAccessible()) {
+ mimesField.setAccessible(true);
+ }
+ final String[] defaultMimes = String[].class.cast(mimesField.get(null));
+ for (int i = 0; i < defaultMimes.length; ) {
+ ctx.addMimeMapping(defaultMimes[i++], defaultMimes[i++]);
+ }
+ } catch (final NoSuchFieldException | IllegalAccessException e) {
+ throw new IllegalStateException("Incompatible Tomcat", e);
+ }
+ }
+
+ ofNullable(meta.consumer).ifPresent(c -> c.accept(ctx));
+
+ tomcat.getHost().addChild(ctx);
+ contexts.put(meta.context, () -> {
+ try {
+ tomcat.getHost().removeChild(ctx);
+ } finally {
+ if (dir != meta.docBase) {
+ IO.delete(dir);
+ }
+ }
+
+ });
+ return this;
+ }
+
+ public Meecrowave bake() {
+ return bake("");
+ }
+
+ public Meecrowave bake(final String ctx) {
+ start();
+ return deployClasspath(ctx);
+ }
+
+ public Meecrowave start() {
+ clearCatalinaSystemProperties = System.getProperty("catalina.base") == null && System.getProperty("catalina.home") == null;
+ if (configuration.isUseLog4j2JulLogManager()) {
+ System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
+ }
+
+ if (configuration.loggingGlobalSetup) {
+ final String[] toRestore = new String[]{
+ System.getProperty("openwebbeans.logging.factory"),
+ System.getProperty("org.apache.cxf.Logger"),
+ System.getProperty("org.apache.tomcat.Logger")
+ };
+ System.setProperty("openwebbeans.logging.factory", Log4j2LoggerFactory.class.getName());
+ System.setProperty("org.apache.cxf.Logger", Log4j2Logger.class.getName());
+ System.setProperty("org.apache.tomcat.Logger", Log4j2Log.class.getName());
+ postTask = () -> {
+ if (toRestore[0] == null) {
+ System.clearProperty("openwebbeans.logging.factory");
+ } else {
+ System.setProperty("openwebbeans.logging.factory", toRestore[0]);
+ }
+ if (toRestore[1] == null) {
+ System.clearProperty("org.apache.cxf.Logger");
+ } else {
+ System.setProperty("org.apache.cxf.Logger", toRestore[1]);
+ }
+ if (toRestore[2] == null) {
+ System.clearProperty("org.apache.tomcat.Logger");
+ } else {
+ System.setProperty("org.apache.tomcat.Logger", toRestore[2]);
+ }
+ };
+ }
+ if (configuration.quickSession) {
+ tomcat = new TomcatWithFastSessionIDs();
+ } else {
+ tomcat = new InternalTomcat();
+ }
+
+ { // setup
+ base = new File(newBaseDir());
+
+ final File conf = createDirectory(base, "conf");
+ createDirectory(base, "lib");
+ createDirectory(base, "logs");
+ createDirectory(base, "temp");
+ createDirectory(base, "work");
+ createDirectory(base, "webapps");
+
+ synchronize(conf, configuration.conf);
+ }
+
+ final Properties props = configuration.properties;
+ StrSubstitutor substitutor = null;
+ for (final String s : props.stringPropertyNames()) {
+ final String v = props.getProperty(s);
+ if (v != null && v.contains("${")) {
+ if (substitutor == null) {
+ final Map<String, String> placeHolders = new HashMap<>();
+ placeHolders.put("meecrowave.embedded.http", Integer.toString(configuration.httpPort));
+ placeHolders.put("meecrowave.embedded.https", Integer.toString(configuration.httpsPort));
+ placeHolders.put("meecrowave.embedded.stop", Integer.toString(configuration.stopPort));
+ substitutor = new StrSubstitutor(placeHolders);
+ }
+ props.put(s, substitutor.replace(v));
+ }
+ }
+
+ final File conf = new File(base, "conf");
+ final File webapps = new File(base, "webapps");
+
+ tomcat.setBaseDir(base.getAbsolutePath());
+ tomcat.setHostname(configuration.host);
+
+ final boolean initialized;
+ if (configuration.serverXml != null) {
+ final File file = new File(conf, "server.xml");
+ if (!file.equals(configuration.serverXml)) {
+ try (final InputStream is = new FileInputStream(configuration.serverXml);
+ final FileOutputStream fos = new FileOutputStream(file)) {
+ IO.copy(is, fos);
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ // respect config (host/port) of the Configuration
+ final QuickServerXmlParser ports = QuickServerXmlParser.parse(file);
+ if (configuration.keepServerXmlAsThis) {
+ configuration.httpPort = Integer.parseInt(ports.http());
+ configuration.stopPort = Integer.parseInt(ports.stop());
+ } else {
+ final Map<String, String> replacements = new HashMap<>();
+ replacements.put(ports.http(), String.valueOf(configuration.httpPort));
+ replacements.put(ports.https(), String.valueOf(configuration.httpsPort));
+ replacements.put(ports.stop(), String.valueOf(configuration.stopPort));
+
+ String serverXmlContent;
+ try (final InputStream stream = new FileInputStream(file)) {
+ serverXmlContent = IO.toString(stream);
+ for (final Map.Entry<String, String> pair : replacements.entrySet()) {
+ serverXmlContent = serverXmlContent.replace(pair.getKey(), pair.getValue());
+ }
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ try (final OutputStream os = new FileOutputStream(file)) {
+ os.write(serverXmlContent.getBytes(StandardCharsets.UTF_8));
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ tomcat.server(createServer(file.getAbsolutePath()));
+ initialized = true;
+ } else {
+ tomcat.getServer().setPort(configuration.stopPort);
+ initialized = false;
+ }
+
+ ofNullable(configuration.getSharedLibraries()).map(File::new).filter(File::isDirectory).ifPresent(libRoot -> {
+ final Collection<URL> libs = new ArrayList<>();
+ try {
+ libs.add(libRoot.toURI().toURL());
+ } catch (final MalformedURLException e) {
+ throw new IllegalStateException(e);
+ }
+ libs.addAll(ofNullable(libRoot.listFiles((dir, name) -> name.endsWith(".jar") || name.endsWith(".zip")))
+ .map(Stream::of).map(s -> s.map(f -> {
+ try {
+ return f.toURI().toURL();
+ } catch (final MalformedURLException e) {
+ throw new IllegalStateException(e);
+ }
+ }).collect(toList()))
+ .orElse(emptyList()));
+ tomcat.getServer().setParentClassLoader(new MeecrowaveContainerLoader(libs.toArray(new URL[libs.size()]), Thread.currentThread().getContextClassLoader()));
+ });
+
+ if (!initialized) {
+ tomcat.setHostname(configuration.host);
+ tomcat.getEngine().setDefaultHost(configuration.host);
+ final StandardHost host = new StandardHost();
+ host.setName(configuration.host);
+ host.setAppBase(webapps.getAbsolutePath());
+ host.setUnpackWARs(true); // forced for now cause OWB doesn't support war:file:// urls
+ try {
+ host.setWorkDir(new File(base, "work").getCanonicalPath());
+ } catch (final IOException e) {
+ host.setWorkDir(new File(base, "work").getAbsolutePath());
+ }
+ tomcat.setHost(host);
+ }
+
+ if (configuration.realm != null) {
+ tomcat.getEngine().setRealm(configuration.realm);
+ }
+
+ if (tomcat.getRawConnector() == null && !configuration.skipHttp) {
+ final Connector connector = createConnector();
+ connector.setPort(configuration.httpPort);
+ if (connector.getAttribute("connectionTimeout") == null) {
+ connector.setAttribute("connectionTimeout", "3000");
+ }
+
+ tomcat.getService().addConnector(connector);
+ tomcat.setConnector(connector);
+ }
+
+ // create https connector
+ if (configuration.ssl) {
+ final Connector httpsConnector = createConnector();
+ httpsConnector.setPort(configuration.httpsPort);
+ httpsConnector.setSecure(true);
+ httpsConnector.setProperty("SSLEnabled", "true");
+ httpsConnector.setProperty("sslProtocol", configuration.sslProtocol);
+
+ if (configuration.keystoreFile != null) {
+ httpsConnector.setAttribute("", configuration.keystoreFile);
+ }
+ if (configuration.keystorePass != null) {
+ httpsConnector.setAttribute("keystorePass", configuration.keystorePass);
+ }
+ httpsConnector.setAttribute("keystoreType", configuration.keystoreType);
+ if (configuration.clientAuth != null) {
+ httpsConnector.setAttribute("clientAuth", configuration.clientAuth);
+ }
+
+ if (configuration.keyAlias != null) {
+ httpsConnector.setAttribute("keyAlias", configuration.keyAlias);
+ }
+
+ tomcat.getService().addConnector(httpsConnector);
+
+ if (configuration.skipHttp) {
+ tomcat.setConnector(httpsConnector);
+ }
+ }
+
+ if (configuration.http2) {
+ final Connector c = configuration.ssl ?
+ tomcat.getService().findConnectors()[tomcat.getService().findConnectors().length - 1] :
+ ofNullable(tomcat.getRawConnector()).orElse(tomcat.getService().findConnectors()[0]);
+
+ c.addUpgradeProtocol(new Http2Protocol());
+ c.addSslHostConfig(buildSslHostConfig());
+ }
+
+ for (final Connector c : configuration.connectors) {
+ tomcat.getService().addConnector(c);
+ }
+ if (!configuration.skipHttp && !configuration.ssl && !configuration.connectors.isEmpty()) {
+ tomcat.setConnector(configuration.connectors.iterator().next());
+ }
+
+ if (configuration.users != null) {
+ for (final Map.Entry<String, String> user : configuration.users.entrySet()) {
+ tomcat.addUser(user.getKey(), user.getValue());
+ }
+ }
+ if (configuration.roles != null) {
+ for (final Map.Entry<String, String> user : configuration.roles.entrySet()) {
+ for (final String role : user.getValue().split(" *, *")) {
+ tomcat.addRole(user.getKey(), role);
+ }
+ }
+ }
+
+ beforeStart();
+
+ try {
+ if (!initialized) {
+ tomcat.init();
+ }
+ tomcat.start();
+ } catch (final LifecycleException e) {
+ throw new IllegalStateException(e);
+ }
+ if (configuration.isUseShutdownHook()) {
+ hook = new Thread(() -> {
+ hook = null; // prevent close to remove the hook which would throw an exception
+ close();
+ }, "meecrowave-stop-hook");
+ Runtime.getRuntime().addShutdownHook(hook);
+ }
+ return this;
+ }
+
+ private SSLHostConfig buildSslHostConfig() {
+ final ObjectRecipe recipe = new ObjectRecipe(SSLHostConfig.class);
+ for (final String key : configuration.properties.stringPropertyNames()) {
+ if (!key.startsWith("connector.sslhostconfig.")) {
+ continue;
+ }
+ final String substring = key.substring("connector.sslhostconfig.".length());
+ recipe.setProperty(substring, configuration.properties.getProperty(key));
+ }
+ return SSLHostConfig.class.cast(recipe.create());
+ }
+
+ protected void beforeStart() {
+ // no-op
+ }
+
+ protected void beforeStop() {
+ // no-op
+ }
+
+ @Override
+ public void close() {
+ if (tomcat == null) {
+ return;
+ }
+ if (hook != null) {
+ Runtime.getRuntime().removeShutdownHook(hook);
+ this.hook = null;
+ }
+ beforeStop();
+ if (MeecrowaveContainerLoader.class.isInstance(tomcat.getServer().getParentClassLoader())) {
+ try {
+ MeecrowaveContainerLoader.class.cast(tomcat.getServer().getParentClassLoader()).close();
+ } catch (final IOException e) {
+ new LogFacade(Meecrowave.class.getName()).error(e.getMessage(), e);
+ }
+ }
+ try {
+ contexts.values().forEach(Runnable::run);
+ } finally {
+ try {
+ tomcat.stop();
+ tomcat.destroy();
+ } catch (final LifecycleException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ if (clearCatalinaSystemProperties) {
+ Stream.of("catalina.base", "catalina.home").forEach(System::clearProperty);
+ }
+ if (configuration.isUseLog4j2JulLogManager()) {
+ System.clearProperty("java.util.logging.manager");
+ }
+ ofNullable(postTask).ifPresent(Runnable::run);
+ postTask = null;
+ try {
+ IO.delete(base);
+ IO.delete(ownedTempDir);
+ } catch (final IllegalArgumentException /*does not exist from the hook*/ e) {
+ // no-op
+ } finally {
+ base = null;
+ }
+ }
+ }
+ }
+
+ protected Connector createConnector() {
+ final Connector connector;
+ final Properties properties = configuration.properties;
+ if (properties != null) {
+ final Map<String, String> attributes = new HashMap<>();
+ final ObjectRecipe recipe = new ObjectRecipe(Connector.class);
+ for (final String key : properties.stringPropertyNames()) {
+ if (!key.startsWith("connector.")) {
+ continue;
+ }
+
+ final String substring = key.substring("connector.".length());
+ if (substring.startsWith("sslhostconfig.")) {
+ continue;
+ }
+
+ if (!substring.startsWith("attributes.")) {
+ recipe.setProperty(substring, properties.getProperty(key));
+ } else {
+ attributes.put(substring.substring("attributes.".length()), properties.getProperty(key));
+ }
+ }
+ connector = recipe.getProperties().isEmpty() ? new Connector() : Connector.class.cast(recipe.create());
+ for (final Map.Entry<String, String> attr : attributes.entrySet()) {
+ connector.setAttribute(attr.getKey(), attr.getValue());
+ }
+ } else {
+ connector = new Connector();
+ }
+ return connector;
+ }
+
+ private static Server createServer(final String serverXml) {
+ final Catalina catalina = new Catalina() {
+ // skip few init we don't need *here*
+ @Override
+ protected void initDirs() {
+ // no-op
+ }
+
+ @Override
+ protected void initStreams() {
+ // no-op
+ }
+
+ @Override
+ protected void initNaming() {
+ // no-op
+ }
+ };
+ catalina.setConfigFile(serverXml);
+ catalina.load();
+ return catalina.getServer();
+ }
+
+ private File createDirectory(final File parent, final String directory) {
+ final File dir = new File(parent, directory);
+ IO.mkdirs(dir);
+ return dir;
+ }
+
+ private void synchronize(final File base, final String resourceBase) {
+ if (resourceBase == null) {
+ return;
+ }
+
+ try {
+ final Map<String, URL> urls = new ResourceFinder("").getResourcesMap(resourceBase);
+ for (final Map.Entry<String, URL> u : urls.entrySet()) {
+ try (final InputStream is = u.getValue().openStream()) {
+ final File to = new File(base, u.getKey());
+ try (final OutputStream os = new FileOutputStream(to)) {
+ IO.copy(is, os);
+ }
+ if ("server.xml".equals(u.getKey())) {
+ configuration.setServerXml(to.getAbsolutePath());
+ }
+ }
+ }
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private String newBaseDir() {
+ File file;
+
+ final String dir = configuration.dir;
+ if (dir != null) {
+ final File dirFile = new File(dir);
+ if (dirFile.exists()) {
+ if (base.exists() && configuration.deleteBaseOnStartup) {
+ IO.delete(base);
+ }
+ return dir;
+ }
+ IO.mkdirs(dirFile);
+ return dirFile.getAbsolutePath();
+ }
+
+ file = new File(Stream.of("target", "build", ".")
+ .map(File::new)
+ .filter(File::isDirectory)
+ .findFirst().get(), "meecrowave-" + System.nanoTime());
+ IO.mkdirs(file);
+ return file.getAbsolutePath();
+ }
+
+ public void await() {
+ tomcat.getServer().await();
+ }
+
+ public static class Builder {
+ @CliOption(name = "http", description = "HTTP port")
+ private int httpPort = 8080;
+
+ @CliOption(name = "https", description = "HTTPS port")
+ private int httpsPort = 8443;
+
+ @CliOption(name = "stop", description = "Shutdown port if used or -1")
+ private int stopPort = -1;
+
+ @CliOption(name = "host", description = "Default host")
+ private String host = "localhost";
+
+ @CliOption(name = "dir", description = "Root folder if provided otherwise a fake one is created in tmp-dir")
+ private String dir;
+
+ @CliOption(name = "server-xml", description = "Provided server.xml")
+ private File serverXml;
+
+ @CliOption(name = "keep-server-xml-as-this", description = "Don't replace ports in server.xml")
+ private boolean keepServerXmlAsThis;
+
+ @CliOption(name = "properties", description = "Passthrough properties")
+ private Properties properties = new Properties();
+
+ @CliOption(name = "quick-session", description = "Should an unsecured but fast session id generator be used")
+ private boolean quickSession = true;
+
+ @CliOption(name = "skip-http", description = "Skip HTTP connector")
+ private boolean skipHttp;
+
+ @CliOption(name = "ssl", description = "Use HTTPS")
+ private boolean ssl;
+
+ @CliOption(name = "keystore-file", description = "HTTPS keystore location")
+ private String keystoreFile;
+
+ @CliOption(name = "keystore-password", description = "HTTPS keystore password")
+ private String keystorePass;
+
+ @CliOption(name = "keystore-type", description = "HTTPS keystore type")
+ private String keystoreType = "JKS";
+
+ @CliOption(name = "client-auth", description = "HTTPS keystore client authentication")
+ private String clientAuth;
+
+ @CliOption(name = "keystore-alias", description = "HTTPS keystore alias")
+ private String keyAlias;
+
+ @CliOption(name = "ssl-protocol", description = "HTTPS protocol")
+ private String sslProtocol;
+
+ @CliOption(name = "web-xml", description = "Global web.xml")
+ private String webXml;
+
+ @CliOption(name = "login-config", description = "web.xml login config")
+ private LoginConfigBuilder loginConfig;
+
+ @CliOption(name = "security-constraint", description = "web.xml security constraint")
+ private Collection<SecurityConstaintBuilder> securityConstraints = new LinkedList<>();
+
+ @CliOption(name = "realm", description = "realm")
+ private Realm realm;
+
+ @CliOption(name = "users", description = "In memory users")
+ private Map<String, String> users;
+
+ @CliOption(name = "roles", description = "In memory roles")
+ private Map<String, String> roles;
+
+ @CliOption(name = "http2", description = "Activate HTTP 2")
+ private boolean http2;
+
+ @CliOption(name = "connector", description = "Custom connectors")
+ private final Collection<Connector> connectors = new ArrayList<>();
+
+ @CliOption(name = "tmp-dir", description = "Temporary directory")
+ private String tempDir = System.getProperty("java.io.tmpdir");
+
+ @CliOption(name = "web-resource-cached", description = "Cache web resources")
+ private boolean webResourceCached = true;
+
+ @CliOption(name = "conf", description = "Conf folder to synchronize")
+ private String conf;
+
+ @CliOption(name = "delete-on-startup", description = "Should the directory be cleaned on startup if existing")
+ private boolean deleteBaseOnStartup = true;
+
+ @CliOption(name = "jaxrs-mapping", description = "Default jaxrs mapping")
+ private String jaxrsMapping = "/*";
+
+ @CliOption(name = "cdi-conversation", description = "Should CDI conversation be activated")
+ private boolean cdiConversation;
+
+ @CliOption(name = "jaxrs-provider-setup", description = "Should default JAX-RS provider be configured")
+ private boolean jaxrsProviderSetup = true;
+
+ @CliOption(name = "jaxrs-default-providers", description = "If jaxrsProviderSetup is true the list of default providers to load (or defaulting to johnson jsonb and jsonp ones)")
+ private String jaxrsDefaultProviders;
+
+ @CliOption(name = "jaxrs-log-provider", description = "Should JAX-RS providers be logged")
+ private boolean jaxrsLogProviders = false;
+
+ @CliOption(name = "logging-global-setup", description = "Should logging be configured to use log4j2 (it is global)")
+ private boolean loggingGlobalSetup = true;
+
+ @CliOption(name = "cxf-servlet-params", description = "Init parameters passed to CXF servlet")
+ private Map<String, String> cxfServletParams;
+
+ @CliOption(name = "tomcat-scanning", description = "Should Tomcat scanning be used (@HandleTypes, @WebXXX)")
+ private boolean tomcatScanning = true;
+
+ @CliOption(name = "tomcat-default-setup", description = "Add default servlet")
+ private boolean tomcatAutoSetup = true;
+
+ @CliOption(name = "use-shutdown-hook", description = "Use shutdown hook to automatically stop the container on Ctrl+C")
+ private boolean useShutdownHook = true;
+
+ @CliOption(name = "tomcat-filter", description = "A Tomcat JarScanFilter")
+ private String tomcatFilter;
+
+ @CliOption(name = "tomcat-default", description = "Should Tomcat default be set (session timeout, mime mapping etc...)")
+ private boolean useTomcatDefaults = true;
+
+ @CliOption(name = "tomcat-wrap-loader", description = "(Experimental) When deploying a classpath (current classloader), " +
+ "should meecrowave wrap the loader to define another loader identity but still use the same classes and resources.")
+ private boolean tomcatWrapLoader = false;
+
+ @CliOption(name = "shared-librairies", description = "A folder containing shared libraries.")
+ private String sharedLibraries;
+
+ @CliOption(name = "log4j2-jul-bridge", description = "Should JUL logs be redirected to Log4j2 - only works before JUL usage.")
+ private boolean useLog4j2JulLogManager = System.getProperty("java.util.logging.manager") == null;
+
+ public Builder() { // load defaults
+ loadFrom("meecrowave.properties");
+ }
+
+ public String getSharedLibraries() {
+ return sharedLibraries;
+ }
+
+ public Builder sharedLibraries(final String sharedLibraries) {
+ setSharedLibraries(sharedLibraries);
+ return this;
+ }
+
+ public void setSharedLibraries(final String sharedLibraries) {
+ this.sharedLibraries = sharedLibraries;
+ }
+
+ public boolean isJaxrsLogProviders() {
+ return jaxrsLogProviders;
+ }
+
+ public void setJaxrsLogProviders(final boolean jaxrsLogProviders) {
+ this.jaxrsLogProviders = jaxrsLogProviders;
+ }
+
+ public boolean isUseTomcatDefaults() {
+ return useTomcatDefaults;
+ }
+
+ public void setUseTomcatDefaults(final boolean useTomcatDefaults) {
+ this.useTomcatDefaults = useTomcatDefaults;
+ }
+
+ public String getTomcatFilter() {
+ return tomcatFilter;
+ }
+
+ public void setTomcatFilter(final String tomcatFilter) {
+ this.tomcatFilter = tomcatFilter;
+ }
+
+ public boolean isTomcatScanning() {
+ return tomcatScanning;
+ }
+
+ public void setTomcatScanning(final boolean tomcatScanning) {
+ this.tomcatScanning = tomcatScanning;
+ }
+
+ public Map<String, String> getCxfServletParams() {
+ return cxfServletParams;
+ }
+
+ public void setCxfServletParams(final Map<String, String> cxfServletParams) {
+ this.cxfServletParams = cxfServletParams;
+ }
+
+ public boolean isLoggingGlobalSetup() {
+ return loggingGlobalSetup;
+ }
+
+ public void setLoggingGlobalSetup(final boolean loggingGlobalSetup) {
+ this.loggingGlobalSetup = loggingGlobalSetup;
+ }
+
+ public boolean isJaxrsProviderSetup() {
+ return jaxrsProviderSetup;
+ }
+
+ public void setJaxrsProviderSetup(final boolean jaxrsProviderSetup) {
+ this.jaxrsProviderSetup = jaxrsProviderSetup;
+ }
+
+ public int getHttpPort() {
+ return httpPort;
+ }
+
+ public void setHttpPort(final int httpPort) {
+ this.httpPort = httpPort;
+ }
+
+ public int getHttpsPort() {
+ return httpsPort;
+ }
+
+ public void setHttpsPort(final int httpsPort) {
+ this.httpsPort = httpsPort;
+ }
+
+ public int getStopPort() {
+ return stopPort;
+ }
+
+ public void setStopPort(final int stopPort) {
+ this.stopPort = stopPort;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(final String host) {
+ this.host = host;
+ }
+
+ public String getDir() {
+ return dir;
+ }
+
+ public void setDir(final String dir) {
+ this.dir = dir;
+ }
+
+ public File getServerXml() {
+ return serverXml;
+ }
+
+ public void setServerXml(final File serverXml) {
+ this.serverXml = serverXml;
+ }
+
+ public boolean isKeepServerXmlAsThis() {
+ return keepServerXmlAsThis;
+ }
+
+ public void setKeepServerXmlAsThis(final boolean keepServerXmlAsThis) {
+ this.keepServerXmlAsThis = keepServerXmlAsThis;
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public void setProperties(final Properties properties) {
+ this.properties = properties;
+ }
+
+ public boolean isQuickSession() {
+ return quickSession;
+ }
+
+ public void setQuickSession(final boolean quickSession) {
+ this.quickSession = quickSession;
+ }
+
+ public boolean isSkipHttp() {
+ return skipHttp;
+ }
+
+ public void setSkipHttp(final boolean skipHttp) {
+ this.skipHttp = skipHttp;
+ }
+
+ public boolean isSsl() {
+ return ssl;
+ }
+
+ public void setSsl(final boolean ssl) {
+ this.ssl = ssl;
+ }
+
+ public String getKeystoreFile() {
+ return keystoreFile;
+ }
+
+ public void setKeystoreFile(final String keystoreFile) {
+ this.keystoreFile = keystoreFile;
+ }
+
+ public String getKeystorePass() {
+ return keystorePass;
+ }
+
+ public void setKeystorePass(final String keystorePass) {
+ this.keystorePass = keystorePass;
+ }
+
+ public String getKeystoreType() {
+ return keystoreType;
+ }
+
+ public void setKeystoreType(final String keystoreType) {
+ this.keystoreType = keystoreType;
+ }
+
+ public String getClientAuth() {
+ return clientAuth;
+ }
+
+ public void setClientAuth(final String clientAuth) {
+ this.clientAuth = clientAuth;
+ }
+
+ public String getKeyAlias() {
+ return keyAlias;
+ }
+
+ public void setKeyAlias(final String keyAlias) {
+ this.keyAlias = keyAlias;
+ }
+
+ public String getSslProtocol() {
+ return sslProtocol;
+ }
+
+ public void setSslProtocol(final String sslProtocol) {
+ this.sslProtocol = sslProtocol;
+ }
+
+ public String getWebXml() {
+ return webXml;
+ }
+
+ public void setWebXml(final String webXml) {
+ this.webXml = webXml;
+ }
+
+ public LoginConfigBuilder getLoginConfig() {
+ return loginConfig;
+ }
+
+ public Builder loginConfig(final LoginConfigBuilder loginConfig) {
+ setLoginConfig(loginConfig);
+ return this;
+ }
+
+ public void setLoginConfig(final LoginConfigBuilder loginConfig) {
+ this.loginConfig = loginConfig;
+ }
+
+ public Collection<SecurityConstaintBuilder> getSecurityConstraints() {
+ return securityConstraints;
+ }
+
+ public Builder securityConstraints(final SecurityConstaintBuilder securityConstraint) {
+ securityConstraints = securityConstraints == null ? new ArrayList<>() : securityConstraints;
+ securityConstraints.add(securityConstraint);
+ return this;
+ }
+
+ public void setSecurityConstraints(final Collection<SecurityConstaintBuilder> securityConstraints) {
+ this.securityConstraints = securityConstraints;
+ }
+
+ public Realm getRealm() {
+ return realm;
+ }
+
+ public Builder realm(final Realm realm) {
+ setRealm(realm);
+ return this;
+ }
+
+ public void setRealm(final Realm realm) {
+ this.realm = realm;
+ }
+
+ public Map<String, String> getUsers() {
+ return users;
+ }
+
+ public void setUsers(final Map<String, String> users) {
+ this.users = users;
+ }
+
+ public Map<String, String> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(final Map<String, String> roles) {
+ this.roles = roles;
+ }
+
+ public boolean isHttp2() {
+ return http2;
+ }
+
+ public void setHttp2(final boolean http2) {
+ this.http2 = http2;
+ }
+
+ public Collection<Connector> getConnectors() {
+ return connectors;
+ }
+
+ public String getTempDir() {
+ return tempDir;
+ }
+
+ public void setTempDir(final String tempDir) {
+ this.tempDir = tempDir;
+ }
+
+ public boolean isWebResourceCached() {
+ return webResourceCached;
+ }
+
+ public void setWebResourceCached(final boolean webResourceCached) {
+ this.webResourceCached = webResourceCached;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(final String conf) {
+ this.conf = conf;
+ }
+
+ public boolean isDeleteBaseOnStartup() {
+ return deleteBaseOnStartup;
+ }
+
+ public void setDeleteBaseOnStartup(final boolean deleteBaseOnStartup) {
+ this.deleteBaseOnStartup = deleteBaseOnStartup;
+ }
+
+ public String getJaxrsMapping() {
+ return jaxrsMapping;
+ }
+
+ public void setJaxrsMapping(final String jaxrsMapping) {
+ this.jaxrsMapping = jaxrsMapping;
+ }
+
+ public boolean isCdiConversation() {
+ return cdiConversation;
+ }
+
+ public void setCdiConversation(final boolean cdiConversation) {
+ this.cdiConversation = cdiConversation;
+ }
+
+ public Builder randomHttpPort() {
+ try (final ServerSocket serverSocket = new ServerSocket(0)) {
+ this.httpPort = serverSocket.getLocalPort();
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ return this;
+ }
+
+ public Builder loadFrom(final String resource) {
+ try (final InputStream is = findStream(resource)) {
+ if (is != null) {
+ final Properties config = new Properties() {{
+ load(is);
+ }};
+ loadFromProperties(config);
+ }
+ return this;
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ public void setServerXml(final String file) {
+ if (file == null) {
+ serverXml = null;
+ } else {
+ final File sXml = new File(file);
+ if (sXml.exists()) {
+ serverXml = sXml;
+ }
+ }
+ }
+
+ public Builder property(final String key, final String value) {
+ properties.setProperty(key, value);
+ return this;
+ }
+
+ private InputStream findStream(final String resource) throws FileNotFoundException {
+ InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
+ if (stream == null) {
+ final File file = new File(resource);
+ if (file.exists()) {
+ return new FileInputStream(file);
+ }
+ }
+ return stream;
+ }
+
+ public Builder user(final String name, final String pwd) {
+ if (users == null) {
+ users = new HashMap<>();
+ }
+ this.users.put(name, pwd);
+ return this;
+ }
+
+ public Builder role(final String user, final String roles) {
+ if (this.roles == null) {
+ this.roles = new HashMap<>();
+ }
+ this.roles.put(user, roles);
+ return this;
+ }
+
+ public Builder cxfServletParam(final String key, final String value) {
+ if (this.cxfServletParams == null) {
+ this.cxfServletParams = new HashMap<>();
+ }
+ this.cxfServletParams.put(key, value);
+ return this;
+ }
+
+ public String getActiveProtocol() {
+ return isSkipHttp() ? "https" : "http";
+ }
+
+ public int getActivePort() {
+ return isSkipHttp() ? getHttpsPort() : getHttpPort();
+ }
+
+ public boolean isTomcatAutoSetup() {
+ return tomcatAutoSetup;
+ }
+
+ public void setTomcatAutoSetup(final boolean tomcatAutoSetup) {
+ this.tomcatAutoSetup = tomcatAutoSetup;
+ }
+
+ public boolean isUseShutdownHook() {
+ return useShutdownHook;
+ }
+
+ public void setUseShutdownHook(final boolean useShutdownHook) {
+ this.useShutdownHook = useShutdownHook;
+ }
+
+ public Builder noShutdownHook() {
+ setUseShutdownHook(false);
+ return this;
+ }
+
+ public boolean isTomcatWrapLoader() {
+ return tomcatWrapLoader;
+ }
+
+ public void setTomcatWrapLoader(final boolean tomcatWrapLoader) {
+ this.tomcatWrapLoader = tomcatWrapLoader;
+ }
+
+ public void addCustomizer(final Consumer<Builder> configurationCustomizer) {
+ configurationCustomizer.accept(this);
+ }
+
+ public String getJaxrsDefaultProviders() {
+ return jaxrsDefaultProviders;
+ }
+
+ public void setJaxrsDefaultProviders(final String jaxrsDefaultProviders) {
+ this.jaxrsDefaultProviders = jaxrsDefaultProviders;
+ }
+
+ public boolean isUseLog4j2JulLogManager() {
+ return useLog4j2JulLogManager;
+ }
+
+ public void setUseLog4j2JulLogManager(final boolean useLog4j2JulLogManager) {
+ this.useLog4j2JulLogManager = useLog4j2JulLogManager;
+ }
+
+ public void loadFromProperties(final Properties config) {
+ // filtering properties with system properties or themself
+ final StrSubstitutor strSubstitutor = new StrSubstitutor(new StrLookup<String>() {
+ @Override
+ public String lookup(final String key) {
+ final String property = System.getProperty(key);
+ return property == null ? config.getProperty(key) : null;
+ }
+ });
+ for (final String key : config.stringPropertyNames()) {
+ final String val = config.getProperty(key);
+ if (val == null || val.trim().isEmpty()) {
+ continue;
+ }
+ final String newVal = strSubstitutor.replace(config.getProperty(key));
+ if (!val.equals(newVal)) {
+ config.setProperty(key, newVal);
+ }
+ }
+
+ for (final Field field : Builder.class.getDeclaredFields()) {
+ final CliOption annotation = field.getAnnotation(CliOption.class);
+ if (annotation == null) {
+ return;
+ }
+ final String name = field.getName();
+ ofNullable(config.getProperty(annotation.name())).ifPresent(val -> {
+ final Object toSet;
+ if (field.getType() == String.class) {
+ toSet = val;
+ } else if (field.getType() == int.class) {
+ if ("httpPort".equals(name) && "-1".equals(val)) { // special case in case of random port
+ randomHttpPort();
+ toSet = null;
+ } else {
+ toSet = Integer.parseInt(val);
+ }
+ } else if (field.getType() == boolean.class) {
+ toSet = Boolean.parseBoolean(val);
+ } else if (field.getType() == File.class) {
+ toSet = new File(val);
+ } else {
+ toSet = null;
+ }
+ if (toSet == null) { // handled elsewhere
+ return;
+ }
+
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ try {
+ field.set(this, toSet);
+ } catch (final IllegalAccessException e) {
+ throw new IllegalStateException(e);
+ }
+ });
+ }
+
+ // not trivial types
+ for (final String prop : config.stringPropertyNames()) {
+ if (prop.startsWith("properties.")) {
+ property(prop.substring("properties.".length()), config.getProperty(prop));
+ } else if (prop.startsWith("users.")) {
+ user(prop.substring("users.".length()), config.getProperty(prop));
+ } else if (prop.startsWith("roles.")) {
+ role(prop.substring("roles.".length()), config.getProperty(prop));
+ } else if (prop.startsWith("cxf.servlet.params.")) {
+ cxfServletParam(prop.substring("cxf.servlet.params.".length()), config.getProperty(prop));
+ } else if (prop.startsWith("connector.")) { // created in container
+ property(prop, config.getProperty(prop));
+ } else if (prop.equals("realm")) {
+ final ObjectRecipe recipe = new ObjectRecipe(config.getProperty(prop));
+ for (final String realmConfig : config.stringPropertyNames()) {
+ if (realmConfig.startsWith("realm.")) {
+ recipe.setProperty(realmConfig.substring("realm.".length()), config.getProperty(realmConfig));
+ }
+ }
+ this.realm = Realm.class.cast(recipe.create());
+ } else if (prop.equals("login")) {
+ final ObjectRecipe recipe = new ObjectRecipe(LoginConfigBuilder.class.getName());
+ for (final String nestedConfig : config.stringPropertyNames()) {
+ if (nestedConfig.startsWith("login.")) {
+ recipe.setProperty(nestedConfig.substring("login.".length()), config.getProperty(nestedConfig));
+ }
+ }
+ loginConfig = LoginConfigBuilder.class.cast(recipe.create());
+ } else if (prop.equals("securityConstraint")) {
+ final ObjectRecipe recipe = new ObjectRecipe(SecurityConstaintBuilder.class.getName());
+ for (final String nestedConfig : config.stringPropertyNames()) {
+ if (nestedConfig.startsWith("securityConstraint.")) {
+ recipe.setProperty(nestedConfig.substring("securityConstraint.".length()), config.getProperty(nestedConfig));
+ }
+ }
+ securityConstraints.add(SecurityConstaintBuilder.class.cast(recipe.create()));
+ } else if (prop.equals("configurationCustomizer")) {
+ final ObjectRecipe recipe = new ObjectRecipe(properties.getProperty(prop));
+ for (final String nestedConfig : config.stringPropertyNames()) {
+ if (nestedConfig.startsWith(prop + '.')) {
+ recipe.setProperty(nestedConfig.substring(prop.length() + 2 /*dot*/), config.getProperty(nestedConfig));
+ }
+ }
+ addCustomizer(Consumer.class.cast(recipe.create()));
+ }
+ }
+ }
+ }
+
+ public static class LoginConfigBuilder {
+ private final LoginConfig loginConfig = new LoginConfig();
+
+ public void setErrorPage(final String errorPage) {
+ loginConfig.setErrorPage(errorPage);
+ }
+
+ public void setLoginPage(final String loginPage) {
+ loginConfig.setLoginPage(loginPage);
+ }
+
+ public void setRealmName(final String realmName) {
+ loginConfig.setRealmName(realmName);
+ }
+
+ public void setAuthMethod(final String authMethod) {
+ loginConfig.setAuthMethod(authMethod);
+ }
+
+ public LoginConfigBuilder errorPage(final String errorPage) {
+ loginConfig.setErrorPage(errorPage);
+ return this;
+ }
+
+ public LoginConfigBuilder loginPage(final String loginPage) {
+ loginConfig.setLoginPage(loginPage);
+ return this;
+ }
+
+ public LoginConfigBuilder realmName(final String realmName) {
+ loginConfig.setRealmName(realmName);
+ return this;
+ }
+
+ public LoginConfigBuilder authMethod(final String authMethod) {
+ loginConfig.setAuthMethod(authMethod);
+ return this;
+ }
+
+ public LoginConfig build() {
+ return loginConfig;
+ }
+
+ public LoginConfigBuilder basic() {
+ return authMethod("BASIC");
+ }
+
+ public LoginConfigBuilder digest() {
+ return authMethod("DIGEST");
+ }
+
+ public LoginConfigBuilder clientCert() {
+ return authMethod("CLIENT-CERT");
+ }
+
+ public LoginConfigBuilder form() {
+ return authMethod("FORM");
+ }
+ }
+
+ public static class SecurityConstaintBuilder {
+ private final SecurityConstraint securityConstraint = new SecurityConstraint();
+
+ public SecurityConstaintBuilder authConstraint(final boolean authConstraint) {
+ securityConstraint.setAuthConstraint(authConstraint);
+ return this;
+ }
+
+ public SecurityConstaintBuilder displayName(final String displayName) {
+ securityConstraint.setDisplayName(displayName);
+ return this;
+ }
+
+ public SecurityConstaintBuilder userConstraint(final String constraint) {
+ securityConstraint.setUserConstraint(constraint);
+ return this;
+ }
+
+ public SecurityConstaintBuilder addAuthRole(final String authRole) {
+ securityConstraint.addAuthRole(authRole);
+ return this;
+ }
+
+ public SecurityConstaintBuilder addCollection(final String name, final String pattern, final String... methods) {
+ final SecurityCollection collection = new SecurityCollection();
+ collection.setName(name);
+ collection.addPattern(pattern);
+ for (final String httpMethod : methods) {
+ collection.addMethod(httpMethod);
+ }
+ securityConstraint.addCollection(collection);
+ return this;
+ }
+
+ public void setAuthConstraint(final boolean authConstraint) {
+ securityConstraint.setAuthConstraint(authConstraint);
+ }
+
+ public void setDisplayName(final String displayName) {
+ securityConstraint.setDisplayName(displayName);
+ }
+
+ public void setUserConstraint(final String userConstraint) {
+ securityConstraint.setUserConstraint(userConstraint);
+ }
+
+ public void setAuthRole(final String authRole) { // easier for config
+ addAuthRole(authRole);
+ }
+
+ // name:pattern:method1/method2
+ public void setCollection(final String value) { // for config
+ final String[] split = value.split(":");
+ if (split.length != 3 && split.length != 2) {
+ throw new IllegalArgumentException("Can't parse " + value + ", syntax is: name:pattern:method1/method2");
+ }
+ addCollection(split[0], split[1], split.length == 2 ? new String[0] : split[2].split("/"));
+ }
+
+ public SecurityConstraint build() {
+ return securityConstraint;
+ }
+ }
+
+ private static class InternalTomcat extends Tomcat {
+ private Connector connector;
+
+ private void server(final Server s) {
+ server = s;
+ connector = server != null && server.findServices().length > 0 && server.findServices()[0].findConnectors().length > 0 ?
+ server.findServices()[0].findConnectors()[0] : null;
+ }
+
+ Connector getRawConnector() {
+ return connector;
+ }
+ }
+
+ private static class TomcatWithFastSessionIDs extends InternalTomcat {
+ @Override
+ public void start() throws LifecycleException {
+ // Use fast, insecure session ID generation for all tests
+ final Server server = getServer();
+ for (final Service service : server.findServices()) {
+ final org.apache.catalina.Container e = service.getContainer();
+ for (final org.apache.catalina.Container h : e.findChildren()) {
+ for (final org.apache.catalina.Container c : h.findChildren()) {
+ Manager m = ((org.apache.catalina.Context) c).getManager();
+ if (m == null) {
+ m = new StandardManager();
+ org.apache.catalina.Context.class.cast(c).setManager(m);
+ }
+ if (m instanceof ManagerBase) {
+ ManagerBase.class.cast(m).setSecureRandomClass(
+ "org.apache.catalina.startup.FastNonSecureRandom");
+ }
+ }
+ }
+ }
+ super.start();
+ }
+ }
+
+ private static class QuickServerXmlParser extends DefaultHandler {
+ private static final SAXParserFactory FACTORY = SAXParserFactory.newInstance();
+
+ static {
+ FACTORY.setNamespaceAware(true);
+ FACTORY.setValidating(false);
+ }
+
+ private static final String STOP_KEY = "STOP";
+ private static final String HTTP_KEY = "HTTP";
+ private static final String SECURED_SUFFIX = "S";
+ private static final String HOST_KEY = "host";
+ private static final String DEFAULT_CONNECTOR_KEY = HTTP_KEY;
+
+ private static final String DEFAULT_HTTP_PORT = "8080";
+ private static final String DEFAULT_HTTPS_PORT = "8443";
+ private static final String DEFAULT_STOP_PORT = "8005";
+ private static final String DEFAULT_HOST = "localhost";
+
+ private final Map<String, String> values = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
+
+ private QuickServerXmlParser(final boolean useDefaults) {
+ if (useDefaults) {
+ values.put(STOP_KEY, DEFAULT_STOP_PORT);
+ values.put(HTTP_KEY, DEFAULT_HTTP_PORT);
+ values.put(HOST_KEY, DEFAULT_HOST);
+ }
+ }
+
+ @Override
+ public void startElement(final String uri, final String localName,
+ final String qName, final Attributes attributes) throws SAXException {
+ if ("Server".equalsIgnoreCase(localName)) {
+ final String port = attributes.getValue("port");
+ if (port != null) {
+ values.put(STOP_KEY, port);
+ } else {
+ values.put(STOP_KEY, DEFAULT_STOP_PORT);
+ }
+ } else if ("Connector".equalsIgnoreCase(localName)) {
+ String protocol = attributes.getValue("protocol");
+ if (protocol == null) {
+ protocol = DEFAULT_CONNECTOR_KEY;
+ } else if (protocol.contains("/")) {
+ protocol = protocol.substring(0, protocol.indexOf("/"));
+ }
+ final String port = attributes.getValue("port");
+ final String ssl = attributes.getValue("secure");
+
+ if (ssl == null || "false".equalsIgnoreCase(ssl)) {
+ values.put(protocol.toUpperCase(), port);
+ } else {
+ values.put(protocol.toUpperCase() + SECURED_SUFFIX, port);
+ }
+ } else if ("Host".equalsIgnoreCase(localName)) {
+ final String host = attributes.getValue("name");
+ if (host != null) {
+ values.put(HOST_KEY, host);
+ }
+ }
+ }
+
+ private static QuickServerXmlParser parse(final File serverXml) {
+ return parse(serverXml, true);
+ }
+
+ private static QuickServerXmlParser parse(final File serverXml, final boolean defaults) {
+ final QuickServerXmlParser handler = new QuickServerXmlParser(defaults);
+ try {
+ final SAXParser parser = FACTORY.newSAXParser();
+ parser.parse(serverXml, handler);
+ } catch (final Exception e) {
+ // no-op: using defaults
+ }
+ return handler;
+ }
+
+ public String http() {
+ return value(HTTP_KEY, DEFAULT_HTTP_PORT);
+ }
+
+ private String https() { // enough common to be exposed as method
+ return securedValue(HTTP_KEY, DEFAULT_HTTPS_PORT);
+ }
+
+ private String stop() {
+ return value(STOP_KEY, DEFAULT_STOP_PORT);
+ }
+
+ private String value(final String key, final String defaultValue) {
+ final String val = values.get(key);
+ if (val == null) {
+ return defaultValue;
+ }
+ return val;
+ }
+
+ private String securedValue(final String key, final String defaultValue) {
+ return value(key + SECURED_SUFFIX, defaultValue);
+ }
+ }
+
+ // there to be able to stack config later on without breaking all methods
+ public static class DeploymentMeta {
+ private final String context;
+ private final File docBase;
+ private final Consumer<Context> consumer;
+
+ public DeploymentMeta(final String context, final File docBase, final Consumer<Context> consumer) {
+ this.context = context;
+ this.docBase = docBase;
+ this.consumer = consumer;
+ }
+ }
+
+ // just to type it and allow some extensions to use a ServiceLoader
+ public interface ConfigurationCustomizer extends Consumer<Meecrowave.Builder> {
+ }
+
+ private static final class MeecrowaveContainerLoader extends URLClassLoader {
+ private MeecrowaveContainerLoader(final URL[] urls, final ClassLoader parent) {
+ super(urls, parent);
+ }
+ }
+}
Added: openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/MeecrowaveExplosion.java
URL: http://svn.apache.org/viewvc/openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/MeecrowaveExplosion.java?rev=1769479&view=auto
==============================================================================
--- openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/MeecrowaveExplosion.java (added)
+++ openwebbeans/microwave/trunk/meecrowave-core/src/main/java/org/apache/meecrowave/MeecrowaveExplosion.java Sun Nov 13 09:34:07 2016
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.meecrowave;
+
+public class MeecrowaveExplosion extends RuntimeException {
+ public MeecrowaveExplosion(final String msg, final Exception e) {
+ super(msg, e);
+ }
+}