| /* |
| * 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.tomcat; |
| |
| import static java.util.Collections.emptySet; |
| import static java.util.Collections.singleton; |
| import static java.util.Optional.ofNullable; |
| |
| 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 javax.servlet.ServletContainerInitializer; |
| import javax.servlet.annotation.HandlesTypes; |
| import javax.servlet.annotation.WebFilter; |
| import javax.servlet.annotation.WebListener; |
| import javax.servlet.annotation.WebServlet; |
| |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleEvent; |
| import org.apache.catalina.startup.ContextConfig; |
| import org.apache.catalina.startup.WebappServiceLoader; |
| import org.apache.meecrowave.Meecrowave; |
| import org.apache.meecrowave.configuration.Configuration; |
| import org.apache.meecrowave.logging.tomcat.LogFacade; |
| import org.apache.meecrowave.openwebbeans.OWBTomcatWebScannerService; |
| import org.apache.meecrowave.watching.ReloadOnChangeController; |
| import org.apache.tomcat.JarScanner; |
| import org.apache.tomcat.util.bcel.classfile.ClassParser; |
| 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; |
| |
| public class MeecrowaveContextConfig extends ContextConfig { |
| private static final byte[] DEFAULT_WEB_XML = "<web-app version=\"3.1\" />".getBytes(StandardCharsets.UTF_8); |
| |
| private final Configuration configuration; |
| private final Map<String, Collection<Class<?>>> webClasses = new HashMap<>(); |
| private final boolean fixDocBase; |
| private final ServletContainerInitializer intializer; |
| private OwbAnnotationFinder finder; |
| private ReloadOnChangeController watcher; |
| |
| public MeecrowaveContextConfig(final Configuration configuration, final boolean fixDocBase, final ServletContainerInitializer intializer) { |
| this.configuration = configuration; |
| this.fixDocBase = fixDocBase; |
| this.intializer= intializer; |
| } |
| |
| @Override |
| protected void fixDocBase() throws IOException { |
| if (!fixDocBase) { |
| return; |
| } |
| super.fixDocBase(); |
| } |
| |
| @Override |
| protected void webConfig() { |
| if (context.getServletContext().getAttribute("meecrowave.configuration") == null) { // redeploy |
| context.getServletContext().setAttribute("meecrowave.configuration", |
| Meecrowave.Builder.class.isInstance(configuration) ? configuration : new Meecrowave.Builder(configuration)); |
| context.addServletContainerInitializer(intializer, emptySet()); |
| } |
| |
| 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()); |
| scannerService.setFilter(ofNullable(context.getJarScanner()).map(JarScanner::getJarScanFilter).orElse(null), context.getServletContext()); |
| scannerService.setDocBase(context.getDocBase()); |
| scannerService.setShared(configuration.getSharedLibraries()); |
| if (configuration.getWatcherBouncing() > 0) { // note that caching should be disabled with this config in most of the times |
| watcher = new ReloadOnChangeController(context, configuration.getWatcherBouncing()); |
| scannerService.setFileVisitor(f -> watcher.register(f)); |
| } |
| 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 |
| protected void processClasses(final WebXml webXml, final Set<WebXml> orderedFragments) { |
| final ClassLoader loader = context.getLoader().getClassLoader(); |
| orderedFragments.forEach(fragment -> { |
| final WebXml annotations = new WebXml(); |
| annotations.setDistributable(true); |
| final URL url = fragment.getURL(); |
| final Collection<Class<?>> classes = webClasses.get(url.toExternalForm()); |
| if (classes == null) { |
| return; |
| } |
| classes.forEach(clazz -> { |
| try (final InputStream stream = loader.getResourceAsStream(clazz.getName().replace('.', '/') + ".class")) { |
| processClass(annotations, new ClassParser(stream).parse()); |
| } catch (final IOException e) { |
| new LogFacade(MeecrowaveContextConfig.class.getName()).error("Can't parse " + clazz); |
| } |
| }); |
| fragment.merge(singleton(annotations)); |
| }); |
| } |
| |
| @Override |
| public void lifecycleEvent(final LifecycleEvent event) { |
| super.lifecycleEvent(event); |
| if (watcher != null && watcher.shouldRun() && Context.class.cast(event.getLifecycle()) == context) { |
| if (Lifecycle.AFTER_START_EVENT.equals(event.getType())) { |
| watcher.start(); |
| } else if (Lifecycle.BEFORE_STOP_EVENT.equals(event.getType())) { |
| watcher.close(); |
| } |
| } |
| } |
| |
| @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 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); |
| classes.addAll(finder.findAnnotatedClasses(annotation)); |
| } else if (t.isInterface()) { |
| classes.addAll(finder.findImplementations(t)); |
| } else { |
| classes.addAll(finder.findSubclasses(t)); |
| } |
| }); |
| }); |
| } catch (final IOException e) { |
| ok = false; |
| } |
| } |
| } |