| /* |
| * 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 static java.util.Collections.emptyList; |
| import static java.util.Collections.emptySet; |
| import static java.util.Comparator.comparing; |
| import static java.util.Locale.ROOT; |
| import static java.util.Optional.ofNullable; |
| import static java.util.stream.Collectors.toList; |
| import static java.util.stream.Collectors.toSet; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.StringReader; |
| import java.io.Writer; |
| import java.lang.management.ManagementFactory; |
| 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.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.StandardCopyOption; |
| import java.util.ArrayList; |
| import java.util.Base64; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Properties; |
| import java.util.ServiceLoader; |
| import java.util.Set; |
| import java.util.TreeMap; |
| import java.util.concurrent.ThreadLocalRandom; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.function.BiPredicate; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.stream.Stream; |
| import java.util.stream.StreamSupport; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.spec.SecretKeySpec; |
| import javax.enterprise.context.spi.CreationalContext; |
| import javax.enterprise.inject.spi.AnnotatedType; |
| import javax.enterprise.inject.spi.BeanManager; |
| import javax.enterprise.inject.spi.CDI; |
| import javax.enterprise.inject.spi.InjectionTarget; |
| import javax.servlet.ServletContainerInitializer; |
| import javax.servlet.SessionCookieConfig; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.parsers.SAXParserFactory; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Globals; |
| import org.apache.catalina.Host; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleState; |
| import org.apache.catalina.Pipeline; |
| import org.apache.catalina.Realm; |
| import org.apache.catalina.Server; |
| import org.apache.catalina.Valve; |
| import org.apache.catalina.connector.Connector; |
| import org.apache.catalina.core.StandardContext; |
| import org.apache.catalina.core.StandardHost; |
| import org.apache.catalina.session.StandardManager; |
| import org.apache.catalina.startup.Catalina; |
| import org.apache.catalina.startup.Tomcat; |
| import org.apache.catalina.util.StandardSessionIdGenerator; |
| import org.apache.coyote.http2.Http2Protocol; |
| import org.apache.meecrowave.api.StartListening; |
| import org.apache.meecrowave.api.StopListening; |
| import org.apache.meecrowave.configuration.Configuration; |
| import org.apache.meecrowave.cxf.ConfigurableBus; |
| import org.apache.meecrowave.cxf.CxfCdiAutoSetup; |
| import org.apache.meecrowave.cxf.Cxfs; |
| import org.apache.meecrowave.io.IO; |
| import org.apache.meecrowave.lang.Substitutor; |
| import org.apache.meecrowave.logging.jul.Log4j2Logger; |
| import org.apache.meecrowave.logging.log4j2.Log4j2Shutdown; |
| import org.apache.meecrowave.logging.log4j2.Log4j2s; |
| 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.service.Priotities; |
| import org.apache.meecrowave.service.ValueTransformer; |
| import org.apache.meecrowave.tomcat.CDIInstanceManager; |
| import org.apache.meecrowave.tomcat.LoggingAccessLogPattern; |
| import org.apache.meecrowave.tomcat.MeecrowaveContextConfig; |
| 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.modeler.Registry; |
| import org.apache.tomcat.util.net.SSLHostConfig; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.xbean.finder.ResourceFinder; |
| import org.apache.xbean.recipe.ObjectRecipe; |
| import org.apache.xbean.recipe.Option; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| public class Meecrowave implements AutoCloseable { |
| private final Configuration configuration; |
| protected ConfigurableBus clientBus; |
| protected File base; |
| protected final File ownedTempDir; |
| protected File workDir; |
| 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; |
| private boolean deleteBase; |
| |
| public Meecrowave() { |
| this(new Builder()); |
| } |
| |
| public Meecrowave(final Configuration configuration) { |
| this.configuration = configuration; |
| this.ownedTempDir = new File(this.configuration.getTempDir(), "meecrowave_" + System.nanoTime()); |
| } |
| |
| public Builder getConfiguration() { |
| return Builder.class.isInstance(configuration) ? |
| Builder.class.cast(configuration) : |
| new Builder(configuration); |
| } |
| |
| public File getBase() { |
| return base; |
| } |
| |
| public Tomcat getTomcat() { |
| return tomcat; |
| } |
| |
| public boolean isServing() { |
| return tomcat != null && tomcat.getHost().getState() == LifecycleState.STARTED; |
| } |
| |
| 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), meta.redeployCallback)); |
| } |
| |
| // shortcut |
| public Meecrowave deployClasspath() { |
| return deployClasspath(""); |
| } |
| |
| // shortcut |
| public Meecrowave bake(final Consumer<Context> customizer) { |
| start(); |
| return deployClasspath(new DeploymentMeta("", null, customizer, null)); |
| } |
| |
| // shortcut (used by plugins) |
| public Meecrowave deployClasspath(final String context) { |
| return deployClasspath(new DeploymentMeta(context, null, 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, 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 |
| final String base = tomcat.getService().findConnectors().length > 0 ? |
| (configuration.getActiveProtocol() + "://" + tomcat.getHost().getName() + ':' + configuration.getActivePort()) : ""; |
| new LogFacade(Meecrowave.class.getName()).info("--------------- " + base + meta.context); |
| |
| |
| final OWBJarScanner scanner = new OWBJarScanner(); |
| final StandardContext ctx = new StandardContext() { |
| @Override |
| public void setApplicationEventListeners(final Object[] listeners) { |
| if (listeners == null) { |
| super.setApplicationEventListeners(null); |
| return; |
| } |
| |
| // ensure owb is first and cxf is last otherwise surprises, |
| // if we don't -> no @RequestScoped in request listeners :( |
| for (int i = 1; i < listeners.length; i++) { |
| if (OWBAutoSetup.EagerBootListener.class.isInstance(listeners[i])) { |
| final Object first = listeners[0]; |
| listeners[0] = listeners[i]; |
| listeners[i] = first; |
| break; |
| } |
| } |
| |
| // and finally let it go after our re-ordering |
| super.setApplicationEventListeners(listeners); |
| } |
| }; |
| ctx.setPath(meta.context); |
| ctx.setName(meta.context); |
| ctx.setJarScanner(scanner); |
| ctx.setInstanceManager(new CDIInstanceManager()); |
| ofNullable(meta.docBase).ifPresent(d -> { |
| try { |
| ctx.setDocBase(meta.docBase.getCanonicalPath()); |
| } catch (final IOException e) { |
| ctx.setDocBase(meta.docBase.getAbsolutePath()); |
| } |
| }); |
| ofNullable(configuration.getTomcatFilter()).ifPresent(filter -> { |
| try { |
| scanner.setJarScanFilter(JarScanFilter.class.cast(Thread.currentThread().getContextClassLoader().loadClass(filter).newInstance())); |
| } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) { |
| throw new IllegalArgumentException(e); |
| } |
| }); |
| |
| final AtomicReference<Runnable> releaseSCI = new AtomicReference<>(); |
| final ServletContainerInitializer meecrowaveInitializer = (c, ctx1) -> { |
| ctx1.setAttribute("meecrowave.configuration", getConfiguration()); |
| ctx1.setAttribute("meecrowave.instance", Meecrowave.this); |
| |
| new OWBAutoSetup().onStartup(c, ctx1); |
| if (Cxfs.IS_PRESENT) { |
| new CxfCdiAutoSetup().onStartup(c, ctx1); |
| } |
| new TomcatAutoInitializer().onStartup(c, ctx1); |
| |
| if (configuration.isInjectServletContainerInitializer()) { |
| final Field f; |
| try { // now cdi is on, we can inject cdi beans in ServletContainerInitializer |
| f = StandardContext.class.getDeclaredField("initializers"); |
| if (!f.isAccessible()) { |
| f.setAccessible(true); |
| } |
| } catch (final Exception e) { |
| throw new IllegalStateException("Bad tomcat version", e); |
| } |
| |
| final List<AutoCloseable> cc; |
| try { |
| cc = ((Map<ServletContainerInitializer, Set<Class<?>>>) f.get(ctx)).keySet().stream() |
| .filter(i -> !i.getClass().getName().startsWith(Meecrowave.class.getName())) |
| .map(i -> { |
| try { |
| return this.inject(i); |
| } catch (final IllegalArgumentException iae) { |
| return null; |
| } |
| }) |
| .filter(Objects::nonNull) |
| .collect(toList()); |
| } catch (final IllegalAccessException e) { |
| throw new IllegalStateException("Can't read initializers", e); |
| } |
| releaseSCI.set(() -> cc.forEach(closeable -> { |
| try { |
| closeable.close(); |
| } catch (final Exception e) { |
| throw new IllegalStateException(e); |
| } |
| })); |
| } |
| }; |
| |
| ctx.addLifecycleListener(new MeecrowaveContextConfig(configuration, meta.docBase != null, meecrowaveInitializer, meta.redeployCallback)); |
| ctx.addLifecycleListener(event -> { |
| switch (event.getType()) { |
| case Lifecycle.BEFORE_START_EVENT: |
| if (configuration.getWebSessionCookieConfig() != null) { |
| final Properties p = new Properties(); |
| try { |
| p.load(new StringReader(configuration.getWebSessionCookieConfig())); |
| } catch (final IOException e) { |
| throw new IllegalArgumentException(e); |
| } |
| if (p.containsKey("domain")) { |
| ctx.setSessionCookieDomain(p.getProperty("domain")); |
| } |
| if (p.containsKey("path")) { |
| ctx.setSessionCookiePath(p.getProperty("path")); |
| } |
| if (p.containsKey("name")) { |
| ctx.setSessionCookieName(p.getProperty("name")); |
| } |
| if (p.containsKey("use-trailing-slash")) { |
| ctx.setSessionCookiePathUsesTrailingSlash(Boolean.parseBoolean(p.getProperty("use-trailing-slash"))); |
| } |
| if (p.containsKey("http-only")) { |
| ctx.setUseHttpOnly(Boolean.parseBoolean(p.getProperty("http-only"))); |
| } |
| if (p.containsKey("secured")) { |
| final SessionCookieConfig sessionCookieConfig = ctx.getServletContext().getSessionCookieConfig(); |
| sessionCookieConfig.setSecure(Boolean.parseBoolean(p.getProperty("secured"))); |
| } |
| } |
| break; |
| case Lifecycle.AFTER_START_EVENT: |
| ctx.getResources().setCachingAllowed(configuration.isWebResourceCached()); |
| break; |
| case Lifecycle.BEFORE_INIT_EVENT: |
| if (configuration.getLoginConfig() != null) { |
| ctx.setLoginConfig(configuration.getLoginConfig().build()); |
| } |
| for (final SecurityConstaintBuilder sc : configuration.getSecurityConstraints()) { |
| ctx.addConstraint(sc.build()); |
| } |
| if (configuration.getWebXml() != null) { |
| ctx.getServletContext().setAttribute(Globals.ALT_DD_ATTR, configuration.getWebXml()); |
| } |
| break; |
| default: |
| } |
| |
| }); |
| ctx.addLifecycleListener(new Tomcat.FixContextListener()); // after having configured the security!!! |
| |
| ctx.addServletContainerInitializer(meecrowaveInitializer, emptySet()); |
| |
| if (configuration.isUseTomcatDefaults()) { |
| ctx.setSessionTimeout(configuration.getWebSessionTimeout() != null ? configuration.getWebSessionTimeout() : 30); |
| ctx.addWelcomeFile("index.html"); |
| ctx.addWelcomeFile("index.htm"); |
| Tomcat.addDefaultMimeTypeMappings(ctx); |
| } else if (configuration.getWebSessionTimeout() != null) { |
| ctx.setSessionTimeout(configuration.getWebSessionTimeout()); |
| } |
| |
| ofNullable(meta.consumer).ifPresent(c -> c.accept(ctx)); |
| if (configuration.isQuickSession() && ctx.getManager() == null) { |
| final StandardManager manager = new StandardManager(); |
| manager.setSessionIdGenerator(new StandardSessionIdGenerator() { |
| @Override |
| protected void getRandomBytes(final byte bytes[]) { |
| ThreadLocalRandom.current().nextBytes(bytes); |
| } |
| |
| @Override |
| public String toString() { |
| return "MeecrowaveSessionIdGenerator@" + System.identityHashCode(this); |
| } |
| }); |
| ctx.setManager(manager); |
| } |
| if (configuration.isAntiResourceLocking() && StandardContext.class.isInstance(ctx)) { |
| StandardContext.class.cast(ctx).setAntiResourceLocking(true); |
| } |
| configuration.getInitializers().forEach(i -> ctx.addServletContainerInitializer(i, emptySet())); |
| configuration.getGlobalContextConfigurers().forEach(it -> it.accept(ctx)); |
| |
| final Host host = tomcat.getHost(); |
| host.addChild(ctx); |
| |
| final ClassLoader classLoader = ctx.getLoader().getClassLoader(); |
| if (host.getState().isAvailable()) { |
| fire(new StartListening(findFirstConnector(), host, ctx), classLoader); |
| } |
| contexts.put(meta.context, () -> { |
| if (host.getState().isAvailable()) { |
| fire(new StopListening(findFirstConnector(), host, ctx), classLoader); |
| } |
| ofNullable(releaseSCI.get()).ifPresent(Runnable::run); |
| tomcat.getHost().removeChild(ctx); |
| }); |
| return this; |
| } |
| |
| public Meecrowave bake() { |
| return bake(""); |
| } |
| |
| public Meecrowave bake(final String ctx) { |
| start(); |
| return deployClasspath(ctx); |
| } |
| |
| public Meecrowave start() { |
| final Map<String, String> systemPropsToRestore = new HashMap<>(); |
| |
| if (configuration.getMeecrowaveProperties() != null && !"meecrowave.properties".equals(configuration.getMeecrowaveProperties())) { |
| configuration.loadFrom(configuration.getMeecrowaveProperties()); |
| } |
| |
| if (configuration.isUseLog4j2JulLogManager() && Log4j2s.IS_PRESENT) { // /!\ don't move this line or add anything before without checking log setup |
| System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); |
| } |
| |
| if (configuration.isLoggingGlobalSetup() && Log4j2s.IS_PRESENT) { |
| |
| setSystemProperty(systemPropsToRestore, "log4j.shutdownHookEnabled", "false"); |
| setSystemProperty(systemPropsToRestore, "openwebbeans.logging.factory", Log4j2LoggerFactory.class.getName()); |
| setSystemProperty(systemPropsToRestore, "org.apache.cxf.Logger", Log4j2Logger.class.getName()); |
| setSystemProperty(systemPropsToRestore, "org.apache.tomcat.Logger", Log4j2Log.class.getName()); |
| |
| postTask = () -> { |
| if (Log4j2s.IS_PRESENT) { |
| new Log4j2Shutdown().shutdown(); |
| } |
| systemPropsToRestore.forEach((key, value) -> { |
| if (value == null) { |
| System.clearProperty(key); |
| } else { |
| System.setProperty(key, value); |
| } |
| }); |
| }; |
| } |
| |
| setupJmx(configuration.isTomcatNoJmx()); |
| |
| clearCatalinaSystemProperties = System.getProperty("catalina.base") == null && System.getProperty("catalina.home") == null; |
| |
| tomcat = new InternalTomcat(); |
| |
| { // setup |
| base = new File(newBaseDir()); |
| |
| // create the temp dir folder. |
| File tempDir; |
| if (configuration.getTempDir() == null || configuration.getTempDir().length() == 0) { |
| tempDir = createDirectory(base, "temp"); |
| } else { |
| tempDir = new File(configuration.getTempDir()); |
| if (!tempDir.exists()) { |
| tempDir.mkdirs(); |
| } |
| } |
| |
| try { |
| workDir = createDirectory(base, "work"); |
| } catch (final IllegalStateException ise) { |
| // in case we could not create that directory we create it in the temp dir folder |
| workDir = createDirectory(tempDir, "work"); |
| } |
| |
| synchronize(new File(base, "conf"), configuration.getConf()); |
| } |
| |
| final Properties props = configuration.getProperties(); |
| Substitutor 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.getHttpPort())); |
| placeHolders.put("meecrowave.embedded.https", Integer.toString(configuration.getHttpsPort())); |
| placeHolders.put("meecrowave.embedded.stop", Integer.toString(configuration.getStopPort())); |
| substitutor = new Substitutor(placeHolders); |
| } |
| props.put(s, substitutor.replace(v)); |
| } |
| } |
| |
| final File conf = new File(base, "conf"); |
| |
| tomcat.setBaseDir(base.getAbsolutePath()); |
| tomcat.setHostname(configuration.getHost()); |
| |
| final boolean initialized; |
| if (configuration.getServerXml() != null) { |
| final File file = new File(conf, "server.xml"); |
| if (!file.equals(configuration.getServerXml())) { |
| try (final InputStream is = new FileInputStream(configuration.getServerXml()); |
| 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.isKeepServerXmlAsThis()) { |
| configuration.setHttpPort(Integer.parseInt(ports.http())); |
| configuration.setStopPort(Integer.parseInt(ports.stop())); |
| } else { |
| final Map<String, String> replacements = new HashMap<>(); |
| replacements.put(ports.http(), String.valueOf(configuration.getHttpPort())); |
| replacements.put(ports.https(), String.valueOf(configuration.getHttpsPort())); |
| replacements.put(ports.stop(), String.valueOf(configuration.getStopPort())); |
| |
| 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.getStopPort()); |
| 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.getHost()); |
| tomcat.getEngine().setDefaultHost(configuration.getHost()); |
| final StandardHost host = new StandardHost(); |
| host.setName(configuration.getHost()); |
| |
| try { |
| final File webapps = createDirectory(base, "webapps"); |
| host.setAppBase(webapps.getAbsolutePath()); |
| } catch (final IllegalStateException ise) { |
| // never an issue since the webapps are deployed being put in webapps - so no dynamic folder |
| // or through their path - so don't need webapps folder |
| } |
| |
| host.setUnpackWARs(true); // forced for now cause OWB doesn't support war:file:// urls |
| try { |
| host.setWorkDir(workDir.getCanonicalPath()); |
| } catch (final IOException e) { |
| host.setWorkDir(workDir.getAbsolutePath()); |
| } |
| tomcat.setHost(host); |
| } |
| |
| ofNullable(configuration.getTomcatAccessLogPattern()) |
| .ifPresent(pattern -> tomcat.getHost().getPipeline().addValve(new LoggingAccessLogPattern(pattern))); |
| final List<Valve> valves = buildValves(); |
| if (!valves.isEmpty()) { |
| final Pipeline pipeline = tomcat.getHost().getPipeline(); |
| valves.forEach(pipeline::addValve); |
| } |
| |
| if (configuration.getRealm() != null) { |
| tomcat.getEngine().setRealm(configuration.getRealm()); |
| } |
| |
| if (tomcat.getRawConnector() == null && !configuration.isSkipHttp()) { |
| final Connector connector = createConnector(); |
| connector.setPort(configuration.getHttpPort()); |
| if (connector.getProperty("connectionTimeout") == null) { |
| connector.setProperty("connectionTimeout", "3000"); |
| } |
| |
| tomcat.getService().addConnector(connector); |
| tomcat.setConnector(connector); |
| } |
| |
| // create https connector |
| if (configuration.isSsl()) { |
| final Connector httpsConnector = createConnector(); |
| httpsConnector.setPort(configuration.getHttpsPort()); |
| httpsConnector.setSecure(true); |
| httpsConnector.setScheme("https"); |
| httpsConnector.setProperty("SSLEnabled", "true"); |
| if (configuration.getSslProtocol() != null) { |
| configuration.getProperties().setProperty("connector.sslhostconfig.sslProtocol", configuration.getSslProtocol()); |
| } |
| if (configuration.getProperties().getProperty("connector.sslhostconfig.hostName") != null) { |
| httpsConnector.setProperty("defaultSSLHostConfigName", configuration.getProperties().getProperty("connector.sslhostconfig.hostName")); |
| } |
| if (configuration.getKeystoreFile() != null) { |
| configuration.getProperties().setProperty("connector.sslhostconfig.certificateKeystoreFile", configuration.getKeystoreFile()); |
| } |
| if (configuration.getKeystorePass() != null) { |
| configuration.getProperties().setProperty("connector.sslhostconfig.certificateKeystorePassword", configuration.getKeystorePass()); |
| } |
| configuration.getProperties().setProperty("connector.sslhostconfig.certificateKeystoreType", configuration.getKeystoreType()); |
| if (configuration.getClientAuth() != null) { |
| httpsConnector.setProperty("clientAuth", configuration.getClientAuth()); |
| } |
| |
| if (configuration.getKeyAlias() != null) { |
| configuration.getProperties().setProperty("connector.sslhostconfig.certificateKeyAlias", configuration.getKeyAlias()); |
| } |
| if (configuration.isHttp2()) { |
| httpsConnector.addUpgradeProtocol(new Http2Protocol()); |
| } |
| final List<SSLHostConfig> buildSslHostConfig = buildSslHostConfig(); |
| if (!buildSslHostConfig.isEmpty()) { |
| createDirectory(base, "conf"); |
| } |
| buildSslHostConfig.forEach(sslHostConf -> { |
| if (isCertificateFromClasspath(sslHostConf.getCertificateKeystoreFile())) { |
| copyCertificateToConfDir(sslHostConf.getCertificateKeystoreFile()); |
| sslHostConf.setCertificateKeystoreFile(base.getAbsolutePath() + "/conf/" + sslHostConf.getCertificateKeystoreFile()); |
| } |
| if (isCertificateFromClasspath(sslHostConf.getCertificateKeyFile())) { |
| copyCertificateToConfDir(sslHostConf.getCertificateKeyFile()); |
| sslHostConf.setCertificateKeyFile(base.getAbsolutePath() + "/conf/" + sslHostConf.getCertificateKeyFile()); |
| copyCertificateToConfDir(sslHostConf.getCertificateFile()); |
| sslHostConf.setCertificateFile(base.getAbsolutePath() + "/conf/" + sslHostConf.getCertificateFile()); |
| } |
| if (isCertificateFromClasspath(sslHostConf.getTruststoreFile())) { |
| copyCertificateToConfDir(sslHostConf.getTruststoreFile()); |
| sslHostConf.setTruststoreFile(base.getAbsolutePath() + "/conf/" + sslHostConf.getTruststoreFile()); |
| } |
| if (isCertificateFromClasspath(sslHostConf.getCertificateChainFile())) { |
| copyCertificateToConfDir(sslHostConf.getCertificateChainFile()); |
| sslHostConf.setCertificateChainFile(base.getAbsolutePath() + "/conf/" + sslHostConf.getCertificateChainFile()); |
| } |
| }); |
| |
| buildSslHostConfig.forEach(httpsConnector::addSslHostConfig); |
| |
| if (configuration.getDefaultSSLHostConfigName() != null) { |
| httpsConnector.setProperty("defaultSSLHostConfigName", configuration.getDefaultSSLHostConfigName()); |
| } |
| tomcat.getService().addConnector(httpsConnector); |
| if (configuration.isSkipHttp()) { |
| tomcat.setConnector(httpsConnector); |
| } |
| } |
| |
| for (final Connector c : configuration.getConnectors()) { |
| tomcat.getService().addConnector(c); |
| } |
| if (!configuration.isSkipHttp() && !configuration.isSsl() && !configuration.getConnectors().isEmpty()) { |
| tomcat.setConnector(configuration.getConnectors().iterator().next()); |
| } |
| |
| if (configuration.getUsers() != null) { |
| for (final Map.Entry<String, String> user : configuration.getUsers().entrySet()) { |
| tomcat.addUser(user.getKey(), user.getValue()); |
| } |
| } |
| if (configuration.getRoles() != null) { |
| for (final Map.Entry<String, String> user : configuration.getRoles().entrySet()) { |
| for (final String role : user.getValue().split(" *, *")) { |
| tomcat.addRole(user.getKey(), role); |
| } |
| } |
| } |
| |
| StreamSupport.stream(ServiceLoader.load(Meecrowave.InstanceCustomizer.class).spliterator(), false) |
| .peek(i -> { |
| if (MeecrowaveAwareInstanceCustomizer.class.isInstance(i)) { |
| MeecrowaveAwareInstanceCustomizer.class.cast(i).setMeecrowave(this); |
| } |
| }) |
| .sorted(Priotities::sortByPriority) |
| .forEach(c -> c.accept(tomcat)); |
| configuration.getInstanceCustomizers().forEach(c -> c.accept(tomcat)); |
| |
| StreamSupport.stream(ServiceLoader.load(Meecrowave.ContextCustomizer.class).spliterator(), false) |
| .peek(i -> { |
| if (MeecrowaveAwareContextCustomizer.class.isInstance(i)) { |
| MeecrowaveAwareContextCustomizer.class.cast(i).setMeecrowave(this); |
| } |
| }) |
| .sorted(Priotities::sortByPriority) |
| .forEach(configuration::addGlobalContextCustomizer); |
| |
| beforeStart(); |
| |
| |
| if (configuration.isInitializeClientBus() && Cxfs.IS_PRESENT && !Cxfs.hasDefaultBus()) { |
| clientBus = new ConfigurableBus(); |
| clientBus.initProviders(configuration, |
| ofNullable(Thread.currentThread().getContextClassLoader()).orElseGet(ClassLoader::getSystemClassLoader)); |
| clientBus.addClientLifecycleListener(); |
| } |
| |
| try { |
| if (!initialized) { |
| tomcat.init(); |
| } |
| |
| tomcat.getHost().addLifecycleListener(event -> { |
| if (!Host.class.isInstance(event.getSource())) { |
| return; |
| } |
| broadcastHostEvent(event.getType(), Host.class.cast(event.getSource())); |
| }); |
| |
| tomcat.start(); |
| } catch (final LifecycleException e) { |
| throw new IllegalStateException(e); |
| } |
| ofNullable(configuration.getPidFile()).ifPresent(pidFile -> { |
| if (pidFile.getParentFile() != null && !pidFile.getParentFile().isDirectory() && !pidFile.getParentFile().mkdirs()) { |
| throw new IllegalArgumentException("Can't create " + pidFile); |
| } |
| final String pid = ManagementFactory.getRuntimeMXBean().getName(); |
| final int at = pid.indexOf('@'); |
| try (final Writer w = new FileWriter(pidFile)) { |
| w.write(at > 0 ? pid.substring(0, at) : pid); |
| } catch (final IOException e) { |
| throw new IllegalStateException("Can't write the pid in " + pid, 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 boolean isCertificateFromClasspath(final String certificate) { |
| final BiPredicate<String, String> equals = System.getProperty("os.name", "ignore").toLowerCase(ROOT).contains("win") ? |
| String::equalsIgnoreCase : String::equals; |
| return certificate != null && !(new File(certificate).exists()) |
| && !equals.test( |
| Paths.get(System.getProperty("user.home")).resolve(".keystore").toAbsolutePath().normalize().toString(), |
| Paths.get(certificate).toAbsolutePath().normalize().toString()); |
| } |
| |
| private void copyCertificateToConfDir(String certificate) { |
| InputStream resourceAsStream = null; |
| try { |
| final Path dstFile = Paths.get(base.getAbsolutePath() + "/conf/" + certificate); |
| resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(certificate); |
| if (resourceAsStream == null) { |
| resourceAsStream = new FileInputStream(new File((this.getClass().getResource("/").toString().replaceAll("file:", "") + "/" + certificate))); |
| } |
| Files.copy(resourceAsStream, dstFile, StandardCopyOption.REPLACE_EXISTING); |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } finally { |
| if (resourceAsStream != null) { |
| try { |
| resourceAsStream.close(); |
| } catch (IOException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| } |
| } |
| |
| public ConfigurableBus getClientBus() { |
| return clientBus; |
| } |
| |
| /** |
| * Store away the current system property for restoring it later |
| * during shutdown. |
| * |
| * @param backupPropertyMap a Map to store away the previous value before setting the newValue |
| * @param propertyKey |
| * @param newValue |
| */ |
| private void setSystemProperty(Map<String, String> backupPropertyMap, String propertyKey, String newValue) { |
| backupPropertyMap.put(propertyKey, System.getProperty(propertyKey)); |
| |
| System.setProperty(propertyKey, newValue); |
| } |
| |
| private void broadcastHostEvent(final String event, final Host host) { |
| switch (event) { |
| case Lifecycle.AFTER_START_EVENT: { |
| final Connector connector = findFirstConnector(); |
| findContexts(host).forEach(ctx -> fire(new StartListening(connector, host, ctx), ctx.getLoader().getClassLoader())); |
| break; |
| } |
| case Lifecycle.BEFORE_STOP_EVENT: { |
| final Connector connector = findFirstConnector(); |
| findContexts(host).forEach(ctx -> fire(new StopListening(connector, host, ctx), ctx.getLoader().getClassLoader())); |
| break; |
| } |
| default: |
| } |
| } |
| |
| private Connector findFirstConnector() { |
| return Stream.of(tomcat.getServer().findServices()) |
| .flatMap(s -> Stream.of(s.findConnectors())) |
| .findFirst() |
| .orElse(null); |
| } |
| |
| private Stream<Context> findContexts(final Host host) { |
| return Stream.of(host.findChildren()) |
| .filter(Context.class::isInstance) |
| .map(Context.class::cast) |
| .filter(ctx -> ctx.getState().isAvailable()); |
| } |
| |
| private <T> void fire(final T event, final ClassLoader classLoader) { |
| final Thread thread = Thread.currentThread(); |
| final ClassLoader loader = thread.getContextClassLoader(); |
| thread.setContextClassLoader(classLoader); |
| try { |
| WebBeansContext.currentInstance() |
| .getBeanManagerImpl() |
| .getEvent() |
| .select(Class.class.cast(event.getClass())) |
| .fire(event); |
| } finally { |
| thread.setContextClassLoader(loader); |
| } |
| } |
| |
| private void setupJmx(final boolean skip) { |
| if (skip) { |
| Registry.disableRegistry(); |
| } |
| } |
| |
| /** |
| * Syntax uses: |
| * <code> |
| * valves.myValve1._className = org.apache.meecrowave.tomcat.LoggingAccessLogPattern |
| * valves.myValve1._order = 0 |
| * <p> |
| * valves.myValve1._className = SSOVa |
| * valves.myValve1._order = 1 |
| * valves.myValve1.showReportInfo = false |
| * </code> |
| * |
| * @return the list of valve from the properties. |
| */ |
| private List<Valve> buildValves() { |
| final List<Valve> valves = new ArrayList<>(); |
| configuration.getProperties().stringPropertyNames().stream() |
| .filter(key -> key.startsWith("valves.") && key.endsWith("._className")) |
| .sorted(comparing(key -> Integer.parseInt(configuration.getProperties() |
| .getProperty(key.replaceFirst("\\._className$", "._order"), "0")))) |
| .map(key -> key.split("\\.")) |
| .filter(parts -> parts.length == 3) |
| .forEach(key -> { |
| final String prefix = key[0] + '.' + key[1] + '.'; |
| final ObjectRecipe recipe = newRecipe(configuration.getProperties().getProperty(prefix + key[2])); |
| configuration.getProperties().stringPropertyNames().stream() |
| .filter(it -> it.startsWith(prefix) && !it.endsWith("._order") && !it.endsWith("._className")) |
| .forEach(propKey -> { |
| final String value = configuration.getProperties().getProperty(propKey); |
| recipe.setProperty(propKey.substring(prefix.length()), value); |
| }); |
| valves.add(Valve.class.cast(recipe.create(Thread.currentThread().getContextClassLoader()))); |
| }); |
| return valves; |
| } |
| |
| private List<SSLHostConfig> buildSslHostConfig() { |
| final List<SSLHostConfig> sslHostConfigs = new ArrayList<>(); |
| // Configures default SSLHostConfig |
| final ObjectRecipe defaultSslHostConfig = newRecipe(SSLHostConfig.class.getName()); |
| for (final String key : configuration.getProperties().stringPropertyNames()) { |
| if (key.startsWith("connector.sslhostconfig.") && key.split("\\.").length == 3) { |
| final String substring = key.substring("connector.sslhostconfig.".length()); |
| defaultSslHostConfig.setProperty(substring, configuration.getProperties().getProperty(key)); |
| } |
| } |
| if (!defaultSslHostConfig.getProperties().isEmpty()) { |
| sslHostConfigs.add(SSLHostConfig.class.cast(defaultSslHostConfig.create())); |
| } |
| // Allows to add N Multiple SSLHostConfig elements not including the default one. |
| final Collection<Integer> itemNumbers = configuration.getProperties().stringPropertyNames() |
| .stream() |
| .filter(key -> (key.startsWith("connector.sslhostconfig.") && key.split("\\.").length == 4)) |
| .map(key -> Integer.parseInt(key.split("\\.")[2])) |
| .collect(toSet()); |
| itemNumbers.stream().sorted().forEach(itemNumber -> { |
| final ObjectRecipe recipe = newRecipe(SSLHostConfig.class.getName()); |
| final String prefix = "connector.sslhostconfig." + itemNumber + '.'; |
| configuration.getProperties().stringPropertyNames().stream() |
| .filter(k -> k.startsWith(prefix)) |
| .forEach(key -> { |
| final String keyName = key.split("\\.")[3]; |
| recipe.setProperty(keyName, configuration.getProperties().getProperty(key)); |
| }); |
| if (!recipe.getProperties().isEmpty()) { |
| final SSLHostConfig sslHostConfig = SSLHostConfig.class.cast(recipe.create()); |
| sslHostConfigs.add(sslHostConfig); |
| new LogFacade(Meecrowave.class.getName()) |
| .info("Created SSLHostConfig #" + itemNumber + " (" + sslHostConfig.getHostName() + ")"); |
| } |
| }); |
| return sslHostConfigs; |
| } |
| |
| protected void beforeStart() { |
| // no-op |
| } |
| |
| protected void beforeStop() { |
| // no-op |
| } |
| |
| public <T> AutoCloseable inject(final T instance) { |
| final BeanManager bm = CDI.current().getBeanManager(); |
| final AnnotatedType<?> annotatedType = bm.createAnnotatedType(instance.getClass()); |
| final InjectionTarget injectionTarget = bm.createInjectionTarget(annotatedType); |
| final CreationalContext<Object> creationalContext = bm.createCreationalContext(null); |
| injectionTarget.inject(instance, creationalContext); |
| return creationalContext::release; |
| } |
| |
| @Override |
| public synchronized 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 { |
| Cxfs.resetDefaultBusIfEquals(clientBus); // after if runnables or listeners trigger CXF |
| tomcat = null; // ensure we can call close() N times and not have side effects |
| contexts.clear(); |
| 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 { |
| if (deleteBase && base != null) { |
| IO.delete(base); |
| } |
| |
| if (ownedTempDir != null) { |
| IO.delete(ownedTempDir); |
| } |
| } catch (final IllegalArgumentException /*does not exist from the hook*/ e) { |
| // no-op |
| } finally { |
| base = null; |
| |
| // not very important if we can't delete it since next restart will write another value normally |
| ofNullable(configuration.getPidFile()).ifPresent(File::delete); |
| } |
| } |
| } |
| } |
| |
| protected Connector createConnector() { |
| final Connector connector; |
| final Properties properties = configuration.getProperties(); |
| if (properties != null) { |
| final Map<String, String> attributes = new HashMap<>(); |
| final ObjectRecipe recipe = newRecipe(Connector.class.getName()); |
| 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.setProperty(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 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()); |
| final File parentFile = to.getParentFile(); |
| createDirectory(parentFile.getParentFile(), parentFile.getName()); |
| 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() { |
| deleteBase = false; |
| String dir = configuration.getDir(); |
| if (dir != null) { |
| final File dirFile = new File(dir); |
| if (dirFile.exists()) { |
| if (base != null && base.exists() && configuration.isDeleteBaseOnStartup()) { |
| IO.delete(base); |
| } |
| return dir; |
| } |
| IO.mkdirs(dirFile); |
| return dirFile.getAbsolutePath(); |
| } |
| |
| final String base = System.getProperty("meecrowave.base"); |
| if (base != null && new File(base).exists()) { |
| return new File(base).getAbsolutePath(); |
| } |
| |
| deleteBase = true; |
| final List<String> lookupPaths = new ArrayList<>(); |
| lookupPaths.add("target"); |
| lookupPaths.add("build"); |
| final File file = lookupPaths.stream() |
| .map(File::new) |
| .filter(File::isDirectory) |
| .findFirst() |
| .map(file1 -> new File(file1, "meecrowave-" + System.nanoTime())).orElse(ownedTempDir); |
| IO.mkdirs(file); |
| return file.getAbsolutePath(); |
| } |
| |
| public void await() { |
| tomcat.getServer().await(); |
| } |
| |
| private static ObjectRecipe newRecipe(final String clazz) { |
| final ObjectRecipe recipe = new ObjectRecipe(clazz); |
| recipe.allow(Option.FIELD_INJECTION); |
| recipe.allow(Option.PRIVATE_PROPERTIES); |
| return recipe; |
| } |
| |
| // this class holds all the built-in config, |
| // extension can use extensions feature (see cli.html) which is basically the same kind of bean |
| // accessible through builder.getExtension(type) builder being accessible through the meecrowave.configuration |
| // attribute of the ServletContext. |
| public static class Builder extends Configuration /* inheritance for backward compatibility */ { |
| // IMPORTANT: this must stay without any field, use parent class to add config please |
| // this only holds builder methods |
| |
| public Builder() { |
| // no-op |
| } |
| |
| // mainly for backward compat |
| public Builder(final Configuration configuration) { |
| super(configuration); |
| } |
| |
| @Override |
| public Builder loadFrom(String resource) { |
| super.loadFrom(resource); |
| return this; |
| } |
| |
| public Builder excludePackages(final String packages) { |
| this.setScanningPackageExcludes(packages); |
| return this; |
| } |
| |
| /** |
| * Only scan the very packages given (startsWith). |
| * This will exclude <em>all</em> other packages from bean scanning |
| */ |
| public Builder includePackages(final String packages) { |
| this.setScanningPackageIncludes(packages); |
| this.setScanningPackageExcludes("*"); |
| return this; |
| } |
| |
| /** |
| * Scan the very packages given (startsWith) <em>in addition</em> to the default rules. |
| */ |
| public Builder withPackages(final String packages) { |
| this.setScanningPackageIncludes(packages); |
| return this; |
| } |
| |
| public Builder sharedLibraries(final String sharedLibraries) { |
| setSharedLibraries(sharedLibraries); |
| return this; |
| } |
| |
| public Builder securityConstraints(final Meecrowave.SecurityConstaintBuilder securityConstraint) { |
| if (getSecurityConstraints() == null) { |
| setSecurityConstraints(new ArrayList<>()); |
| } |
| getSecurityConstraints().add(securityConstraint); |
| return this; |
| } |
| |
| public Builder randomHttpPort() { |
| try (final ServerSocket serverSocket = new ServerSocket(0)) { |
| setHttpPort(serverSocket.getLocalPort()); |
| } catch (final IOException e) { |
| throw new IllegalStateException(e); |
| } |
| return this; |
| } |
| |
| public Builder randomHttpsPort() { |
| try (final ServerSocket serverSocket = new ServerSocket(0)) { |
| setHttpsPort(serverSocket.getLocalPort()); |
| } catch (final IOException e) { |
| throw new IllegalStateException(e); |
| } |
| return this; |
| } |
| |
| public Builder property(final String key, final String value) { |
| getProperties().setProperty(key, value); |
| return this; |
| } |
| |
| |
| public Builder user(final String name, final String pwd) { |
| if (getUsers() == null) { |
| setUsers(new HashMap<>()); |
| } |
| getUsers().put(name, pwd); |
| return this; |
| } |
| |
| public Builder role(final String user, final String roles) { |
| if (getRoles() == null) { |
| setRoles(new HashMap<>()); |
| } |
| getRoles().put(user, roles); |
| return this; |
| } |
| |
| public Builder cxfServletParam(final String key, final String value) { |
| if (getCxfServletParams() == null) { |
| setCxfServletParams(new HashMap<>()); |
| } |
| getCxfServletParams().put(key, value); |
| return this; |
| } |
| |
| @Deprecated // withers are deprecated, we prefer the no fluent API |
| public Builder withJsonpBufferStrategy(final String jsonpBufferStrategy) { |
| setJsonpBufferStrategy(jsonpBufferStrategy); |
| return this; |
| } |
| |
| public Builder noShutdownHook() { |
| setUseShutdownHook(false); |
| return this; |
| } |
| |
| public Builder instanceCustomizer(final Consumer<Tomcat> customizer) { |
| addInstanceCustomizer(customizer); |
| return this; |
| } |
| |
| public Builder pidFile(final Path pidFile) { |
| setPidFile(pidFile.toFile()); |
| return this; |
| } |
| |
| public Builder watcherBouncing(final int watcherBouncing) { |
| setWatcherBouncing(watcherBouncing); |
| return this; |
| } |
| |
| public Builder httpPort(final int httpPort) { |
| if (httpPort <= 0) { |
| randomHttpPort(); |
| } else { |
| setHttpPort(httpPort); |
| } |
| return this; |
| } |
| |
| public Builder httpsPort(final int httpsPort) { |
| if (httpsPort <= 0) { |
| randomHttpsPort(); |
| } else { |
| setHttpsPort(httpsPort); |
| } |
| return this; |
| } |
| |
| public Builder stopPort(final int stopPort) { |
| setStopPort(stopPort); |
| return this; |
| } |
| |
| public Builder host(final String host) { |
| setHost(host); |
| return this; |
| } |
| |
| public Builder dir(final String dir) { |
| setDir(dir); |
| return this; |
| } |
| |
| public Builder serverXml(final Path serverXml) { |
| setServerXml(serverXml.toFile()); |
| return this; |
| } |
| |
| public Builder keepServerXmlAsThis(final boolean keepServerXmlAsThis) { |
| setKeepServerXmlAsThis(keepServerXmlAsThis); |
| return this; |
| } |
| |
| public Builder properties(final Properties properties) { |
| getProperties().putAll(properties); |
| return this; |
| } |
| |
| public Builder quickSession(final boolean quickSession) { |
| setQuickSession(quickSession); |
| return this; |
| } |
| |
| public Builder skipHttp(final boolean skipHttp) { |
| setSkipHttp(skipHttp); |
| return this; |
| } |
| |
| public Builder ssl(final boolean ssl) { |
| setSsl(ssl); |
| return this; |
| } |
| |
| public Builder keystoreFile(final String keystoreFile) { |
| setKeystoreFile(keystoreFile); |
| return this; |
| } |
| |
| public Builder keystorePass(final String keystorePass) { |
| setKeystorePass(keystorePass); |
| return this; |
| } |
| |
| public Builder keystoreType(final String keystoreType) { |
| setKeystoreType(keystoreType); |
| return this; |
| } |
| |
| public Builder clientAuth(final String clientAuth) { |
| setClientAuth(clientAuth); |
| return this; |
| } |
| |
| public Builder keyAlias(final String keyAlias) { |
| setKeyAlias(keyAlias); |
| return this; |
| } |
| |
| public Builder sslProtocol(final String sslProtocol) { |
| setSslProtocol(sslProtocol); |
| return this; |
| } |
| |
| public Builder webXml(final String webXml) { |
| setWebXml(webXml); |
| return this; |
| } |
| |
| public Builder loginConfig(final LoginConfigBuilder loginConfig) { |
| setLoginConfig(loginConfig); |
| return this; |
| } |
| |
| public Builder securityConstraint(final SecurityConstaintBuilder securityConstraint) { |
| if (getSecurityConstraints() == null) { |
| setSecurityConstraints(new ArrayList<>()); |
| } |
| getSecurityConstraints().add(securityConstraint); |
| return this; |
| } |
| |
| public Builder realm(final Realm realm) { |
| setRealm(realm); |
| return this; |
| } |
| |
| public Builder users(final Map<String, String> users) { |
| if (getUsers() == null) { |
| setUsers(new HashMap<>()); |
| } |
| getUsers().putAll(users); |
| return this; |
| } |
| |
| public Builder roles(final Map<String, String> roles) { |
| if (getRoles() == null) { |
| setRoles(new HashMap<>()); |
| } |
| getRoles().putAll(roles); |
| return this; |
| } |
| |
| public Builder http2(final boolean http2) { |
| setHttp2(http2); |
| return this; |
| } |
| |
| public Builder connector(final Connector connector) { |
| getConnectors().add(connector); |
| return this; |
| } |
| |
| public Builder tempDir(final String tempDir) { |
| setTempDir(tempDir); |
| return this; |
| } |
| |
| public Builder webResourceCached(final boolean webResourceCached) { |
| setWebResourceCached(webResourceCached); |
| return this; |
| } |
| |
| public Builder conf(final String conf) { |
| setConf(conf); |
| return this; |
| } |
| |
| public Builder deleteBaseOnStartup(final boolean deleteBaseOnStartup) { |
| setDeleteBaseOnStartup(deleteBaseOnStartup); |
| return this; |
| } |
| |
| public Builder jaxrsMapping(final String jaxrsMapping) { |
| setJaxrsMapping(jaxrsMapping); |
| return this; |
| } |
| |
| public Builder cdiConversation(final boolean cdiConversation) { |
| setCdiConversation(cdiConversation); |
| return this; |
| } |
| |
| public Builder jaxrsProviderSetup(final boolean jaxrsProviderSetup) { |
| setJaxrsProviderSetup(jaxrsProviderSetup); |
| return this; |
| } |
| |
| public Builder jaxrsDefaultProviders(final String jaxrsDefaultProviders) { |
| setJaxrsDefaultProviders(jaxrsDefaultProviders); |
| return this; |
| } |
| |
| public Builder jaxrsAutoActivateBeanValidation(final boolean jaxrsAutoActivateBeanValidation) { |
| setJaxrsAutoActivateBeanValidation(jaxrsAutoActivateBeanValidation); |
| return this; |
| } |
| |
| public Builder jaxrsLogProviders(final boolean jaxrsLogProviders) { |
| setJaxrsLogProviders(jaxrsLogProviders); |
| return this; |
| } |
| |
| public Builder jsonpBufferStrategy(final String jsonpBufferStrategy) { |
| setJsonpBufferStrategy(jsonpBufferStrategy); |
| return this; |
| } |
| |
| public Builder jsonpMaxStringLen(final int jsonpMaxStringLen) { |
| setJsonpMaxStringLen(jsonpMaxStringLen); |
| return this; |
| } |
| |
| public Builder jsonpMaxReadBufferLen(final int jsonpMaxReadBufferLen) { |
| setJsonpMaxReadBufferLen(jsonpMaxReadBufferLen); |
| return this; |
| } |
| |
| public Builder jsonpMaxWriteBufferLen(final int jsonpMaxWriteBufferLen) { |
| setJsonpMaxWriteBufferLen(jsonpMaxWriteBufferLen); |
| return this; |
| } |
| |
| public Builder jsonpSupportsComment(final boolean jsonpSupportsComment) { |
| setJsonpSupportsComment(jsonpSupportsComment); |
| return this; |
| } |
| |
| public Builder jsonpPrettify(final boolean jsonpPrettify) { |
| setJsonpPrettify(jsonpPrettify); |
| return this; |
| } |
| |
| public Builder jsonbEncoding(final String jsonbEncoding) { |
| setJsonbEncoding(jsonbEncoding); |
| return this; |
| } |
| |
| public Builder jsonbNulls(final boolean jsonbNulls) { |
| setJsonbNulls(jsonbNulls); |
| return this; |
| } |
| |
| public Builder jsonbIJson(final boolean jsonbIJson) { |
| setJsonbIJson(jsonbIJson); |
| return this; |
| } |
| |
| public Builder jsonbPrettify(final boolean jsonbPrettify) { |
| setJsonbPrettify(jsonbPrettify); |
| return this; |
| } |
| |
| public Builder jsonbBinaryStrategy(final String jsonbBinaryStrategy) { |
| setJsonbBinaryStrategy(jsonbBinaryStrategy); |
| return this; |
| } |
| |
| public Builder jsonbNamingStrategy(final String jsonbNamingStrategy) { |
| setJsonbNamingStrategy(jsonbNamingStrategy); |
| return this; |
| } |
| |
| public Builder jsonbOrderStrategy(final String jsonbOrderStrategy) { |
| setJsonbOrderStrategy(jsonbOrderStrategy); |
| return this; |
| } |
| |
| public Builder loggingGlobalSetup(final boolean loggingGlobalSetup) { |
| setLoggingGlobalSetup(loggingGlobalSetup); |
| return this; |
| } |
| |
| public Builder tomcatScanning(final boolean tomcatScanning) { |
| setTomcatScanning(tomcatScanning); |
| return this; |
| } |
| |
| public Builder tomcatAutoSetup(final boolean tomcatAutoSetup) { |
| setTomcatAutoSetup(tomcatAutoSetup); |
| return this; |
| } |
| |
| public Builder tomcatJspDevelopment(final boolean tomcatJspDevelopment) { |
| setTomcatJspDevelopment(tomcatJspDevelopment); |
| return this; |
| } |
| |
| public Builder useShutdownHook(final boolean useShutdownHook) { |
| setUseShutdownHook(useShutdownHook); |
| return this; |
| } |
| |
| public Builder tomcatFilter(final String tomcatFilter) { |
| setTomcatFilter(tomcatFilter); |
| return this; |
| } |
| |
| public Builder scanningIncludes(final String scanningIncludes) { |
| setScanningIncludes(scanningIncludes); |
| return this; |
| } |
| |
| public Builder scanningExcludes(final String scanningExcludes) { |
| setScanningExcludes(scanningExcludes); |
| return this; |
| } |
| |
| public Builder scanningPackageIncludes(final String scanningPackageIncludes) { |
| setScanningPackageIncludes(scanningPackageIncludes); |
| return this; |
| } |
| |
| public Builder scanningPackageIncludes(final Collection<String> scanningPackageIncludes) { |
| setScanningPackageIncludes(String.join(",", scanningPackageIncludes)); |
| return this; |
| } |
| |
| public Builder scanningPackageExcludes(final String scanningPackageExcludes) { |
| setScanningPackageExcludes(scanningPackageExcludes); |
| return this; |
| } |
| |
| public Builder scanningPackageExcludes(final Collection<String> scanningPackageExcludes) { |
| setScanningPackageExcludes(String.join(",", scanningPackageExcludes)); |
| return this; |
| } |
| |
| public Builder webSessionTimeout(final int webSessionTimeout) { |
| setWebSessionTimeout(webSessionTimeout); |
| return this; |
| } |
| |
| public Builder webSessionCookieConfig(final String webSessionCookieConfig) { |
| setWebSessionCookieConfig(webSessionCookieConfig); |
| return this; |
| } |
| |
| public Builder useTomcatDefaults(final boolean useTomcatDefaults) { |
| setUseTomcatDefaults(useTomcatDefaults); |
| return this; |
| } |
| |
| public Builder tomcatWrapLoader(final boolean tomcatWrapLoader) { |
| setTomcatWrapLoader(tomcatWrapLoader); |
| return this; |
| } |
| |
| public Builder tomcatNoJmx(final boolean tomcatNoJmx) { |
| setTomcatNoJmx(tomcatNoJmx); |
| return this; |
| } |
| |
| public Builder useLog4j2JulLogManager(final boolean useLog4j2JulLogManager) { |
| setUseLog4j2JulLogManager(useLog4j2JulLogManager); |
| return this; |
| } |
| |
| public Builder injectServletContainerInitializer(final boolean injectServletContainerInitializer) { |
| setInjectServletContainerInitializer(injectServletContainerInitializer); |
| return this; |
| } |
| |
| public Builder tomcatAccessLogPattern(final String tomcatAccessLogPattern) { |
| setTomcatAccessLogPattern(tomcatAccessLogPattern); |
| return this; |
| } |
| |
| public Builder meecrowaveProperties(final String meecrowaveProperties) { |
| setMeecrowaveProperties(meecrowaveProperties); |
| return this; |
| } |
| |
| public Builder jaxwsSupportIfAvailable(final boolean jaxwsSupportIfAvailable) { |
| setJaxwsSupportIfAvailable(jaxwsSupportIfAvailable); |
| return this; |
| } |
| |
| public Builder defaultSSLHostConfigName(final String defaultSSLHostConfigName) { |
| setDefaultSSLHostConfigName(defaultSSLHostConfigName); |
| return this; |
| } |
| |
| public Builder initializeClientBus(final boolean initializeClientBus) { |
| setInitializeClientBus(initializeClientBus); |
| return this; |
| } |
| |
| public Builder instanceCustomizers(final Consumer<Tomcat> instanceCustomizer) { |
| addInstanceCustomizer(instanceCustomizer); |
| return this; |
| } |
| |
| public Builder initializer(final ServletContainerInitializer initializer) { |
| addServletContextInitializer(initializer); |
| return this; |
| } |
| |
| public Builder antiResourceLocking(final boolean antiResourceLocking) { |
| setAntiResourceLocking(antiResourceLocking); |
| return this; |
| } |
| |
| public Builder contextConfigurer(final Consumer<Context> contextConfigurers) { |
| addGlobalContextCustomizer(contextConfigurers); |
| return this; |
| } |
| } |
| |
| public static class ValueTransformers implements Function<String, String> { |
| private final Map<String, ValueTransformer> transformers = new HashMap<>(); |
| |
| @Override |
| public String apply(final String value) { |
| if (value.startsWith("decode:")) { |
| if (transformers.isEmpty()) { // lazy loading |
| transformers.put("Static3DES", new ValueTransformer() { // compatibility with tomee |
| private final SecretKeySpec key = new SecretKeySpec(new byte[]{ |
| (byte) 0x76, (byte) 0x6F, (byte) 0xBA, (byte) 0x39, (byte) 0x31, |
| (byte) 0x2F, (byte) 0x0D, (byte) 0x4A, (byte) 0xA3, (byte) 0x90, |
| (byte) 0x55, (byte) 0xFE, (byte) 0x55, (byte) 0x65, (byte) 0x61, |
| (byte) 0x13, (byte) 0x34, (byte) 0x82, (byte) 0x12, (byte) 0x17, |
| (byte) 0xAC, (byte) 0x77, (byte) 0x39, (byte) 0x19}, "DESede"); |
| |
| @Override |
| public String name() { |
| return "Static3DES"; |
| } |
| |
| @Override |
| public String apply(final String encodedPassword) { |
| Objects.requireNonNull(encodedPassword, "value can't be null"); |
| try { |
| final byte[] cipherText = Base64.getDecoder().decode(encodedPassword); |
| final Cipher cipher = Cipher.getInstance("DESede"); |
| cipher.init(Cipher.DECRYPT_MODE, key); |
| return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8); |
| } catch (final Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| }); |
| for (final ValueTransformer t : ServiceLoader.load(ValueTransformer.class)) { |
| transformers.put(t.name(), t); |
| } |
| } |
| |
| final String substring = value.substring("decode:".length()); |
| final int sep = substring.indexOf(':'); |
| if (sep < 0) { |
| throw new IllegalArgumentException("No transformer algorithm for " + value); |
| } |
| final String algo = substring.substring(0, sep); |
| return Objects.requireNonNull(transformers.get(algo), "No ValueTransformer for value '" + value + "'").apply(substring.substring(sep + 1)); |
| } |
| return value; |
| } |
| } |
| |
| 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 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; |
| private final Consumer<Context> redeployCallback; |
| |
| public DeploymentMeta(final String context, final File docBase, final Consumer<Context> consumer, final Consumer<Context> redeployCallback) { |
| this.context = context; |
| this.docBase = docBase; |
| this.consumer = consumer; |
| this.redeployCallback = redeployCallback; |
| } |
| } |
| |
| // just to type it and allow some extensions to use a ServiceLoader |
| public interface ConfigurationCustomizer extends Consumer<Configuration> { |
| } |
| |
| /** |
| * SPI to customize Tomcat instance. They are sorted by Priority, default being 0. |
| */ |
| public interface InstanceCustomizer extends Consumer<Tomcat> { |
| } |
| |
| /** |
| * SPI to customize context instances. They are sorted by Priority, default being 0. |
| */ |
| public interface ContextCustomizer extends Consumer<Context> { |
| } |
| |
| public interface MeecrowaveAwareContextCustomizer extends ContextCustomizer { |
| void setMeecrowave(Meecrowave meecrowave); |
| } |
| |
| // since it is too early to have CDI and lookup the instance we must set it manually |
| public interface MeecrowaveAwareInstanceCustomizer extends InstanceCustomizer { |
| void setMeecrowave(Meecrowave meecrowave); |
| } |
| |
| private static final class MeecrowaveContainerLoader extends URLClassLoader { |
| private MeecrowaveContainerLoader(final URL[] urls, final ClassLoader parent) { |
| super(urls, parent); |
| } |
| } |
| } |