From 8878567139d41c92b32cd9e013cfcdd64c995e66 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 22 Nov 2024 15:01:24 -0600 Subject: [PATCH 001/104] Initial pass at implementation of EnvironmentContextProvider --- .../org/eclipse/jetty/deploy/AppProvider.java | 8 +- .../deploy/providers/ContextProvider.java | 13 + .../providers/EnvironmentContextProvider.java | 780 ++++++++++++++++++ .../deploy/providers/ScanningAppProvider.java | 67 +- .../org/eclipse/jetty/server/Deployable.java | 5 + .../src/main/config/etc/jetty-ee10-deploy.xml | 86 +- .../jetty/ee10/webapp/WebAppContext.java | 36 + .../src/main/config/etc/jetty-ee11-deploy.xml | 89 +- .../jetty/ee11/webapp/WebAppContext.java | 36 + .../src/main/config/etc/jetty-ee8-deploy.xml | 85 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 88 +- .../jetty/ee9/webapp/WebAppContext.java | 35 + 12 files changed, 1020 insertions(+), 308 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java index a49fcc70dd54..e65e07e3920b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java @@ -42,8 +42,12 @@ public interface AppProvider extends LifeCycle ContextHandler createContextHandler(App app) throws Exception; /** - * * @return The name of the {@link org.eclipse.jetty.util.component.Environment} this provider is for. + * @deprecated not used by all AppProviders, no generic replacement provided. */ - String getEnvironmentName(); + @Deprecated(forRemoval = true, since = "12.1.0") + default String getEnvironmentName() + { + return ""; + } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index dc2c0aecbe55..b58a5c39a402 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -138,6 +138,8 @@ public boolean accept(File dir, String name) } } + private String _environmentName; + public ContextProvider() { super(); @@ -145,6 +147,17 @@ public ContextProvider() setScanInterval(0); } + @SuppressWarnings("removal") // only to suppress deprecation in interface, still needed here + public String getEnvironmentName() + { + return _environmentName; + } + + public void setEnvironmentName(String environmentName) + { + _environmentName = environmentName; + } + public Map getProperties() { return _properties; diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java new file mode 100644 index 000000000000..4fa08049f033 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java @@ -0,0 +1,780 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.io.IOResources; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.LoggerFactory; + +/** + * The webapps directory scanning provider. + *

+ * This provider scans one or more directories (typically "webapps") for contexts to + * deploy, which may be:

+ *

+ * To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * implements some heuristics to ignore some files found in the scans:

+ *

For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and + * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". + * The properties will be initialized with: + *

+ *

+ */ +@ManagedObject("Provider for start-up deployment of webapps based on presence in directory") +public class EnvironmentContextProvider extends ScanningAppProvider +{ + private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(EnvironmentContextProvider.class); + + public EnvironmentContextProvider() + { + super(); + setFilenameFilter(new Filter()); + setScanInterval(0); + } + + private static Map asProperties(Attributes attributes) + { + Map props = new HashMap<>(); + attributes.getAttributeNameSet().stream() + .map((name) -> + { + // undo old prefixed entries + if (name.startsWith(Deployable.ATTRIBUTE_PREFIX)) + return name.substring(Deployable.ATTRIBUTE_PREFIX.length()); + else + return name; + }) + .forEach((name) -> props.put(name, Objects.toString(attributes.getAttribute(name)))); + return props; + } + + @Override + public ContextHandler createContextHandler(final App app) throws Exception + { + Environment environment = Environment.get(app.getEnvironmentName()); + + if (environment == null) + { + LOG.warn("Environment [{}] is not available for app [{}]. The available environments are: {}", + app.getEnvironmentName(), + app, + Environment.getAll().stream() + .map(Environment::getName) + .collect(Collectors.joining(",", "[", "]")) + ); + return null; + } + + if (LOG.isDebugEnabled()) + LOG.debug("createContextHandler {} in {}", app, environment); + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(environment.getClassLoader()); + + // Create de-aliased file + Path path = app.getPath().toRealPath(); + if (!Files.exists(path)) + throw new IllegalStateException("App resource does not exist " + path); + + // prepare app attributes to use for app deployment + Attributes appAttributes = initAttributes(environment, app); + + Object context = null; + + // check if there is a specific ContextHandler type to create set in the + // properties associated with the webapp. If there is, we create it _before_ + // applying the environment xml file. + String contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); + if (contextHandlerClassName != null) + context = Class.forName(contextHandlerClassName).getDeclaredConstructor().newInstance(); + + // Collect the optional environment context xml files. + // Order them according to the name of their property key names. + List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() + .stream() + .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) + .map(k -> Path.of((String)appAttributes.getAttribute(k))) + .sorted() + .toList(); + + // apply each environment context xml file + for (Path envXmlPath : sortedEnvXmlPaths) + { + if (LOG.isDebugEnabled()) + LOG.debug("Applying environment specific context file {}", envXmlPath); + context = applyXml(context, envXmlPath, environment, appAttributes); + } + + // Handle a context XML file + if (FileID.isXml(path)) + { + context = applyXml(context, path, environment, appAttributes); + + // Look for the contextHandler itself + ContextHandler contextHandler = null; + if (context instanceof ContextHandler c) + contextHandler = c; + else if (context instanceof Supplier supplier) + { + Object nestedContext = supplier.get(); + if (nestedContext instanceof ContextHandler c) + contextHandler = c; + } + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + + return contextHandler; + } + // Otherwise it must be a directory or an archive + else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) + { + throw new IllegalStateException("unable to create ContextHandler for " + app); + } + + // Build the web application if necessary + if (context == null) + { + contextHandlerClassName = (String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); + if (StringUtil.isBlank(contextHandlerClassName)) + throw new IllegalStateException("No ContextHandler classname for " + app); + Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); + if (contextHandlerClass == null) + throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app); + + context = contextHandlerClass.getDeclaredConstructor().newInstance(); + } + + //set a backup value for the path to the war in case it hasn't already been set + appAttributes.setAttribute(Deployable.WAR, path.toString()); + return initializeContextHandler(context, path, appAttributes); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + @Deprecated + public Map getProperties(Environment environment) + { + return asProperties(environment); + } + + public void loadProperties(Environment environment, Path path) throws IOException + { + try (InputStream inputStream = Files.newInputStream(path)) + { + loadProperties(environment, inputStream); + } + } + + public void loadProperties(Environment environment, Resource resource) throws IOException + { + try (InputStream inputStream = IOResources.asInputStream(resource)) + { + loadProperties(environment, inputStream); + } + } + + public void loadPropertiesFromString(Environment environment, String path) throws IOException + { + loadProperties(environment, Path.of(path)); + } + + protected Object applyXml(Object context, Path xml, Environment environment, Attributes attributes) throws Exception + { + if (!FileID.isXml(xml)) + return null; + + XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, asProperties(attributes)) + { + @Override + public void initializeDefaults(Object context) + { + super.initializeDefaults(context); + EnvironmentContextProvider.this.initializeContextHandler(context, xml, attributes); + } + }; + + xmlc.getIdMap().put("Environment", environment.getName()); + xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml); + + // Put all Environment attributes into XMLC as properties that can be used. + attributes.getAttributeNameSet() + .stream() + .filter(k -> !k.startsWith("jetty.home") && + !k.startsWith("jetty.base") && + !k.startsWith("jetty.webapps")) + .forEach(k -> + { + String v = Objects.toString(attributes.getAttribute(k)); + xmlc.getProperties().put(k, v); + }); + + // Run configure against appropriate classloader. + ClassLoader xmlClassLoader = getXmlClassLoader(environment, xml); + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(xmlClassLoader); + + try + { + // Create or configure the context + if (context == null) + return xmlc.configure(); + + return xmlc.configure(context); + } + finally + { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + /** + * Return a ClassLoader that can load a {@link Environment#CORE} based webapp + * that is entirely defined within the {@code webapps/} directory. + * + *

+ * The resulting ClassLoader consists of the following entries: + *

    + *
  1. The java archive {@code .jar}
  2. + *
  3. The java archives {@code .d/lib/*.jar}
  4. + *
  5. The directory {@code .d/classes/}
  6. + *
+ *

+ * + * @param path to XML defining this webapp, must be absolute, and cannot be in root directory of drive. + * filename of XML will be used to determine the {@code } of the other entries in this + * ClassLoader. + * @return the classloader for this CORE environment webapp. + * @throws IOException if unable to apply to create classloader. + */ + protected ClassLoader findCoreContextClassLoader(Path path) throws IOException + { + Path webapps = path.getParent(); + String basename = FileID.getBasename(path); + List urls = new ArrayList<>(); + + // Is there a matching jar file? + // TODO: both files can be there depending on FileSystem, is this still sane? + // TODO: what about other capitalization? eg: ".Jar" ? + Path contextJar = webapps.resolve(basename + ".jar"); + if (!Files.exists(contextJar)) + contextJar = webapps.resolve(basename + ".JAR"); + if (Files.exists(contextJar)) + urls.add(contextJar.toUri().toURL()); + + // Is there a matching lib directory? + Path libDir = webapps.resolve(basename + ".d/lib"); + if (Files.exists(libDir) && Files.isDirectory(libDir)) + { + try (Stream paths = Files.list(libDir)) + { + paths.filter(FileID::isJavaArchive) + .map(Path::toUri) + .forEach(uri -> + { + try + { + urls.add(uri.toURL()); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + }); + } + } + + // Is there a matching lib directory? + Path classesDir = webapps.resolve(basename + ".d/classes"); + if (Files.exists(classesDir) && Files.isDirectory(libDir)) + urls.add(classesDir.toUri().toURL()); + + if (LOG.isDebugEnabled()) + LOG.debug("Core classloader for {}", urls); + + if (urls.isEmpty()) + return null; + return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); + } + + protected ContextHandler initializeContextHandler(Object context, Path path, Attributes attributes) + { + if (LOG.isDebugEnabled()) + LOG.debug("initializeContextHandler {}", context); + // find the ContextHandler + ContextHandler contextHandler; + if (context instanceof ContextHandler handler) + contextHandler = handler; + else if (Supplier.class.isAssignableFrom(context.getClass())) + { + @SuppressWarnings("unchecked") + Supplier provider = (Supplier)context; + contextHandler = provider.get(); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a context {}", context); + return null; + } + + assert contextHandler != null; + + initializeContextPath(contextHandler, path); + + if (Files.isDirectory(path)) + { + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); + } + + // pass through properties as attributes directly + attributes.getAttributeNameSet().stream() + .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) + .map((name) -> name.substring(Deployable.ATTRIBUTE_PREFIX.length())) + .forEach((name) -> contextHandler.setAttribute(name, attributes.getAttribute(name))); + + String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); + if (StringUtil.isNotBlank(contextPath)) + contextHandler.setContextPath(contextPath); + + if (context instanceof Deployable deployable) + deployable.initializeDefaults(attributes); + + return contextHandler; + } + + protected void initializeContextPath(ContextHandler context, Path path) + { + // Strip any 3 char extension from non directories + String basename = FileID.getBasename(path); + String contextPath = basename; + + // special case of archive (or dir) named "root" is / context + if (contextPath.equalsIgnoreCase("root")) + { + contextPath = "/"; + } + // handle root with virtual host form + else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) + { + int dash = contextPath.indexOf('-'); + String virtual = contextPath.substring(dash + 1); + context.setVirtualHosts(Arrays.asList(virtual.split(","))); + contextPath = "/"; + } + + // Ensure "/" is Prepended to all context paths. + if (contextPath.charAt(0) != '/') + contextPath = "/" + contextPath; + + // Set the display name and context Path + context.setDisplayName(basename); + context.setContextPath(contextPath); + } + + protected boolean isDeployable(Path path) + { + String basename = FileID.getBasename(path); + + //is the file that changed a directory? + if (Files.isDirectory(path)) + { + // deploy if there is not a .xml or .war file of the same basename? + return !Files.exists(path.getParent().resolve(basename + ".xml")) && + !Files.exists(path.getParent().resolve(basename + ".XML")) && + !Files.exists(path.getParent().resolve(basename + ".war")) && + !Files.exists(path.getParent().resolve(basename + ".WAR")); + } + + // deploy if it is a .war and there is not a .xml for of the same basename + if (FileID.isWebArchive(path)) + { + // if a .xml file exists for it + return !Files.exists(path.getParent().resolve(basename + ".xml")) && + !Files.exists(path.getParent().resolve(basename + ".XML")); + } + + // otherwise only deploy an XML + return FileID.isXml(path); + } + + @Override + protected void pathAdded(Path path) throws Exception + { + if (isDeployable(path)) + super.pathAdded(path); + } + + @Override + protected void pathChanged(Path path) throws Exception + { + if (isDeployable(path)) + super.pathChanged(path); + } + + /** + * Get the ClassLoader appropriate for applying Jetty XML. + * @param environment the environment to use + * @param xml the path to the XML + * @return the appropriate ClassLoader. + * @throws IOException if unable to create the ClassLoader + */ + private ClassLoader getXmlClassLoader(Environment environment, Path xml) throws IOException + { + if (Environment.CORE.equals(environment)) + { + // this XML belongs to a CORE deployment. + return findCoreContextClassLoader(xml); + } + else + { + return environment.getClassLoader(); + } + } + + private Attributes initAttributes(Environment environment, App app) throws IOException + { + Attributes attributes = new Attributes.Mapped(); + + // Start appAttributes with Attributes from Environment + environment.getAttributeNameSet().forEach((key) -> + attributes.setAttribute(key, environment.getAttribute(key))); + + // TODO: double check if an empty environment name makes sense. Will the default environment name? + String env = app.getEnvironmentName(); + + if (StringUtil.isNotBlank(env)) + { + // Load environment specific properties files + Path parent = app.getPath().getParent(); + Properties envProps = loadEnvironmentProperties(parent, env); + + envProps.stringPropertyNames().forEach( + k -> attributes.setAttribute(k, envProps.getProperty(k)) + ); + } + + // Overlay the app properties + app.getProperties().forEach(attributes::setAttribute); + + return attributes; + } + + /** + * Load all of the {@link Environment} specific {@code [-].properties} files + * found in the directory provided. + * + *

+ * All found properties files are first sorted by filename, then loaded one by one into + * a single {@link Properties} instance. + *

+ * + * @param directory the directory to load environment properties from. + * @param env the environment name + */ + private Properties loadEnvironmentProperties(Path directory, String env) throws IOException + { + Properties props = new Properties(); + List envPropertyFiles = new ArrayList<>(); + + // Get all environment specific properties files for this environment, + // order them according to the lexical ordering of the filenames + try (Stream paths = Files.list(directory)) + { + envPropertyFiles = paths.filter(Files::isRegularFile) + .map(directory::relativize) + .filter(p -> + { + String name = p.getName(0).toString(); + if (!name.endsWith(".properties")) + return false; + if (!name.startsWith(env)) + return false; + return true; + }).sorted().toList(); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Environment property files {}", envPropertyFiles); + + // Load each *.properties file + for (Path file : envPropertyFiles) + { + Path resolvedFile = directory.resolve(file); + if (Files.exists(resolvedFile)) + { + Properties tmp = new Properties(); + try (InputStream stream = Files.newInputStream(resolvedFile)) + { + tmp.load(stream); + //put each property into our substitution pool + tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); + } + } + } + + return props; + } + + private void loadProperties(Environment environment, InputStream inputStream) throws IOException + { + Properties props = new Properties(); + props.load(inputStream); + props.stringPropertyNames().forEach((name) -> + environment.setAttribute(name, props.getProperty(name))); + } + + /** + * Builder of a deployment configuration for a specific {@link Environment}. + * + *

+ * Results in {@link Attributes} for {@link Environment} containing the + * deployment configuration (as {@link Deployable} keys) that is applied to all deployable + * apps belonging to that {@link Environment}. + *

+ */ + public static class EnvBuilder + { + // Using setters in this class to allow jetty-xml + // syntax to skip setting of an environment attribute if property is unset, + // allowing the in code values to be same defaults as they are in embedded-jetty. + + private final Environment environment; + + public EnvBuilder(String name) + { + environment = Environment.ensure(name); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} attribute. + * + * @param configurations The configuration class names as a comma separated list + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String configurations) + { + setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. + * + * @param configurations The configuration class names. + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String[] configurations) + { + if (configurations == null) + environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); + else + environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); + } + + /** + * This is equivalent to setting the {@link Deployable#CONTAINER_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning container jars + * @see Deployable#CONTAINER_SCAN_JARS + */ + public void setContainerScanJarPattern(String pattern) + { + environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); + } + + /** + * The name of the class that this environment uses to create {@link ContextHandler} + * instances (can be class that implements {@code java.util.function.Supplier} + * as well). + * + * @param classname the classname for this environment's context deployable. + * @see Deployable#CONTEXT_HANDLER_CLASS + */ + public void setContextHandlerClass(String classname) + { + environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); + } + + /** + * Set the defaultsDescriptor. + * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} attribute. + * + * @param defaultsDescriptor the defaultsDescriptor to set + * @see Deployable#DEFAULTS_DESCRIPTOR + */ + public void setDefaultsDescriptor(String defaultsDescriptor) + { + environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); + } + + /** + * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} attribute. + * + * @param extractWars the extractWars to set + * @see Deployable#EXTRACT_WARS + */ + public void setExtractWars(boolean extractWars) + { + environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); + } + + /** + * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} attribute. + * + * @param parentLoaderPriority the parentLoaderPriority to set + * @see Deployable#PARENT_LOADER_PRIORITY + */ + public void setParentLoaderPriority(boolean parentLoaderPriority) + { + environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_EXCLUSION_PATTERN} property. + * + * @param pattern The regex pattern to exclude ServletContainerInitializers from executing + * @see Deployable#SCI_EXCLUSION_PATTERN + */ + public void setServletContainerInitializerExclusionPattern(String pattern) + { + environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_ORDER} property. + * + * @param order The ordered list of ServletContainerInitializer classes to run + * @see Deployable#SCI_ORDER + */ + public void setServletContainerInitializerOrder(String order) + { + environment.setAttribute(Deployable.SCI_ORDER, order); + } + + /** + * This is equivalent to setting the {@link Deployable#WEBINF_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning web-inf jars + * @see Deployable#WEBINF_SCAN_JARS + */ + public void setWebInfScanJarPattern(String pattern) + { + environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); + } + } + + public class Filter implements FilenameFilter + { + @Override + public boolean accept(File dir, String name) + { + if (dir == null || !dir.canRead()) + return false; + + // Accept XML files and WARs + if (FileID.isXml(name) || FileID.isWebArchive(name)) + return true; + + Path path = dir.toPath().resolve(name); + + // Ignore any other file that are not directories + if (!Files.isDirectory(path)) + return false; + + // Don't deploy monitored resources + if (getMonitoredResources().stream().map(Resource::getPath).anyMatch(path::equals)) + return false; + + // Ignore hidden directories + if (name.startsWith(".")) + return false; + + String lowerName = name.toLowerCase(Locale.ENGLISH); + + // is it a nominated config directory + if (lowerName.endsWith(".d")) + return false; + + // ignore source control directories + if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) + return false; + + // ignore directories that have sibling war or XML file + if (Files.exists(dir.toPath().resolve(name + ".war")) || + Files.exists(dir.toPath().resolve(name + ".WAR")) || + Files.exists(dir.toPath().resolve(name + ".xml")) || + Files.exists(dir.toPath().resolve(name + ".XML"))) + return false; + + return true; + } + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 01d395d5699c..283190714090 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -29,15 +29,12 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Scanner; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; @@ -58,7 +55,6 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements private int _scanInterval = 10; private Scanner _scanner; private boolean _useRealPaths; - private String _environmentName; private boolean _deferInitialScan = false; private final Scanner.DiscreteListener _scannerListener = new Scanner.DiscreteListener() @@ -93,17 +89,6 @@ protected ScanningAppProvider(FilenameFilter filter) installBean(_appMap); } - @Override - public String getEnvironmentName() - { - return _environmentName; - } - - public void setEnvironmentName(String environmentName) - { - _environmentName = environmentName; - } - /** * @return True if the real path of the scanned files should be used for deployment. */ @@ -149,40 +134,7 @@ protected App createApp(Path path) App app = new App(_deploymentManager, this, path); if (LOG.isDebugEnabled()) LOG.debug("{} creating {}", this, app); - - String defaultEnvironmentName = _deploymentManager.getDefaultEnvironmentName(); - - String environmentName = app.getEnvironmentName(); - if (StringUtil.isBlank(environmentName) && StringUtil.isNotBlank(defaultEnvironmentName)) - { - environmentName = defaultEnvironmentName; - app.getProperties().put(Deployable.ENVIRONMENT, environmentName); - if (LOG.isDebugEnabled()) - LOG.debug("{} default environment for {}", this, app); - } - - if (StringUtil.isNotBlank(environmentName)) - { - // If the app specifies the environment for this provider, then this deployer will deploy it. - if (environmentName.equalsIgnoreCase(getEnvironmentName())) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} created {}", this, app); - return app; - } - - // If we are the default provider then we may warn - if (getEnvironmentName().equalsIgnoreCase(defaultEnvironmentName)) - { - // if the app specified an environment name, then produce warning if there is no provider for it. - if (!_deploymentManager.hasAppProviderFor(environmentName)) - LOG.warn("No AppProvider with environment {} for {}", environmentName, app); - return null; - } - } - - LOG.warn("{} no environment for {}, ignoring", this, app); - return null; + return app; } @Override @@ -190,21 +142,10 @@ protected void doStart() throws Exception { if (LOG.isDebugEnabled()) LOG.debug("{}.doStart()", this.getClass().getSimpleName()); - if (_monitored.size() == 0) - throw new IllegalStateException("No configuration dir specified"); - if (_environmentName == null) - { - List nonCore = Environment.getAll().stream().filter(environment -> !environment.equals(Environment.CORE)).toList(); - if (nonCore.size() != 1) - throw new IllegalStateException("No environment configured"); - _environmentName = nonCore.get(0).getName(); - } - - Environment environment = Environment.get(_environmentName); - if (environment == null) - throw new IllegalStateException("Unknown environment " + _environmentName); + if (_monitored.isEmpty()) + throw new IllegalStateException("No monitored dir specified"); - LOG.info("Deployment monitor {} in {} at intervals {}s", getEnvironmentName(), _monitored, getScanInterval()); + LOG.info("Deployment monitor in {} at intervals {}s", _monitored, getScanInterval()); List files = new ArrayList<>(); for (Resource resource : _monitored) { diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index d08275f8cccb..947b2668a74c 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -18,6 +18,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jetty.util.Attributes; + /** * Interface that can be implemented by ContextHandlers within Environments to allow configuration * to be passed from the DeploymentManager without dependencies on the Deployment module itself. @@ -67,5 +69,8 @@ public interface Deployable String WAR = "jetty.deploy.war"; String WEBINF_SCAN_JARS = "jetty.deploy.webInfScanJarPattern"; + // TODO: should we deprecate this one? void initializeDefaults(Map properties); + + void initializeDefaults(Attributes attributes); } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 868a0e51462c..0c973bf2822e 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -1,65 +1,31 @@ - + - - - org.eclipse.jetty.deploy.DeploymentManager - - - - contextHandlerClass - - - - - - - - ee10 - - - - - - - - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee10.xml - - - - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - - - - - - + + ee10 + + + + + + + /etc/webdefault-ee10.xml + + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 0a4d5ab71f5e..3bb3e7fb406d 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; @@ -243,6 +244,41 @@ public void initializeDefaults(Map properties) _defaultContextPath = true; } + @Override + public void initializeDefaults(Attributes attributes) + { + for (String keyName : attributes.getAttributeNameSet()) + { + Object value = attributes.getAttribute(keyName); + if (LOG.isDebugEnabled()) + LOG.debug("init {}: {}", keyName, value); + + switch (keyName) + { + case Deployable.WAR -> + { + if (getWar() == null) + setWar((String)value); + } + case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); + case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses((String[])value); + case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); + case Deployable.EXTRACT_WARS -> setExtractWAR((Boolean)value); + case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority((Boolean)value); + case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); + case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor((String)value); + case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); + case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); + default -> + { + if (LOG.isDebugEnabled() && value != null) + LOG.debug("unknown property {}={}", keyName, value); + } + } + } + _defaultContextPath = true; + } + public boolean isContextPathDefault() { return _defaultContextPath; diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index 84e2ee4fdbd9..5ab08e473b7a 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -1,68 +1,31 @@ - - - - - + - - - org.eclipse.jetty.deploy.DeploymentManager - - - - contextHandlerClass - - - - - - - - ee11 - - - - - - - - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee11.xml - - - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - - - - - - + + ee11 + + + + + + + /etc/webdefault-ee11.xml + + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java index 73127bbc0389..98679e3bde27 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; @@ -225,6 +226,41 @@ public void initializeDefaults(Map properties) _defaultContextPath = true; } + @Override + public void initializeDefaults(Attributes attributes) + { + for (String keyName : attributes.getAttributeNameSet()) + { + Object value = attributes.getAttribute(keyName); + if (LOG.isDebugEnabled()) + LOG.debug("init {}: {}", keyName, value); + + switch (keyName) + { + case Deployable.WAR -> + { + if (getWar() == null) + setWar((String)value); + } + case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); + case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses((String[])value); + case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); + case Deployable.EXTRACT_WARS -> setExtractWAR((Boolean)value); + case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority((Boolean)value); + case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); + case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor((String)value); + case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); + case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); + default -> + { + if (LOG.isDebugEnabled() && value != null) + LOG.debug("unknown property {}={}", keyName, value); + } + } + } + _defaultContextPath = true; + } + public boolean isContextPathDefault() { return _defaultContextPath; diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index ef89f770e7b1..03ac84cca3cc 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -1,64 +1,31 @@ - + - - - org.eclipse.jetty.deploy.DeploymentManager - - - - contextHandlerClass - - - - - - - - ee8 - - - - - - - - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee8.xml - - - - - - - - - - - .*/jetty-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\.jar|.*jsp.jstl-[^/]*\.jar - - - - - - - - - - - - - - - - + + ee8 + + + + + + + /etc/webdefault-ee8.xml + + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index d4f60b484b11..adcf0e856a3c 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -1,65 +1,31 @@ - + - + - - - org.eclipse.jetty.deploy.DeploymentManager - - - - contextHandlerClass - - - - - - - - ee9 - - - - - - - - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee9.xml - - - - - - - - - - - - .*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - - - - - - + + ee9 + + + + + + + /etc/webdefault-ee9.xml + + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index 7ccf48b9e25d..89be9babf757 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -269,6 +269,41 @@ public void initializeDefaults(Map properties) _defaultContextPath = true; } + @Override + public void initializeDefaults(Attributes attributes) + { + for (String keyName : attributes.getAttributeNameSet()) + { + Object value = attributes.getAttribute(keyName); + if (LOG.isDebugEnabled()) + LOG.debug("init {}: {}", keyName, value); + + switch (keyName) + { + case Deployable.WAR -> + { + if (getWar() == null) + setWar((String)value); + } + case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); + case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses((String[])value); + case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); + case Deployable.EXTRACT_WARS -> setExtractWAR((Boolean)value); + case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority((Boolean)value); + case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); + case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor((String)value); + case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); + case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); + default -> + { + if (LOG.isDebugEnabled() && value != null) + LOG.debug("unknown property {}={}", keyName, value); + } + } + } + _defaultContextPath = true; + } + public boolean isContextPathDefault() { return _defaultContextPath; From 3a46e37e18a2070966c098eca4b99e005f59bdaa Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 22 Nov 2024 15:12:04 -0600 Subject: [PATCH 002/104] Fixing javadoc errors --- .../providers/EnvironmentContextProvider.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java index 4fa08049f033..f321803a8a96 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java @@ -51,17 +51,19 @@ /** * The webapps directory scanning provider. - *

- * This provider scans one or more directories (typically "webapps") for contexts to - * deploy, which may be:

    + *

    This provider scans one or more directories (typically "webapps") for contexts to + * deploy, which may be: + *

    + *
      *
    • A standard WAR file (must end in ".war")
    • *
    • A directory containing an expanded WAR file
    • *
    • A directory containing static content
    • *
    • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
    • *
    - *

    - * To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * implements some heuristics to ignore some files found in the scans:

      + *

      To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * implements some heuristics to ignore some files found in the scans: + *

      + *
        *
      • Hidden files (starting with {@code "."}) are ignored
      • *
      • Directories with names ending in {@code ".d"} are ignored
      • *
      • Property files with names ending in {@code ".properties"} are not deployed.
      • @@ -75,13 +77,13 @@ *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". * The properties will be initialized with: + *

        *
          *
        • The properties set on the application via {@link App#getProperties()}
        • *
        • The app specific properties file {@code webapps/.properties}
        • *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • *
        • The {@link Attributes} from the {@link Environment}
        • *
        - *

        */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") public class EnvironmentContextProvider extends ScanningAppProvider @@ -299,14 +301,12 @@ public void initializeDefaults(Object context) * Return a ClassLoader that can load a {@link Environment#CORE} based webapp * that is entirely defined within the {@code webapps/} directory. * - *

        - * The resulting ClassLoader consists of the following entries: + *

        The resulting ClassLoader consists of the following entries:

        *
          *
        1. The java archive {@code .jar}
        2. *
        3. The java archives {@code .d/lib/*.jar}
        4. *
        5. The directory {@code .d/classes/}
        6. *
        - *

        * * @param path to XML defining this webapp, must be absolute, and cannot be in root directory of drive. * filename of XML will be used to determine the {@code } of the other entries in this From c5c07c2fab4225344106e1be759d64b5c1c3df54 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 25 Nov 2024 13:04:28 -0600 Subject: [PATCH 003/104] Moving things around --- .../jetty/deploy/DeploymentManager.java | 6 ++++++ .../providers/EnvironmentContextProvider.java | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 5ce184b35e60..aa04ff8ea1b7 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -132,7 +132,9 @@ void setLifeCycleNode(Node node) * Get the default {@link Environment} name for deployed applications, which is * the maximal name when using the {@link Deployable#ENVIRONMENT_COMPARATOR}. * @return The default {@link Environment} name or null. + * @deprecated Moved to {@link org.eclipse.jetty.deploy.providers.EnvironmentContextProvider} */ + @Deprecated(since = "12.1.0", forRemoval = true) public String getDefaultEnvironmentName() { return _providers.stream() @@ -184,6 +186,10 @@ public void setAppProviders(Collection providers) } } + /** + * @deprecated No replacement. AppProvider interface no longer has getEnvironmentName. + */ + @Deprecated(since = "12.1.0", forRemoval = true) public boolean hasAppProviderFor(String environmentName) { return environmentName != null && getAppProviders().stream() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java index f321803a8a96..3bd0039cd846 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java @@ -89,6 +89,7 @@ public class EnvironmentContextProvider extends ScanningAppProvider { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(EnvironmentContextProvider.class); + private String defaultEnvironmentName; public EnvironmentContextProvider() { @@ -97,6 +98,23 @@ public EnvironmentContextProvider() setScanInterval(0); } + public String getDefaultEnvironmentName() + { + if (defaultEnvironmentName == null) + { + return Environment.getAll().stream() + .map(Environment::getName) + .max(Deployable.ENVIRONMENT_COMPARATOR) + .orElse(null); + } + return defaultEnvironmentName; + } + + public void setDefaultEnvironmentName(String name) + { + this.defaultEnvironmentName = name; + } + private static Map asProperties(Attributes attributes) { Map props = new HashMap<>(); From e0bdeb382149233edcecd46ffa076f6f773dbfe0 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 25 Nov 2024 13:10:50 -0600 Subject: [PATCH 004/104] Moving changes back into ContextProvider --- .../jetty/deploy/DeploymentManager.java | 3 +- .../deploy/providers/ContextProvider.java | 920 ++++++++++-------- .../providers/EnvironmentContextProvider.java | 798 --------------- .../src/main/config/etc/jetty-ee10-deploy.xml | 6 +- .../src/main/config/etc/jetty-ee11-deploy.xml | 6 +- .../src/main/config/etc/jetty-ee8-deploy.xml | 6 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 6 +- 7 files changed, 550 insertions(+), 1195 deletions(-) delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index aa04ff8ea1b7..dce0331884c4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.deploy.graph.Edge; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.deploy.graph.Route; +import org.eclipse.jetty.deploy.providers.ContextProvider; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -132,7 +133,7 @@ void setLifeCycleNode(Node node) * Get the default {@link Environment} name for deployed applications, which is * the maximal name when using the {@link Deployable#ENVIRONMENT_COMPARATOR}. * @return The default {@link Environment} name or null. - * @deprecated Moved to {@link org.eclipse.jetty.deploy.providers.EnvironmentContextProvider} + * @deprecated not used, replacement at {@link ContextProvider#getDefaultEnvironmentName()} */ @Deprecated(since = "12.1.0", forRemoval = true) public String getDefaultEnvironmentName() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index b58a5c39a402..cfeaab2c55de 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -21,7 +21,6 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -39,10 +38,10 @@ import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.resource.Resource; @@ -52,93 +51,45 @@ /** * The webapps directory scanning provider. - *

        - * This provider scans one or more directories (typically "webapps") for contexts to - * deploy, which may be:

          - *
        • A standard WAR file (must end in ".war")
        • - *
        • A directory containing an expanded WAR file
        • - *
        • A directory containing static content
        • - *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • - *
        - *

        - * To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * implements some heuristics to ignore some files found in the scans:

          - *
        • Hidden files (starting with ".") are ignored
        • - *
        • Directories with names ending in ".d" are ignored
        • - *
        • Property files with names ending in ".properties" are not deployed.
        • - *
        • If a directory and a WAR file exist ( eg foo/ and foo.war) then the directory is assumed to be - * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • - *
        • If a directory and a matching XML file exist ( eg foo/ and foo.xml) then the directory is assumed to be - * an unpacked WAR and only the XML is deployed (which may used the directory in it's configuration)
        • - *
        • If a WAR file and a matching XML exist (eg foo.war and foo.xml) then the WAR is assumed to - * be configured by the XML and only the XML is deployed. - *
        - *

        - * Only {@link App}s discovered that report {@link App#getEnvironmentName()} matching this providers - * {@link #getEnvironmentName()} will be deployed. - *

        - *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and - * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". - * The properties will be initialized with:

          - *
        • The properties set on the application via {@link App#getProperties()}; otherwise:
        • - *
        • The properties set on this provider via {@link #getProperties()}
        • - *
        + * *

        This provider scans one or more directories (typically "webapps") for contexts to + * * deploy, which may be: + * *

        + * *
          + * *
        • A standard WAR file (must end in ".war")
        • + * *
        • A directory containing an expanded WAR file
        • + * *
        • A directory containing static content
        • + * *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • + * *
        + * *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * * implements some heuristics to ignore some files found in the scans: + * *

        + * *
          + * *
        • Hidden files (starting with {@code "."}) are ignored
        • + * *
        • Directories with names ending in {@code ".d"} are ignored
        • + * *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • + * *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be + * * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • + * *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be + * * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • + * *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to + * * be configured by the XML and only the XML is deployed. + * *
        + * *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and + * * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". + * * The properties will be initialized with: + * *

        + * *
          + * *
        • The properties set on the application via {@link App#getProperties()}
        • + * *
        • The app specific properties file {@code webapps/.properties}
        • + * *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • + * *
        • The {@link Attributes} from the {@link Environment}
        • + * *
        */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") public class ContextProvider extends ScanningAppProvider { private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ContextProvider.class); - - private final Map _properties = new HashMap<>(); - - public class Filter implements FilenameFilter - { - @Override - public boolean accept(File dir, String name) - { - if (dir == null || !dir.canRead()) - return false; - - // Accept XML files and WARs - if (FileID.isXml(name) || FileID.isWebArchive(name)) - return true; - - Path path = dir.toPath().resolve(name); - - // Ignore any other file that are not directories - if (!Files.isDirectory(path)) - return false; - - // Don't deploy monitored resources - if (getMonitoredResources().stream().map(Resource::getPath).anyMatch(path::equals)) - return false; - - // Ignore hidden directories - if (name.startsWith(".")) - return false; - - String lowerName = name.toLowerCase(Locale.ENGLISH); - - // is it a nominated config directory - if (lowerName.endsWith(".d")) - return false; - - // ignore source control directories - if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) - return false; - - // ignore directories that have sibling war or XML file - if (Files.exists(dir.toPath().resolve(name + ".war")) || - Files.exists(dir.toPath().resolve(name + ".WAR")) || - Files.exists(dir.toPath().resolve(name + ".xml")) || - Files.exists(dir.toPath().resolve(name + ".XML"))) - return false; - - return true; - } - } - - private String _environmentName; + private String defaultEnvironmentName; public ContextProvider() { @@ -147,203 +98,39 @@ public ContextProvider() setScanInterval(0); } - @SuppressWarnings("removal") // only to suppress deprecation in interface, still needed here - public String getEnvironmentName() - { - return _environmentName; - } - - public void setEnvironmentName(String environmentName) - { - _environmentName = environmentName; - } - - public Map getProperties() - { - return _properties; - } - - public void loadProperties(Resource resource) throws IOException - { - Properties props = new Properties(); - try (InputStream inputStream = IOResources.asInputStream(resource)) - { - props.load(inputStream); - props.forEach((key, value) -> _properties.put((String)key, (String)value)); - } - } - - public void loadPropertiesFromPath(Path path) throws IOException - { - Properties props = new Properties(); - try (InputStream inputStream = Files.newInputStream(path)) - { - props.load(inputStream); - props.forEach((key, value) -> _properties.put((String)key, (String)value)); - } - } - - public void loadPropertiesFromString(String path) throws IOException - { - loadPropertiesFromPath(Path.of(path)); - } - - /** - * Get the extractWars. - * This is equivalent to getting the {@link Deployable#EXTRACT_WARS} property. - * - * @return the extractWars - */ - @ManagedAttribute("extract war files") - public boolean isExtractWars() - { - return Boolean.parseBoolean(_properties.get(Deployable.EXTRACT_WARS)); - } - - /** - * Set the extractWars. - * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} property. - * - * @param extractWars the extractWars to set - */ - public void setExtractWars(boolean extractWars) - { - _properties.put(Deployable.EXTRACT_WARS, Boolean.toString(extractWars)); - } - - /** - * Get the parentLoaderPriority. - * This is equivalent to getting the {@link Deployable#PARENT_LOADER_PRIORITY} property. - * - * @return the parentLoaderPriority - */ - @ManagedAttribute("parent classloader has priority") - public boolean isParentLoaderPriority() - { - return Boolean.parseBoolean(_properties.get(Deployable.PARENT_LOADER_PRIORITY)); - } - - /** - * Set the parentLoaderPriority. - * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} property. - * - * @param parentLoaderPriority the parentLoaderPriority to set - */ - public void setParentLoaderPriority(boolean parentLoaderPriority) - { - _properties.put(Deployable.PARENT_LOADER_PRIORITY, Boolean.toString(parentLoaderPriority)); - } - - /** - * Get the defaultsDescriptor. - * This is equivalent to getting the {@link Deployable#DEFAULTS_DESCRIPTOR} property. - * - * @return the defaultsDescriptor - */ - @ManagedAttribute("default descriptor for webapps") - public String getDefaultsDescriptor() + private static Map asProperties(Attributes attributes) { - return _properties.get(Deployable.DEFAULTS_DESCRIPTOR); - } - - /** - * Set the defaultsDescriptor. - * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} property. - * - * @param defaultsDescriptor the defaultsDescriptor to set - */ - public void setDefaultsDescriptor(String defaultsDescriptor) - { - _properties.put(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * - * @param configurations The configuration class names as a comma separated list - */ - public void setConfigurationClasses(String configurations) - { - setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * - * @param configurations The configuration class names. - */ - public void setConfigurationClasses(String[] configurations) - { - _properties.put(Deployable.CONFIGURATION_CLASSES, (configurations == null) - ? null - : String.join(",", configurations)); + Map props = new HashMap<>(); + attributes.getAttributeNameSet().stream() + .map((name) -> + { + // undo old prefixed entries + if (name.startsWith(Deployable.ATTRIBUTE_PREFIX)) + return name.substring(Deployable.ATTRIBUTE_PREFIX.length()); + else + return name; + }) + .forEach((name) -> props.put(name, Objects.toString(attributes.getAttribute(name)))); + return props; } - /** - * This is equivalent to getting the {@link Deployable#CONFIGURATION_CLASSES} property. - * - * @return The configuration class names. - */ - @ManagedAttribute("configuration classes for webapps to be processed through") - public String[] getConfigurationClasses() + @Override + public ContextHandler createContextHandler(final App app) throws Exception { - String cc = _properties.get(Deployable.CONFIGURATION_CLASSES); - return cc == null ? new String[0] : cc.split(","); - } + Environment environment = Environment.get(app.getEnvironmentName()); - protected ContextHandler initializeContextHandler(Object context, Path path, Map properties) - { - if (LOG.isDebugEnabled()) - LOG.debug("initializeContextHandler {}", context); - // find the ContextHandler - ContextHandler contextHandler; - if (context instanceof ContextHandler handler) - contextHandler = handler; - else if (Supplier.class.isAssignableFrom(context.getClass())) + if (environment == null) { - @SuppressWarnings("unchecked") - Supplier provider = (Supplier)context; - contextHandler = provider.get(); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Not a context {}", context); + LOG.warn("Environment [{}] is not available for app [{}]. The available environments are: {}", + app.getEnvironmentName(), + app, + Environment.getAll().stream() + .map(Environment::getName) + .collect(Collectors.joining(",", "[", "]")) + ); return null; } - assert contextHandler != null; - - initializeContextPath(contextHandler, path); - - if (Files.isDirectory(path)) - contextHandler.setBaseResource(ResourceFactory.of(this).newResource(path)); - - //TODO think of better way of doing this - //pass through properties as attributes directly - for (Map.Entry prop : properties.entrySet()) - { - String key = prop.getKey(); - String value = prop.getValue(); - if (key.startsWith(Deployable.ATTRIBUTE_PREFIX)) - contextHandler.setAttribute(key.substring(Deployable.ATTRIBUTE_PREFIX.length()), value); - } - - String contextPath = properties.get(Deployable.CONTEXT_PATH); - if (StringUtil.isNotBlank(contextPath)) - contextHandler.setContextPath(contextPath); - - if (context instanceof Deployable deployable) - deployable.initializeDefaults(properties); - - return contextHandler; - } - - @Override - public ContextHandler createContextHandler(final App app) throws Exception - { - Environment environment = Environment.get(app.getEnvironmentName()); - if (LOG.isDebugEnabled()) LOG.debug("createContextHandler {} in {}", app, environment); @@ -353,112 +140,43 @@ public ContextHandler createContextHandler(final App app) throws Exception Thread.currentThread().setContextClassLoader(environment.getClassLoader()); // Create de-aliased file - Path path = app.getPath().toRealPath().toAbsolutePath().toFile().getCanonicalFile().toPath(); + Path path = app.getPath().toRealPath(); if (!Files.exists(path)) throw new IllegalStateException("App resource does not exist " + path); - // prepare properties - Map properties = new HashMap<>(); - - //add in properties from start mechanism - properties.putAll(getProperties()); + // prepare app attributes to use for app deployment + Attributes appAttributes = initAttributes(environment, app); Object context = null; - //check if there is a specific ContextHandler type to create set in the - //properties associated with the webapp. If there is, we create it _before_ - //applying the environment xml file. - String contextHandlerClassName = app.getProperties().get(Deployable.CONTEXT_HANDLER_CLASS); + + // check if there is a specific ContextHandler type to create set in the + // properties associated with the webapp. If there is, we create it _before_ + // applying the environment xml file. + String contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); if (contextHandlerClassName != null) context = Class.forName(contextHandlerClassName).getDeclaredConstructor().newInstance(); - //Add in environment-specific properties: - // allow multiple eeXX[-zzz].properties files, ordered lexically - // allow each to contain jetty.deploy.environmentXml[.zzzz] properties - // accumulate all properties for substitution purposes - // order all jetty.deploy.environmentXml[.zzzz] properties lexically - // apply the context xml files named by the ordered jetty.deploy.environmentXml[.zzzz] properties - String env = app.getEnvironmentName() == null ? "" : app.getEnvironmentName(); - - if (StringUtil.isNotBlank(env)) + // Collect the optional environment context xml files. + // Order them according to the name of their property key names. + List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() + .stream() + .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) + .map(k -> Path.of((String)appAttributes.getAttribute(k))) + .sorted() + .toList(); + + // apply each environment context xml file + for (Path envXmlPath : sortedEnvXmlPaths) { - List envPropertyFiles = new ArrayList<>(); - Path parent = app.getPath().getParent(); - - //Get all environment specific properties files for this environment, - //order them according to the lexical ordering of the filenames - try (Stream paths = Files.list(parent)) - { - envPropertyFiles = paths.filter(Files::isRegularFile) - .map(p -> parent.relativize(p)) - .filter(p -> - { - String name = p.getName(0).toString(); - if (!name.endsWith(".properties")) - return false; - if (!name.startsWith(env)) - return false; - return true; - }).sorted().collect(Collectors.toList()); - } - if (LOG.isDebugEnabled()) - LOG.debug("Environment property files {}", envPropertyFiles); - - Map envXmlFilenameMap = new HashMap<>(); - for (Path file : envPropertyFiles) - { - Path resolvedFile = parent.resolve(file); - if (Files.exists(resolvedFile)) - { - Properties tmp = new Properties(); - try (InputStream stream = Files.newInputStream(resolvedFile)) - { - tmp.load(stream); - //put each property into our substitution pool - tmp.stringPropertyNames().forEach(k -> properties.put(k, tmp.getProperty(k))); - } - } - } - - //extract any properties that name environment context xml files - for (Map.Entry entry : properties.entrySet()) - { - String name = Objects.toString(entry.getKey(), ""); - if (name.startsWith(Deployable.ENVIRONMENT_XML)) - { - //ensure all environment context xml files are absolute paths - Path envXmlPath = Paths.get(entry.getValue().toString()); - if (!envXmlPath.isAbsolute()) - envXmlPath = getMonitoredDirResource().getPath().getParent().resolve(envXmlPath); - //accumulate all properties that name environment xml files so they can be ordered - envXmlFilenameMap.put(name, envXmlPath); - } - } - - //order the environment context xml files according to the name of their properties - List sortedEnvXmlProperties = envXmlFilenameMap.keySet().stream().sorted().toList(); - - //apply each environment context xml file - for (String property : sortedEnvXmlProperties) - { - Path envXmlPath = envXmlFilenameMap.get(property); - if (LOG.isDebugEnabled()) - LOG.debug("Applying environment specific context file {}", envXmlPath); - context = applyXml(context, envXmlPath, env, properties); - } + LOG.debug("Applying environment specific context file {}", envXmlPath); + context = applyXml(context, envXmlPath, environment, appAttributes); } - //add in properties specific to the deployable - properties.putAll(app.getProperties()); - // Handle a context XML file if (FileID.isXml(path)) { - ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(path) : null; - if (coreContextClassLoader != null) - Thread.currentThread().setContextClassLoader(coreContextClassLoader); - - context = applyXml(context, path, env, properties); + context = applyXml(context, path, environment, appAttributes); // Look for the contextHandler itself ContextHandler contextHandler = null; @@ -473,10 +191,6 @@ else if (context instanceof Supplier supplier) if (contextHandler == null) throw new IllegalStateException("Unknown context type of " + context); - // Set the classloader if we have a coreContextClassLoader - if (coreContextClassLoader != null) - contextHandler.setClassLoader(coreContextClassLoader); - return contextHandler; } // Otherwise it must be a directory or an archive @@ -488,7 +202,7 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) // Build the web application if necessary if (context == null) { - contextHandlerClassName = (String)environment.getAttribute("contextHandlerClass"); + contextHandlerClassName = (String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); if (StringUtil.isBlank(contextHandlerClassName)) throw new IllegalStateException("No ContextHandler classname for " + app); Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); @@ -499,8 +213,8 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) } //set a backup value for the path to the war in case it hasn't already been set - properties.put(Deployable.WAR, path.toString()); - return initializeContextHandler(context, path, properties); + appAttributes.setAttribute(Deployable.WAR, path.toString()); + return initializeContextHandler(context, path, appAttributes); } finally { @@ -508,36 +222,127 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) } } - protected Object applyXml(Object context, Path xml, String environment, Map properties) throws Exception + /** + * Get the default {@link Environment} name for discovered web applications that + * do not declare the {@link Environment} that they belong to. + * + *

        + * Falls back to {@link Environment#getAll()} list, and returns + * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} + *

        + * + * @return the default environment name. + */ + public String getDefaultEnvironmentName() + { + if (defaultEnvironmentName == null) + { + return Environment.getAll().stream() + .map(Environment::getName) + .max(Deployable.ENVIRONMENT_COMPARATOR) + .orElse(null); + } + return defaultEnvironmentName; + } + + public void setDefaultEnvironmentName(String name) + { + this.defaultEnvironmentName = name; + } + + @Deprecated + public Map getProperties(Environment environment) + { + return asProperties(environment); + } + + public void loadProperties(Environment environment, Path path) throws IOException + { + try (InputStream inputStream = Files.newInputStream(path)) + { + loadProperties(environment, inputStream); + } + } + + public void loadProperties(Environment environment, Resource resource) throws IOException + { + try (InputStream inputStream = IOResources.asInputStream(resource)) + { + loadProperties(environment, inputStream); + } + } + + public void loadPropertiesFromString(Environment environment, String path) throws IOException + { + loadProperties(environment, Path.of(path)); + } + + protected Object applyXml(Object context, Path xml, Environment environment, Attributes attributes) throws Exception { if (!FileID.isXml(xml)) return null; - XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, properties) + XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, asProperties(attributes)) { @Override public void initializeDefaults(Object context) { super.initializeDefaults(context); - ContextProvider.this.initializeContextHandler(context, xml, properties); + ContextProvider.this.initializeContextHandler(context, xml, attributes); } }; - xmlc.getIdMap().put("Environment", environment); + xmlc.getIdMap().put("Environment", environment.getName()); xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml); - // If it is a core context environment, then look for a classloader - ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(xml) : null; - if (coreContextClassLoader != null) - Thread.currentThread().setContextClassLoader(coreContextClassLoader); + // Put all Environment attributes into XMLC as properties that can be used. + attributes.getAttributeNameSet() + .stream() + .filter(k -> !k.startsWith("jetty.home") && + !k.startsWith("jetty.base") && + !k.startsWith("jetty.webapps")) + .forEach(k -> + { + String v = Objects.toString(attributes.getAttribute(k)); + xmlc.getProperties().put(k, v); + }); - // Create or configure the context - if (context == null) - return xmlc.configure(); + // Run configure against appropriate classloader. + ClassLoader xmlClassLoader = getXmlClassLoader(environment, xml); + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(xmlClassLoader); - return xmlc.configure(context); + try + { + // Create or configure the context + if (context == null) + return xmlc.configure(); + + return xmlc.configure(context); + } + finally + { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } } + /** + * Return a ClassLoader that can load a {@link Environment#CORE} based webapp + * that is entirely defined within the {@code webapps/} directory. + * + *

        The resulting ClassLoader consists of the following entries:

        + *
          + *
        1. The java archive {@code .jar}
        2. + *
        3. The java archives {@code .d/lib/*.jar}
        4. + *
        5. The directory {@code .d/classes/}
        6. + *
        + * + * @param path to XML defining this webapp, must be absolute, and cannot be in root directory of drive. + * filename of XML will be used to determine the {@code } of the other entries in this + * ClassLoader. + * @return the classloader for this CORE environment webapp. + * @throws IOException if unable to apply to create classloader. + */ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException { Path webapps = path.getParent(); @@ -545,6 +350,8 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException List urls = new ArrayList<>(); // Is there a matching jar file? + // TODO: both files can be there depending on FileSystem, is this still sane? + // TODO: what about other capitalization? eg: ".Jar" ? Path contextJar = webapps.resolve(basename + ".jar"); if (!Files.exists(contextJar)) contextJar = webapps.resolve(basename + ".JAR"); @@ -552,7 +359,7 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException urls.add(contextJar.toUri().toURL()); // Is there a matching lib directory? - Path libDir = webapps.resolve(basename + ".d" + path.getFileSystem().getSeparator() + "lib"); + Path libDir = webapps.resolve(basename + ".d/lib"); if (Files.exists(libDir) && Files.isDirectory(libDir)) { try (Stream paths = Files.list(libDir)) @@ -574,7 +381,7 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException } // Is there a matching lib directory? - Path classesDir = webapps.resolve(basename + ".d" + path.getFileSystem().getSeparator() + "classes"); + Path classesDir = webapps.resolve(basename + ".d/classes"); if (Files.exists(classesDir) && Files.isDirectory(libDir)) urls.add(classesDir.toUri().toURL()); @@ -586,6 +393,53 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); } + protected ContextHandler initializeContextHandler(Object context, Path path, Attributes attributes) + { + if (LOG.isDebugEnabled()) + LOG.debug("initializeContextHandler {}", context); + // find the ContextHandler + ContextHandler contextHandler; + if (context instanceof ContextHandler handler) + contextHandler = handler; + else if (Supplier.class.isAssignableFrom(context.getClass())) + { + @SuppressWarnings("unchecked") + Supplier provider = (Supplier)context; + contextHandler = provider.get(); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a context {}", context); + return null; + } + + assert contextHandler != null; + + initializeContextPath(contextHandler, path); + + if (Files.isDirectory(path)) + { + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); + } + + // pass through properties as attributes directly + attributes.getAttributeNameSet().stream() + .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) + .map((name) -> name.substring(Deployable.ATTRIBUTE_PREFIX.length())) + .forEach((name) -> contextHandler.setAttribute(name, attributes.getAttribute(name))); + + String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); + if (StringUtil.isNotBlank(contextPath)) + contextHandler.setContextPath(contextPath); + + if (context instanceof Deployable deployable) + deployable.initializeDefaults(attributes); + + return contextHandler; + } + protected void initializeContextPath(ContextHandler context, Path path) { // Strip any 3 char extension from non directories @@ -654,4 +508,302 @@ protected void pathChanged(Path path) throws Exception if (isDeployable(path)) super.pathChanged(path); } + + /** + * Get the ClassLoader appropriate for applying Jetty XML. + * @param environment the environment to use + * @param xml the path to the XML + * @return the appropriate ClassLoader. + * @throws IOException if unable to create the ClassLoader + */ + private ClassLoader getXmlClassLoader(Environment environment, Path xml) throws IOException + { + if (Environment.CORE.equals(environment)) + { + // this XML belongs to a CORE deployment. + return findCoreContextClassLoader(xml); + } + else + { + return environment.getClassLoader(); + } + } + + private Attributes initAttributes(Environment environment, App app) throws IOException + { + Attributes attributes = new Attributes.Mapped(); + + // Start appAttributes with Attributes from Environment + environment.getAttributeNameSet().forEach((key) -> + attributes.setAttribute(key, environment.getAttribute(key))); + + // TODO: double check if an empty environment name makes sense. Will the default environment name? + String env = app.getEnvironmentName(); + + if (StringUtil.isNotBlank(env)) + { + // Load environment specific properties files + Path parent = app.getPath().getParent(); + Properties envProps = loadEnvironmentProperties(parent, env); + + envProps.stringPropertyNames().forEach( + k -> attributes.setAttribute(k, envProps.getProperty(k)) + ); + } + + // Overlay the app properties + app.getProperties().forEach(attributes::setAttribute); + + return attributes; + } + + /** + * Load all of the {@link Environment} specific {@code [-].properties} files + * found in the directory provided. + * + *

        + * All found properties files are first sorted by filename, then loaded one by one into + * a single {@link Properties} instance. + *

        + * + * @param directory the directory to load environment properties from. + * @param env the environment name + */ + private Properties loadEnvironmentProperties(Path directory, String env) throws IOException + { + Properties props = new Properties(); + List envPropertyFiles = new ArrayList<>(); + + // Get all environment specific properties files for this environment, + // order them according to the lexical ordering of the filenames + try (Stream paths = Files.list(directory)) + { + envPropertyFiles = paths.filter(Files::isRegularFile) + .map(directory::relativize) + .filter(p -> + { + String name = p.getName(0).toString(); + if (!name.endsWith(".properties")) + return false; + if (!name.startsWith(env)) + return false; + return true; + }).sorted().toList(); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Environment property files {}", envPropertyFiles); + + // Load each *.properties file + for (Path file : envPropertyFiles) + { + Path resolvedFile = directory.resolve(file); + if (Files.exists(resolvedFile)) + { + Properties tmp = new Properties(); + try (InputStream stream = Files.newInputStream(resolvedFile)) + { + tmp.load(stream); + //put each property into our substitution pool + tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); + } + } + } + + return props; + } + + private void loadProperties(Environment environment, InputStream inputStream) throws IOException + { + Properties props = new Properties(); + props.load(inputStream); + props.stringPropertyNames().forEach((name) -> + environment.setAttribute(name, props.getProperty(name))); + } + + /** + * Builder of a deployment configuration for a specific {@link Environment}. + * + *

        + * Results in {@link Attributes} for {@link Environment} containing the + * deployment configuration (as {@link Deployable} keys) that is applied to all deployable + * apps belonging to that {@link Environment}. + *

        + */ + public static class EnvBuilder + { + // Using setters in this class to allow jetty-xml + // syntax to skip setting of an environment attribute if property is unset, + // allowing the in code values to be same defaults as they are in embedded-jetty. + + private final Environment environment; + + public EnvBuilder(String name) + { + environment = Environment.ensure(name); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} attribute. + * + * @param configurations The configuration class names as a comma separated list + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String configurations) + { + setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. + * + * @param configurations The configuration class names. + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String[] configurations) + { + if (configurations == null) + environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); + else + environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); + } + + /** + * This is equivalent to setting the {@link Deployable#CONTAINER_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning container jars + * @see Deployable#CONTAINER_SCAN_JARS + */ + public void setContainerScanJarPattern(String pattern) + { + environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); + } + + /** + * The name of the class that this environment uses to create {@link ContextHandler} + * instances (can be class that implements {@code java.util.function.Supplier} + * as well). + * + * @param classname the classname for this environment's context deployable. + * @see Deployable#CONTEXT_HANDLER_CLASS + */ + public void setContextHandlerClass(String classname) + { + environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); + } + + /** + * Set the defaultsDescriptor. + * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} attribute. + * + * @param defaultsDescriptor the defaultsDescriptor to set + * @see Deployable#DEFAULTS_DESCRIPTOR + */ + public void setDefaultsDescriptor(String defaultsDescriptor) + { + environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); + } + + /** + * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} attribute. + * + * @param extractWars the extractWars to set + * @see Deployable#EXTRACT_WARS + */ + public void setExtractWars(boolean extractWars) + { + environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); + } + + /** + * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} attribute. + * + * @param parentLoaderPriority the parentLoaderPriority to set + * @see Deployable#PARENT_LOADER_PRIORITY + */ + public void setParentLoaderPriority(boolean parentLoaderPriority) + { + environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_EXCLUSION_PATTERN} property. + * + * @param pattern The regex pattern to exclude ServletContainerInitializers from executing + * @see Deployable#SCI_EXCLUSION_PATTERN + */ + public void setServletContainerInitializerExclusionPattern(String pattern) + { + environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_ORDER} property. + * + * @param order The ordered list of ServletContainerInitializer classes to run + * @see Deployable#SCI_ORDER + */ + public void setServletContainerInitializerOrder(String order) + { + environment.setAttribute(Deployable.SCI_ORDER, order); + } + + /** + * This is equivalent to setting the {@link Deployable#WEBINF_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning web-inf jars + * @see Deployable#WEBINF_SCAN_JARS + */ + public void setWebInfScanJarPattern(String pattern) + { + environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); + } + } + + public class Filter implements FilenameFilter + { + @Override + public boolean accept(File dir, String name) + { + if (dir == null || !dir.canRead()) + return false; + + // Accept XML files and WARs + if (FileID.isXml(name) || FileID.isWebArchive(name)) + return true; + + Path path = dir.toPath().resolve(name); + + // Ignore any other file that are not directories + if (!Files.isDirectory(path)) + return false; + + // Don't deploy monitored resources + if (getMonitoredResources().stream().map(Resource::getPath).anyMatch(path::equals)) + return false; + + // Ignore hidden directories + if (name.startsWith(".")) + return false; + + String lowerName = name.toLowerCase(Locale.ENGLISH); + + // is it a nominated config directory + if (lowerName.endsWith(".d")) + return false; + + // ignore source control directories + if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) + return false; + + // ignore directories that have sibling war or XML file + if (Files.exists(dir.toPath().resolve(name + ".war")) || + Files.exists(dir.toPath().resolve(name + ".WAR")) || + Files.exists(dir.toPath().resolve(name + ".xml")) || + Files.exists(dir.toPath().resolve(name + ".XML"))) + return false; + + return true; + } + } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java deleted file mode 100644 index 3bd0039cd846..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/EnvironmentContextProvider.java +++ /dev/null @@ -1,798 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.io.IOResources; -import org.eclipse.jetty.server.Deployable; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.Attributes; -import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.component.Environment; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceFactory; -import org.eclipse.jetty.xml.XmlConfiguration; -import org.slf4j.LoggerFactory; - -/** - * The webapps directory scanning provider. - *

        This provider scans one or more directories (typically "webapps") for contexts to - * deploy, which may be: - *

        - *
          - *
        • A standard WAR file (must end in ".war")
        • - *
        • A directory containing an expanded WAR file
        • - *
        • A directory containing static content
        • - *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • - *
        - *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * implements some heuristics to ignore some files found in the scans: - *

        - *
          - *
        • Hidden files (starting with {@code "."}) are ignored
        • - *
        • Directories with names ending in {@code ".d"} are ignored
        • - *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • - *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be - * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • - *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • - *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to - * be configured by the XML and only the XML is deployed. - *
        - *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and - * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". - * The properties will be initialized with: - *

        - *
          - *
        • The properties set on the application via {@link App#getProperties()}
        • - *
        • The app specific properties file {@code webapps/.properties}
        • - *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • - *
        • The {@link Attributes} from the {@link Environment}
        • - *
        - */ -@ManagedObject("Provider for start-up deployment of webapps based on presence in directory") -public class EnvironmentContextProvider extends ScanningAppProvider -{ - private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(EnvironmentContextProvider.class); - private String defaultEnvironmentName; - - public EnvironmentContextProvider() - { - super(); - setFilenameFilter(new Filter()); - setScanInterval(0); - } - - public String getDefaultEnvironmentName() - { - if (defaultEnvironmentName == null) - { - return Environment.getAll().stream() - .map(Environment::getName) - .max(Deployable.ENVIRONMENT_COMPARATOR) - .orElse(null); - } - return defaultEnvironmentName; - } - - public void setDefaultEnvironmentName(String name) - { - this.defaultEnvironmentName = name; - } - - private static Map asProperties(Attributes attributes) - { - Map props = new HashMap<>(); - attributes.getAttributeNameSet().stream() - .map((name) -> - { - // undo old prefixed entries - if (name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - return name.substring(Deployable.ATTRIBUTE_PREFIX.length()); - else - return name; - }) - .forEach((name) -> props.put(name, Objects.toString(attributes.getAttribute(name)))); - return props; - } - - @Override - public ContextHandler createContextHandler(final App app) throws Exception - { - Environment environment = Environment.get(app.getEnvironmentName()); - - if (environment == null) - { - LOG.warn("Environment [{}] is not available for app [{}]. The available environments are: {}", - app.getEnvironmentName(), - app, - Environment.getAll().stream() - .map(Environment::getName) - .collect(Collectors.joining(",", "[", "]")) - ); - return null; - } - - if (LOG.isDebugEnabled()) - LOG.debug("createContextHandler {} in {}", app, environment); - - ClassLoader old = Thread.currentThread().getContextClassLoader(); - try - { - Thread.currentThread().setContextClassLoader(environment.getClassLoader()); - - // Create de-aliased file - Path path = app.getPath().toRealPath(); - if (!Files.exists(path)) - throw new IllegalStateException("App resource does not exist " + path); - - // prepare app attributes to use for app deployment - Attributes appAttributes = initAttributes(environment, app); - - Object context = null; - - // check if there is a specific ContextHandler type to create set in the - // properties associated with the webapp. If there is, we create it _before_ - // applying the environment xml file. - String contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); - if (contextHandlerClassName != null) - context = Class.forName(contextHandlerClassName).getDeclaredConstructor().newInstance(); - - // Collect the optional environment context xml files. - // Order them according to the name of their property key names. - List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() - .stream() - .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) - .map(k -> Path.of((String)appAttributes.getAttribute(k))) - .sorted() - .toList(); - - // apply each environment context xml file - for (Path envXmlPath : sortedEnvXmlPaths) - { - if (LOG.isDebugEnabled()) - LOG.debug("Applying environment specific context file {}", envXmlPath); - context = applyXml(context, envXmlPath, environment, appAttributes); - } - - // Handle a context XML file - if (FileID.isXml(path)) - { - context = applyXml(context, path, environment, appAttributes); - - // Look for the contextHandler itself - ContextHandler contextHandler = null; - if (context instanceof ContextHandler c) - contextHandler = c; - else if (context instanceof Supplier supplier) - { - Object nestedContext = supplier.get(); - if (nestedContext instanceof ContextHandler c) - contextHandler = c; - } - if (contextHandler == null) - throw new IllegalStateException("Unknown context type of " + context); - - return contextHandler; - } - // Otherwise it must be a directory or an archive - else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) - { - throw new IllegalStateException("unable to create ContextHandler for " + app); - } - - // Build the web application if necessary - if (context == null) - { - contextHandlerClassName = (String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); - if (StringUtil.isBlank(contextHandlerClassName)) - throw new IllegalStateException("No ContextHandler classname for " + app); - Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); - if (contextHandlerClass == null) - throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app); - - context = contextHandlerClass.getDeclaredConstructor().newInstance(); - } - - //set a backup value for the path to the war in case it hasn't already been set - appAttributes.setAttribute(Deployable.WAR, path.toString()); - return initializeContextHandler(context, path, appAttributes); - } - finally - { - Thread.currentThread().setContextClassLoader(old); - } - } - - @Deprecated - public Map getProperties(Environment environment) - { - return asProperties(environment); - } - - public void loadProperties(Environment environment, Path path) throws IOException - { - try (InputStream inputStream = Files.newInputStream(path)) - { - loadProperties(environment, inputStream); - } - } - - public void loadProperties(Environment environment, Resource resource) throws IOException - { - try (InputStream inputStream = IOResources.asInputStream(resource)) - { - loadProperties(environment, inputStream); - } - } - - public void loadPropertiesFromString(Environment environment, String path) throws IOException - { - loadProperties(environment, Path.of(path)); - } - - protected Object applyXml(Object context, Path xml, Environment environment, Attributes attributes) throws Exception - { - if (!FileID.isXml(xml)) - return null; - - XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, asProperties(attributes)) - { - @Override - public void initializeDefaults(Object context) - { - super.initializeDefaults(context); - EnvironmentContextProvider.this.initializeContextHandler(context, xml, attributes); - } - }; - - xmlc.getIdMap().put("Environment", environment.getName()); - xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml); - - // Put all Environment attributes into XMLC as properties that can be used. - attributes.getAttributeNameSet() - .stream() - .filter(k -> !k.startsWith("jetty.home") && - !k.startsWith("jetty.base") && - !k.startsWith("jetty.webapps")) - .forEach(k -> - { - String v = Objects.toString(attributes.getAttribute(k)); - xmlc.getProperties().put(k, v); - }); - - // Run configure against appropriate classloader. - ClassLoader xmlClassLoader = getXmlClassLoader(environment, xml); - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(xmlClassLoader); - - try - { - // Create or configure the context - if (context == null) - return xmlc.configure(); - - return xmlc.configure(context); - } - finally - { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - /** - * Return a ClassLoader that can load a {@link Environment#CORE} based webapp - * that is entirely defined within the {@code webapps/} directory. - * - *

        The resulting ClassLoader consists of the following entries:

        - *
          - *
        1. The java archive {@code .jar}
        2. - *
        3. The java archives {@code .d/lib/*.jar}
        4. - *
        5. The directory {@code .d/classes/}
        6. - *
        - * - * @param path to XML defining this webapp, must be absolute, and cannot be in root directory of drive. - * filename of XML will be used to determine the {@code } of the other entries in this - * ClassLoader. - * @return the classloader for this CORE environment webapp. - * @throws IOException if unable to apply to create classloader. - */ - protected ClassLoader findCoreContextClassLoader(Path path) throws IOException - { - Path webapps = path.getParent(); - String basename = FileID.getBasename(path); - List urls = new ArrayList<>(); - - // Is there a matching jar file? - // TODO: both files can be there depending on FileSystem, is this still sane? - // TODO: what about other capitalization? eg: ".Jar" ? - Path contextJar = webapps.resolve(basename + ".jar"); - if (!Files.exists(contextJar)) - contextJar = webapps.resolve(basename + ".JAR"); - if (Files.exists(contextJar)) - urls.add(contextJar.toUri().toURL()); - - // Is there a matching lib directory? - Path libDir = webapps.resolve(basename + ".d/lib"); - if (Files.exists(libDir) && Files.isDirectory(libDir)) - { - try (Stream paths = Files.list(libDir)) - { - paths.filter(FileID::isJavaArchive) - .map(Path::toUri) - .forEach(uri -> - { - try - { - urls.add(uri.toURL()); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - }); - } - } - - // Is there a matching lib directory? - Path classesDir = webapps.resolve(basename + ".d/classes"); - if (Files.exists(classesDir) && Files.isDirectory(libDir)) - urls.add(classesDir.toUri().toURL()); - - if (LOG.isDebugEnabled()) - LOG.debug("Core classloader for {}", urls); - - if (urls.isEmpty()) - return null; - return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); - } - - protected ContextHandler initializeContextHandler(Object context, Path path, Attributes attributes) - { - if (LOG.isDebugEnabled()) - LOG.debug("initializeContextHandler {}", context); - // find the ContextHandler - ContextHandler contextHandler; - if (context instanceof ContextHandler handler) - contextHandler = handler; - else if (Supplier.class.isAssignableFrom(context.getClass())) - { - @SuppressWarnings("unchecked") - Supplier provider = (Supplier)context; - contextHandler = provider.get(); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Not a context {}", context); - return null; - } - - assert contextHandler != null; - - initializeContextPath(contextHandler, path); - - if (Files.isDirectory(path)) - { - ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); - contextHandler.setBaseResource(resourceFactory.newResource(path)); - } - - // pass through properties as attributes directly - attributes.getAttributeNameSet().stream() - .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - .map((name) -> name.substring(Deployable.ATTRIBUTE_PREFIX.length())) - .forEach((name) -> contextHandler.setAttribute(name, attributes.getAttribute(name))); - - String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); - if (StringUtil.isNotBlank(contextPath)) - contextHandler.setContextPath(contextPath); - - if (context instanceof Deployable deployable) - deployable.initializeDefaults(attributes); - - return contextHandler; - } - - protected void initializeContextPath(ContextHandler context, Path path) - { - // Strip any 3 char extension from non directories - String basename = FileID.getBasename(path); - String contextPath = basename; - - // special case of archive (or dir) named "root" is / context - if (contextPath.equalsIgnoreCase("root")) - { - contextPath = "/"; - } - // handle root with virtual host form - else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) - { - int dash = contextPath.indexOf('-'); - String virtual = contextPath.substring(dash + 1); - context.setVirtualHosts(Arrays.asList(virtual.split(","))); - contextPath = "/"; - } - - // Ensure "/" is Prepended to all context paths. - if (contextPath.charAt(0) != '/') - contextPath = "/" + contextPath; - - // Set the display name and context Path - context.setDisplayName(basename); - context.setContextPath(contextPath); - } - - protected boolean isDeployable(Path path) - { - String basename = FileID.getBasename(path); - - //is the file that changed a directory? - if (Files.isDirectory(path)) - { - // deploy if there is not a .xml or .war file of the same basename? - return !Files.exists(path.getParent().resolve(basename + ".xml")) && - !Files.exists(path.getParent().resolve(basename + ".XML")) && - !Files.exists(path.getParent().resolve(basename + ".war")) && - !Files.exists(path.getParent().resolve(basename + ".WAR")); - } - - // deploy if it is a .war and there is not a .xml for of the same basename - if (FileID.isWebArchive(path)) - { - // if a .xml file exists for it - return !Files.exists(path.getParent().resolve(basename + ".xml")) && - !Files.exists(path.getParent().resolve(basename + ".XML")); - } - - // otherwise only deploy an XML - return FileID.isXml(path); - } - - @Override - protected void pathAdded(Path path) throws Exception - { - if (isDeployable(path)) - super.pathAdded(path); - } - - @Override - protected void pathChanged(Path path) throws Exception - { - if (isDeployable(path)) - super.pathChanged(path); - } - - /** - * Get the ClassLoader appropriate for applying Jetty XML. - * @param environment the environment to use - * @param xml the path to the XML - * @return the appropriate ClassLoader. - * @throws IOException if unable to create the ClassLoader - */ - private ClassLoader getXmlClassLoader(Environment environment, Path xml) throws IOException - { - if (Environment.CORE.equals(environment)) - { - // this XML belongs to a CORE deployment. - return findCoreContextClassLoader(xml); - } - else - { - return environment.getClassLoader(); - } - } - - private Attributes initAttributes(Environment environment, App app) throws IOException - { - Attributes attributes = new Attributes.Mapped(); - - // Start appAttributes with Attributes from Environment - environment.getAttributeNameSet().forEach((key) -> - attributes.setAttribute(key, environment.getAttribute(key))); - - // TODO: double check if an empty environment name makes sense. Will the default environment name? - String env = app.getEnvironmentName(); - - if (StringUtil.isNotBlank(env)) - { - // Load environment specific properties files - Path parent = app.getPath().getParent(); - Properties envProps = loadEnvironmentProperties(parent, env); - - envProps.stringPropertyNames().forEach( - k -> attributes.setAttribute(k, envProps.getProperty(k)) - ); - } - - // Overlay the app properties - app.getProperties().forEach(attributes::setAttribute); - - return attributes; - } - - /** - * Load all of the {@link Environment} specific {@code [-].properties} files - * found in the directory provided. - * - *

        - * All found properties files are first sorted by filename, then loaded one by one into - * a single {@link Properties} instance. - *

        - * - * @param directory the directory to load environment properties from. - * @param env the environment name - */ - private Properties loadEnvironmentProperties(Path directory, String env) throws IOException - { - Properties props = new Properties(); - List envPropertyFiles = new ArrayList<>(); - - // Get all environment specific properties files for this environment, - // order them according to the lexical ordering of the filenames - try (Stream paths = Files.list(directory)) - { - envPropertyFiles = paths.filter(Files::isRegularFile) - .map(directory::relativize) - .filter(p -> - { - String name = p.getName(0).toString(); - if (!name.endsWith(".properties")) - return false; - if (!name.startsWith(env)) - return false; - return true; - }).sorted().toList(); - } - - if (LOG.isDebugEnabled()) - LOG.debug("Environment property files {}", envPropertyFiles); - - // Load each *.properties file - for (Path file : envPropertyFiles) - { - Path resolvedFile = directory.resolve(file); - if (Files.exists(resolvedFile)) - { - Properties tmp = new Properties(); - try (InputStream stream = Files.newInputStream(resolvedFile)) - { - tmp.load(stream); - //put each property into our substitution pool - tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); - } - } - } - - return props; - } - - private void loadProperties(Environment environment, InputStream inputStream) throws IOException - { - Properties props = new Properties(); - props.load(inputStream); - props.stringPropertyNames().forEach((name) -> - environment.setAttribute(name, props.getProperty(name))); - } - - /** - * Builder of a deployment configuration for a specific {@link Environment}. - * - *

        - * Results in {@link Attributes} for {@link Environment} containing the - * deployment configuration (as {@link Deployable} keys) that is applied to all deployable - * apps belonging to that {@link Environment}. - *

        - */ - public static class EnvBuilder - { - // Using setters in this class to allow jetty-xml - // syntax to skip setting of an environment attribute if property is unset, - // allowing the in code values to be same defaults as they are in embedded-jetty. - - private final Environment environment; - - public EnvBuilder(String name) - { - environment = Environment.ensure(name); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} attribute. - * - * @param configurations The configuration class names as a comma separated list - * @see Deployable#CONFIGURATION_CLASSES - */ - public void setConfigurationClasses(String configurations) - { - setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * - * @param configurations The configuration class names. - * @see Deployable#CONFIGURATION_CLASSES - */ - public void setConfigurationClasses(String[] configurations) - { - if (configurations == null) - environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); - else - environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); - } - - /** - * This is equivalent to setting the {@link Deployable#CONTAINER_SCAN_JARS} property. - * - * @param pattern The regex pattern to use when bytecode scanning container jars - * @see Deployable#CONTAINER_SCAN_JARS - */ - public void setContainerScanJarPattern(String pattern) - { - environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); - } - - /** - * The name of the class that this environment uses to create {@link ContextHandler} - * instances (can be class that implements {@code java.util.function.Supplier} - * as well). - * - * @param classname the classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS - */ - public void setContextHandlerClass(String classname) - { - environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); - } - - /** - * Set the defaultsDescriptor. - * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} attribute. - * - * @param defaultsDescriptor the defaultsDescriptor to set - * @see Deployable#DEFAULTS_DESCRIPTOR - */ - public void setDefaultsDescriptor(String defaultsDescriptor) - { - environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); - } - - /** - * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} attribute. - * - * @param extractWars the extractWars to set - * @see Deployable#EXTRACT_WARS - */ - public void setExtractWars(boolean extractWars) - { - environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); - } - - /** - * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} attribute. - * - * @param parentLoaderPriority the parentLoaderPriority to set - * @see Deployable#PARENT_LOADER_PRIORITY - */ - public void setParentLoaderPriority(boolean parentLoaderPriority) - { - environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); - } - - /** - * This is equivalent to setting the {@link Deployable#SCI_EXCLUSION_PATTERN} property. - * - * @param pattern The regex pattern to exclude ServletContainerInitializers from executing - * @see Deployable#SCI_EXCLUSION_PATTERN - */ - public void setServletContainerInitializerExclusionPattern(String pattern) - { - environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); - } - - /** - * This is equivalent to setting the {@link Deployable#SCI_ORDER} property. - * - * @param order The ordered list of ServletContainerInitializer classes to run - * @see Deployable#SCI_ORDER - */ - public void setServletContainerInitializerOrder(String order) - { - environment.setAttribute(Deployable.SCI_ORDER, order); - } - - /** - * This is equivalent to setting the {@link Deployable#WEBINF_SCAN_JARS} property. - * - * @param pattern The regex pattern to use when bytecode scanning web-inf jars - * @see Deployable#WEBINF_SCAN_JARS - */ - public void setWebInfScanJarPattern(String pattern) - { - environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); - } - } - - public class Filter implements FilenameFilter - { - @Override - public boolean accept(File dir, String name) - { - if (dir == null || !dir.canRead()) - return false; - - // Accept XML files and WARs - if (FileID.isXml(name) || FileID.isWebArchive(name)) - return true; - - Path path = dir.toPath().resolve(name); - - // Ignore any other file that are not directories - if (!Files.isDirectory(path)) - return false; - - // Don't deploy monitored resources - if (getMonitoredResources().stream().map(Resource::getPath).anyMatch(path::equals)) - return false; - - // Ignore hidden directories - if (name.startsWith(".")) - return false; - - String lowerName = name.toLowerCase(Locale.ENGLISH); - - // is it a nominated config directory - if (lowerName.endsWith(".d")) - return false; - - // ignore source control directories - if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) - return false; - - // ignore directories that have sibling war or XML file - if (Files.exists(dir.toPath().resolve(name + ".war")) || - Files.exists(dir.toPath().resolve(name + ".WAR")) || - Files.exists(dir.toPath().resolve(name + ".xml")) || - Files.exists(dir.toPath().resolve(name + ".XML"))) - return false; - - return true; - } - } -} diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 0c973bf2822e..3f5a24819b34 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -3,18 +3,18 @@ - + ee10 + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath /etc/webdefault-ee10.xml - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index 5ab08e473b7a..415591463449 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -3,18 +3,18 @@ - + ee11 + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath /etc/webdefault-ee11.xml - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index 03ac84cca3cc..f75a07217839 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -3,18 +3,18 @@ - + ee8 + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath /etc/webdefault-ee8.xml - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index adcf0e856a3c..9c7fc4abdb4d 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -3,18 +3,18 @@ - + ee9 + jetty.deploy.defaultsDescriptor + jetty.deploy.defaultsDescriptorPath /etc/webdefault-ee9.xml - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath From 7588d5fd47dc63a75aee2124ce3cee944b0b05f4 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 25 Nov 2024 14:48:03 -0600 Subject: [PATCH 005/104] Introducing ContextProvider.configureEnvironment(String name) --- .../src/main/config/etc/jetty-core-deploy.xml | 38 +---- .../src/main/config/etc/jetty-deploy.xml | 49 +++--- .../config/etc/jetty-deployment-manager.xml | 29 ++++ .../src/main/config/modules/core-deploy.mod | 17 +-- .../src/main/config/modules/deploy.mod | 15 +- .../config/modules/deployment-manager.mod | 12 ++ .../deploy/providers/ContextProvider.java | 140 ++++++++++++------ .../jetty/deploy/DeploymentManagerTest.java | 2 +- .../ContextProviderDeferredStartupTest.java | 7 +- .../ContextProviderRuntimeUpdatesTest.java | 5 +- .../providers/ContextProviderStartupTest.java | 6 +- .../resources/jetty-core-deploy-custom.xml | 21 +++ .../resources/jetty-deploymgr-contexts.xml | 40 ----- .../jetty/ee10/test/DeploymentErrorTest.java | 2 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 52 +++---- .../src/main/config/modules/ee10-deploy.mod | 9 -- .../src/main/config/etc/jetty-ee11-deploy.xml | 52 +++---- .../src/main/config/modules/ee11-deploy.mod | 6 - .../src/main/config/etc/jetty-ee8-deploy.xml | 52 +++---- .../src/main/config/modules/ee8-deploy.mod | 6 - .../jetty/ee9/test/DeploymentErrorTest.java | 2 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 52 +++---- .../src/main/config/modules/ee9-deploy.mod | 6 - 23 files changed, 326 insertions(+), 294 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml create mode 100644 jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod create mode 100644 jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml delete mode 100644 jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml index e4d6b0c4acad..7adf72376086 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml @@ -1,37 +1,13 @@ - + - - - org.eclipse.jetty.deploy.DeploymentManager + + + core + + + - - - contextHandlerClass - - - - - - - - core - - - - - - - - - - - - - - - - diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 02d9dc902096..afc5c4e338f7 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -2,30 +2,31 @@ - - - - - + + + + + - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + + + diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml new file mode 100644 index 000000000000..6d7a6b7dd238 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod index 8de1a2de16e4..bf893a6f3cf8 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod +++ b/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod @@ -15,21 +15,6 @@ webapps/ [xml] etc/jetty-core-deploy.xml -[ini] -contextHandlerClass?=org.eclipse.jetty.server.handler.ResourceHandler$ResourceContext - [ini-template] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - -# Defer Initial Scan -# true to have the initial scan deferred until the Server component is started. -# Note: deploy failures do not fail server startup in a deferred initial scan mode. -# false (default) to have initial scan occur as normal. -# jetty.deploy.deferInitialScan=false - -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 - -## Default ContextHandler class for core deployments +## Default ContextHandler class for "core" environment deployments # contextHandlerClass=org.eclipse.jetty.server.handler.ResourceHandler$ResourceContext diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod index a4db8cd3efa5..1dff1db7ef9e 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod +++ b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod @@ -1,8 +1,9 @@ [description] -This module enables web application deployment from the `$JETTY_BASE/webapps` directory. +This module enables web application context deployment from the `$JETTY_BASE/webapps` directory. [depend] server +deployment-manager [lib] lib/jetty-deploy-${jetty.version}.jar @@ -13,3 +14,15 @@ webapps/ [xml] etc/jetty-deploy.xml +[ini-template] +## Monitored directory name (relative to $jetty.base) +# jetty.deploy.monitoredDir=webapps + +# Defer Initial Scan +# true to have the initial scan deferred until the Server component is started. +# Note: deploy failures do not fail server startup in a deferred initial scan mode. +# false (default) to have initial scan occur as normal. +# jetty.deploy.deferInitialScan=false + +## Monitored directory scan period (seconds) +# jetty.deploy.scanInterval=0 diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod b/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod new file mode 100644 index 000000000000..d9e73c73b41e --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod @@ -0,0 +1,12 @@ +[description] +This module enables the DeploymentManager + +[depend] +server + +[lib] +lib/jetty-deploy-${jetty.version}.jar + +[xml] +etc/jetty-deployment-manager.xml + diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index cfeaab2c55de..ee46a83037ec 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -21,6 +21,7 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -51,39 +52,39 @@ /** * The webapps directory scanning provider. - * *

        This provider scans one or more directories (typically "webapps") for contexts to - * * deploy, which may be: - * *

        - * *
          - * *
        • A standard WAR file (must end in ".war")
        • - * *
        • A directory containing an expanded WAR file
        • - * *
        • A directory containing static content
        • - * *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • - * *
        - * *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * * implements some heuristics to ignore some files found in the scans: - * *

        - * *
          - * *
        • Hidden files (starting with {@code "."}) are ignored
        • - * *
        • Directories with names ending in {@code ".d"} are ignored
        • - * *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • - * *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be - * * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • - * *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • - * *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to - * * be configured by the XML and only the XML is deployed. - * *
        - * *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and - * * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". - * * The properties will be initialized with: - * *

        - * *
          - * *
        • The properties set on the application via {@link App#getProperties()}
        • - * *
        • The app specific properties file {@code webapps/.properties}
        • - * *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • - * *
        • The {@link Attributes} from the {@link Environment}
        • - * *
        + * *

        This provider scans one or more directories (typically "webapps") for contexts to + * * deploy, which may be: + * *

        + * *
          + * *
        • A standard WAR file (must end in ".war")
        • + * *
        • A directory containing an expanded WAR file
        • + * *
        • A directory containing static content
        • + * *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • + * *
        + * *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * * implements some heuristics to ignore some files found in the scans: + * *

        + * *
          + * *
        • Hidden files (starting with {@code "."}) are ignored
        • + * *
        • Directories with names ending in {@code ".d"} are ignored
        • + * *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • + * *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be + * * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • + * *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be + * * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • + * *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to + * * be configured by the XML and only the XML is deployed. + * *
        + * *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and + * * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". + * * The properties will be initialized with: + * *

        + * *
          + * *
        • The properties set on the application via {@link App#getProperties()}
        • + * *
        • The app specific properties file {@code webapps/.properties}
        • + * *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • + * *
        • The {@link Attributes} from the {@link Environment}
        • + * *
        */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") public class ContextProvider extends ScanningAppProvider @@ -117,7 +118,8 @@ private static Map asProperties(Attributes attributes) @Override public ContextHandler createContextHandler(final App app) throws Exception { - Environment environment = Environment.get(app.getEnvironmentName()); + String envName = app.getEnvironmentName(); + Environment environment = Environment.get(StringUtil.isNotBlank(envName) ? envName : getDefaultEnvironmentName()); if (environment == null) { @@ -161,7 +163,14 @@ public ContextHandler createContextHandler(final App app) throws Exception List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() .stream() .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) - .map(k -> Path.of((String)appAttributes.getAttribute(k))) + .map(k -> + { + Path envXmlPath = Paths.get((String)appAttributes.getAttribute(k)); + if (!envXmlPath.isAbsolute()) + envXmlPath = getMonitoredDirResource().getPath().getParent().resolve(envXmlPath); + return envXmlPath; + }) + .filter(Files::isRegularFile) .sorted() .toList(); @@ -227,8 +236,8 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) * do not declare the {@link Environment} that they belong to. * *

        - * Falls back to {@link Environment#getAll()} list, and returns - * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} + * Falls back to {@link Environment#getAll()} list, and returns + * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} *

        * * @return the default environment name. @@ -277,6 +286,46 @@ public void loadPropertiesFromString(Environment environment, String path) throw loadProperties(environment, Path.of(path)); } + /** + * Configure the Environment specific Deploy settings. + * + * @param name the name of the environment. + * @return the deployment configuration for the {@link Environment}. + */ + public EnvironmentConfig configureEnvironment(String name) + { + return new EnvironmentConfig(Environment.ensure(name)); + } + + /** + * To enable support for an {@link Environment}, just ensure it exists. + * + *

        + * Eg: {@code Environment.ensure("ee11");} + *

        + * + *

        + * To configure Environment specific deployment {@link Attributes}, + * either set the appropriate {@link Deployable} attribute via {@link Attributes#setAttribute(String, Object)}, + * or use the convenience class {@link EnvironmentConfig}. + *

        + * + *
        {@code
        +     * ContextProvider provider = new ContextProvider();
        +     * ContextProvider.EnvironmentConfig envbuilder = provider.configureEnvironment("ee10");
        +     * envbuilder.setExtractWars(true);
        +     * envbuilder.setParentLoaderPriority(false);
        +     * }
        + * + * @see #configureEnvironment(String) instead + * @deprecated not used anymore. + */ + @Deprecated(since = "12.1.0", forRemoval = true) + public void setEnvironmentName(String name) + { + Environment.ensure(name); + } + protected Object applyXml(Object context, Path xml, Environment environment, Attributes attributes) throws Exception { if (!FileID.isXml(xml)) @@ -511,6 +560,7 @@ protected void pathChanged(Path path) throws Exception /** * Get the ClassLoader appropriate for applying Jetty XML. + * * @param environment the environment to use * @param xml the path to the XML * @return the appropriate ClassLoader. @@ -562,8 +612,8 @@ private Attributes initAttributes(Environment environment, App app) throws IOExc * found in the directory provided. * *

        - * All found properties files are first sorted by filename, then loaded one by one into - * a single {@link Properties} instance. + * All found properties files are first sorted by filename, then loaded one by one into + * a single {@link Properties} instance. *

        * * @param directory the directory to load environment properties from. @@ -625,12 +675,12 @@ private void loadProperties(Environment environment, InputStream inputStream) th * Builder of a deployment configuration for a specific {@link Environment}. * *

        - * Results in {@link Attributes} for {@link Environment} containing the - * deployment configuration (as {@link Deployable} keys) that is applied to all deployable - * apps belonging to that {@link Environment}. + * Results in {@link Attributes} for {@link Environment} containing the + * deployment configuration (as {@link Deployable} keys) that is applied to all deployable + * apps belonging to that {@link Environment}. *

        */ - public static class EnvBuilder + public static class EnvironmentConfig { // Using setters in this class to allow jetty-xml // syntax to skip setting of an environment attribute if property is unset, @@ -638,9 +688,9 @@ public static class EnvBuilder private final Environment environment; - public EnvBuilder(String name) + private EnvironmentConfig(Environment environment) { - environment = Environment.ensure(name); + this.environment = environment; } /** diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 2bd5d43f3646..432501a67e62 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -165,7 +165,7 @@ public void testXmlConfigured(WorkDir workDir) throws Exception jetty = new XmlConfiguredJetty(testdir); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); - jetty.addConfiguration("jetty-deploymgr-contexts.xml"); + jetty.addConfiguration("jetty-core-deploy-custom.xml"); // Should not throw an Exception jetty.load(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java index cda077a79211..c85948ce1788 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java @@ -27,14 +27,13 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.component.Container; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -68,7 +67,9 @@ public void testDelayedDeploy() throws Exception jetty = new XmlConfiguredJetty(realBase); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); - jetty.addConfiguration("jetty-deploymgr-contexts.xml"); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration("jetty-core-deploy-custom.xml"); // Put a context into the base jetty.copyWebapp("bar-core-context.xml", "bar.xml"); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java index 977db71608ea..a600972b985c 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.Scanner; @@ -80,7 +81,9 @@ public void startJetty() throws Exception { jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); - jetty.addConfiguration("jetty-deploymgr-contexts.xml"); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration("jetty-core-deploy-custom.xml"); // Should not throw an Exception jetty.load(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index a68db97d0e66..9fc7c0a25e0b 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -37,7 +37,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -63,7 +62,9 @@ public void setupEnvironment() throws Exception jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); - jetty.addConfiguration("jetty-deploymgr-contexts.xml"); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration("jetty-core-deploy-custom.xml"); // Setup initial context jetty.copyWebapp("bar-core-context.xml", "bar.xml"); @@ -167,7 +168,6 @@ public void testNonEnvironmentPropertyFileNotApplied() throws Exception /** * Test that properties of the same name will be overridden, in the order of the name of the .properties file - * @throws Exception */ @Test public void testPropertyOverriding() throws Exception diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml new file mode 100644 index 000000000000..748ab6ecbda8 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -0,0 +1,21 @@ + + + + + core + + + + + + core + + + + + + + + + + diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml deleted file mode 100644 index 0b3431bd2225..000000000000 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - core - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 89877be22cba..b7daf2bb4952 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -107,7 +107,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); ContextProvider appProvider = new ContextProvider(); - appProvider.setEnvironmentName("ee10"); + appProvider.configureEnvironment("ee10"); appProvider.setScanInterval(1); appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 3f5a24819b34..019157c06ce5 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -3,29 +3,31 @@ - - ee10 - - - - - - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath - - /etc/webdefault-ee10.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - + + + ee10 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee10.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod index a3e44ecc5c78..30f04501fb69 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod @@ -15,15 +15,6 @@ etc/jetty-ee10-deploy.xml [ini-template] # tag::ini-template[] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - -## Defaults Descriptor for all deployed webapps -# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee10.xml - -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 - ## Whether to extract *.war files # jetty.deploy.extractWars=true diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index 415591463449..eeccefecff4a 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -3,29 +3,31 @@ - - ee11 - - - - - - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath - - /etc/webdefault-ee11.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - + + + ee11 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee11.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod b/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod index f36f320f7992..27a2ed0974c6 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod @@ -15,15 +15,9 @@ etc/jetty-ee11-deploy.xml [ini-template] # tag::ini-template[] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - ## Defaults Descriptor for all deployed webapps # jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee11.xml -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 - ## Whether to extract *.war files # jetty.deploy.extractWars=true diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index f75a07217839..fdb720ccb634 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -3,29 +3,31 @@ - - ee8 - - - - - - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath - - /etc/webdefault-ee8.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - + + + ee8 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee8.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod index 3d44d7d5bedd..6897cd9484a2 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod @@ -15,15 +15,9 @@ etc/jetty-ee8-deploy.xml [ini-template] # tag::ini-template[] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - ## Defaults Descriptor for all deployed webapps # jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee8.xml -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 - ## Whether to extract *.war files # jetty.deploy.extractWars=true diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index d9e73b607cbb..34fb6841dfe9 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -102,7 +102,7 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); ContextProvider appProvider = new ContextProvider(); - appProvider.setEnvironmentName("ee9"); + Environment.ensure("ee9"); appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); appProvider.setScanInterval(1); deploymentManager.addAppProvider(appProvider); diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 9c7fc4abdb4d..4ff9703d3569 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -3,29 +3,31 @@ - - ee9 - - - - - - jetty.deploy.defaultsDescriptor - jetty.deploy.defaultsDescriptorPath - - /etc/webdefault-ee9.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - + + + ee9 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee9.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod index 742f44c76019..75b121460f2d 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod @@ -15,15 +15,9 @@ etc/jetty-ee9-deploy.xml [ini-template] # tag::ini-template[] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - ## Defaults Descriptor for all deployed webapps # jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee9.xml -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 - ## Whether to extract *.war files # jetty.deploy.extractWars=true From 42015b3fbd76242d59f72050b158a106cacf30fc Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 26 Nov 2024 15:22:19 -0600 Subject: [PATCH 006/104] Fixing [-zzz].properties sorting --- .../jetty/deploy/providers/ContextProvider.java | 14 +++++++++++--- .../providers/ContextProviderStartupTest.java | 13 +++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index ee46a83037ec..7028e45ad550 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -45,6 +45,7 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.PathCollators; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; @@ -167,11 +168,18 @@ public ContextHandler createContextHandler(final App app) throws Exception { Path envXmlPath = Paths.get((String)appAttributes.getAttribute(k)); if (!envXmlPath.isAbsolute()) - envXmlPath = getMonitoredDirResource().getPath().getParent().resolve(envXmlPath); + { + Path monitoredPath = getMonitoredDirResource().getPath(); + // not all Resource implementations support java.nio.file.Path. + if (monitoredPath != null) + { + envXmlPath = monitoredPath.getParent().resolve(envXmlPath); + } + } return envXmlPath; }) .filter(Files::isRegularFile) - .sorted() + .sorted(PathCollators.byName(true)) .toList(); // apply each environment context xml file @@ -211,7 +219,7 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) // Build the web application if necessary if (context == null) { - contextHandlerClassName = (String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); + contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); if (StringUtil.isBlank(contextHandlerClassName)) throw new IllegalStateException("No ContextHandler classname for " + app); Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index 9fc7c0a25e0b..563c50b87f9a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -123,17 +123,18 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception public void testStartupWithAbsoluteEnvironmentContext() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = " + - MavenPaths.findTestResourceFile("etc/core-context.xml"), StandardOpenOption.CREATE_NEW); + Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), + String.format("%s = %s%n", Deployable.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), + StandardOpenOption.CREATE_NEW); assertTrue(Files.exists(propsFile)); - Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), Deployable.ENVIRONMENT_XML + ".other = " + MavenPaths.findTestResourceFile("etc/core-context-other.xml"), StandardOpenOption.CREATE_NEW); + Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), + String.format("%s = %s%n", (Deployable.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), + StandardOpenOption.CREATE_NEW); assertTrue(Files.exists(props2File)); - Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING); - Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); - //check core environment context xml was applied to the produced context + // check core environment context xml was applied to the produced context ContextHandler context = jetty.getContextHandler("/bar"); assertNotNull(context); assertThat(context.getAttribute("core-context-0"), equalTo("core-context-0")); From f9c90cec7d662497a015f6456c33cb42aecd8707 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 26 Nov 2024 15:45:01 -0600 Subject: [PATCH 007/104] Allow an Environment to be removed (needed for testing with Environment) --- .../java/org/eclipse/jetty/util/component/Environment.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Environment.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Environment.java index b2d7cca0bdcd..4338c07b9471 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Environment.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/component/Environment.java @@ -50,6 +50,11 @@ static Environment set(Environment environment) return Named.__environments.put(environment.getName(), environment); } + static Environment remove(String name) + { + return Named.__environments.remove(name); + } + /** * @return The case-insensitive name of the environment. */ From 83fc5f101143ecc8b17bf3010d02517b409faa2b Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 26 Nov 2024 15:45:17 -0600 Subject: [PATCH 008/104] Fixing test cases --- .../jetty/deploy/DeploymentManagerTest.java | 62 ++++++++++++------- .../ContextProviderDeferredStartupTest.java | 2 +- .../ContextProviderRuntimeUpdatesTest.java | 3 +- .../providers/ContextProviderStartupTest.java | 3 +- .../jetty/deploy/test/XmlConfiguredJetty.java | 61 ++---------------- 5 files changed, 47 insertions(+), 84 deletions(-) diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 432501a67e62..f3e8b85d4a12 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.util.Collection; +import java.util.List; import java.util.Set; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; @@ -23,7 +24,9 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.component.LifeCycle; import org.hamcrest.Matchers; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -37,6 +40,21 @@ @ExtendWith(WorkDirExtension.class) public class DeploymentManagerTest { + /** + * Cleanup after any tests that modify the Environments singleton + */ + @AfterEach + public void clearEnvironments() + { + List envnames = Environment.getAll().stream() + .map(Environment::getName) + .toList(); + for (String envname : envnames) + { + Environment.remove(envname); + } + assertEquals(0, Environment.getAll().size()); + } @Test public void testReceiveApp() throws Exception @@ -53,20 +71,27 @@ public void testReceiveApp() throws Exception // Start DepMan depman.start(); - // Trigger new App - mockProvider.createWebapp("foo-webapp-1.war"); - - // Test app tracking - Collection apps = depman.getApps(); - assertNotNull(apps, "Should never be null"); - assertEquals(1, apps.size(), "Expected App Count"); - - // Test app get - App app = apps.stream().findFirst().orElse(null); - assertNotNull(app); - App actual = depman.getApp(app.getPath()); - assertNotNull(actual, "Should have gotten app (by id)"); - assertThat(actual.getPath().toString(), endsWith("mock-foo-webapp-1.war")); + try + { + // Trigger new App + mockProvider.createWebapp("foo-webapp-1.war"); + + // Test app tracking + Collection apps = depman.getApps(); + assertNotNull(apps, "Should never be null"); + assertEquals(1, apps.size(), "Expected App Count"); + + // Test app get + App app = apps.stream().findFirst().orElse(null); + assertNotNull(app); + App actual = depman.getApp(app.getPath()); + assertNotNull(actual, "Should have gotten app (by id)"); + assertThat(actual.getPath().toString(), endsWith("mock-foo-webapp-1.war")); + } + finally + { + LifeCycle.stop(depman); + } } @Test @@ -177,14 +202,7 @@ public void testXmlConfigured(WorkDir workDir) throws Exception { if (jetty != null) { - try - { - jetty.stop(); - } - catch (Exception ignore) - { - // ignore - } + jetty.stop(); } } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java index c85948ce1788..620223a1e319 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java @@ -55,7 +55,7 @@ public class ContextProviderDeferredStartupTest @AfterEach public void teardownEnvironment() throws Exception { - LifeCycle.stop(jetty); + jetty.stop(); } @Test diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java index a600972b985c..653c1a5a12a3 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.Scanner; -import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -115,7 +114,7 @@ public void scanEnded(int cycle) @AfterEach public void teardownEnvironment() throws Exception { - LifeCycle.stop(jetty); + jetty.stop(); } public void waitForDirectoryScan() diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index 563c50b87f9a..dceaeb90eea5 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -27,7 +27,6 @@ import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,7 +81,7 @@ public void startJetty() throws Exception @AfterEach public void teardownEnvironment() throws Exception { - LifeCycle.stop(jetty); + jetty.stop(); } @Test diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java index 2ee6ce882eda..9822961c6657 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java @@ -36,9 +36,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpURI; -import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -47,6 +45,7 @@ import org.eclipse.jetty.toolchain.test.PathMatchers; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; @@ -290,47 +289,6 @@ public Server load() throws Exception return this._server; } - /* - Path testConfig = _jettyBase.resolve("xml-configured-jetty.properties"); - setProperty("jetty.deploy.common.properties", testConfig.toString()); - - // Write out configuration for use by ConfigurationManager. - Properties properties = new Properties(); - properties.putAll(_properties); - try (OutputStream out = Files.newOutputStream(testConfig)) - { - properties.store(out, "Generated by " + XmlConfiguredJetty.class.getName()); - } - - XmlConfiguration last = null; - Object[] obj = new Object[_xmlConfigurations.size()]; - - // Configure everything - for (int i = 0; i < _xmlConfigurations.size(); i++) - { - Resource config = _xmlConfigurations.get(i); - XmlConfiguration configuration = new XmlConfiguration(config); - if (last != null) - configuration.getIdMap().putAll(last.getIdMap()); - configuration.getProperties().putAll(_properties); - obj[i] = configuration.configure(); - last = configuration; - } - - Map ids = last.getIdMap(); - - // Test for Server Instance. - Server server = (Server)ids.get("Server"); - if (server == null) - { - throw new Exception("Load failed to configure a " + Server.class.getName()); - } - - this._server = server; - this._server.setStopTimeout(10000); - this._contexts = (ContextHandlerCollection)ids.get("Contexts"); - } */ - public void removeWebapp(String name) throws IOException { Path webappFile = _jettyBase.resolve("webapps/" + name); @@ -358,19 +316,7 @@ public void start() throws Exception _server.start(); // Find the active server port. - _serverPort = -1; - Connector[] connectors = _server.getConnectors(); - for (int i = 0; _serverPort < 0 && i < connectors.length; i++) - { - if (connectors[i] instanceof NetworkConnector) - { - int port = ((NetworkConnector)connectors[i]).getLocalPort(); - if (port > 0) - _serverPort = port; - } - } - - assertTrue((1 <= this._serverPort) && (this._serverPort <= 65535), "Server Port is between 1 and 65535. Was actually <" + _serverPort + ">"); + _serverPort = _server.getURI().getPort(); // Uncomment to have server start and continue to run (without exiting) // System.err.printf("Listening to port %d%n",this.serverPort); @@ -379,6 +325,7 @@ public void start() throws Exception public void stop() throws Exception { - _server.stop(); + LifeCycle.stop(_server); + _properties.clear(); } } From 249679f9b5bb6247bc722d5ab862b29cd778dc6d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 27 Nov 2024 08:55:16 -0600 Subject: [PATCH 009/104] Fix javadoc --- .../deploy/providers/ContextProvider.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 7028e45ad550..34140a910165 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -52,40 +52,40 @@ import org.slf4j.LoggerFactory; /** - * The webapps directory scanning provider. - * *

        This provider scans one or more directories (typically "webapps") for contexts to - * * deploy, which may be: - * *

        - * *
          - * *
        • A standard WAR file (must end in ".war")
        • - * *
        • A directory containing an expanded WAR file
        • - * *
        • A directory containing static content
        • - * *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • - * *
        - * *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * * implements some heuristics to ignore some files found in the scans: - * *

        - * *
          - * *
        • Hidden files (starting with {@code "."}) are ignored
        • - * *
        • Directories with names ending in {@code ".d"} are ignored
        • - * *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • - * *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be - * * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • - * *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • - * *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to - * * be configured by the XML and only the XML is deployed. - * *
        - * *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and - * * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". - * * The properties will be initialized with: - * *

        - * *
          - * *
        • The properties set on the application via {@link App#getProperties()}
        • - * *
        • The app specific properties file {@code webapps/.properties}
        • - * *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • - * *
        • The {@link Attributes} from the {@link Environment}
        • - * *
        + *

        Jetty Environment WebApp Hot Deployment Provider.

        + * + *

        This provider scans one or more directories (typically "webapps") for contexts to + * deploy, which may be:

        + *
          + *
        • A standard WAR file (must end in ".war")
        • + *
        • A directory containing an expanded WAR file
        • + *
        • A directory containing static content
        • + *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • + *
        + *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * implements some heuristics to ignore some files found in the scans: + *

        + *
          + *
        • Hidden files (starting with {@code "."}) are ignored
        • + *
        • Directories with names ending in {@code ".d"} are ignored
        • + *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • + *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be + * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
        • + *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be + * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
        • + *
        • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to + * be configured by the XML and only the XML is deployed. + *
        + *

        For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and + * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". + * The properties will be initialized with: + *

        + *
          + *
        • The properties set on the application via {@link App#getProperties()}
        • + *
        • The app specific properties file {@code webapps/.properties}
        • + *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • + *
        • The {@link Attributes} from the {@link Environment}
        • + *
        */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") public class ContextProvider extends ScanningAppProvider From bef04d9888b0fd7ea72a5a49bd5566196d769d42 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 27 Nov 2024 08:55:25 -0600 Subject: [PATCH 010/104] Fix method names --- .../src/main/config/etc/jetty-ee10-deploy.xml | 6 +++--- .../src/main/config/etc/jetty-ee11-deploy.xml | 6 +++--- .../src/main/config/etc/jetty-ee8-deploy.xml | 6 +++--- .../src/main/config/etc/jetty-ee9-deploy.xml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 019157c06ce5..19c0107e843a 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -26,8 +26,8 @@ .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$
        - - - + + +
        diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index eeccefecff4a..876c9407dfaa 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -26,8 +26,8 @@ .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$
        - - - + + +
        diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index fdb720ccb634..d54f7394043c 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -26,8 +26,8 @@ .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$
        - - - + + +
        diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 4ff9703d3569..506b7f427b69 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -26,8 +26,8 @@ .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$
        - - - + + +
        From add337ae1fb7d43503dd20a9aa9d760472d04351 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 27 Nov 2024 10:15:29 -0600 Subject: [PATCH 011/104] Fixing Environment contextClass loading --- .../jetty/deploy/providers/ContextProvider.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 34140a910165..7a43df7fb15f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -135,7 +135,7 @@ public ContextHandler createContextHandler(final App app) throws Exception } if (LOG.isDebugEnabled()) - LOG.debug("createContextHandler {} in {}", app, environment); + LOG.debug("createContextHandler {} in {}", app, environment.getName()); ClassLoader old = Thread.currentThread().getContextClassLoader(); try @@ -157,7 +157,17 @@ public ContextHandler createContextHandler(final App app) throws Exception // applying the environment xml file. String contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); if (contextHandlerClassName != null) - context = Class.forName(contextHandlerClassName).getDeclaredConstructor().newInstance(); + { + Class contextClass = Loader.loadClass(contextHandlerClassName); + if (contextClass == null) + { + throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app + " in environment " + environment.getName()); + } + else + { + context = contextClass.getDeclaredConstructor().newInstance(); + } + } // Collect the optional environment context xml files. // Order them according to the name of their property key names. From 2e9660d233d060d65f7e897dfb141c64b92f6832 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 27 Nov 2024 15:09:35 -0600 Subject: [PATCH 012/104] Fixing test cases --- .../deploy/providers/ContextProvider.java | 12 +- .../org/eclipse/jetty/server/Deployable.java | 1 + .../src/test/resources/RFC2616Base.xml | 191 +++++++++--------- .../src/test/resources/deploy.xml | 70 +++---- .../src/test/resources/RFC2616Base.xml | 36 ++-- .../src/test/resources/deploy.xml | 37 +--- .../src/test/resources/RFC2616Base.xml | 36 ++-- .../src/test/resources/deploy.xml | 35 +--- 8 files changed, 177 insertions(+), 241 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 7a43df7fb15f..04555a07f61a 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -229,7 +229,8 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) // Build the web application if necessary if (context == null) { - contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); + // Create the webapp from the default context class + contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT); if (StringUtil.isBlank(contextHandlerClassName)) throw new IllegalStateException("No ContextHandler classname for " + app); Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); @@ -752,12 +753,17 @@ public void setContainerScanJarPattern(String pattern) * instances (can be class that implements {@code java.util.function.Supplier} * as well). * + *

        + * This is the fallback class used, if the context class itself isn't defined by + * the web application being deployed. + *

        + * * @param classname the classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS + * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT */ public void setContextHandlerClass(String classname) { - environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); + environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); } /** diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index 947b2668a74c..b42ff8ecbf3d 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -59,6 +59,7 @@ public interface Deployable String CONTAINER_SCAN_JARS = "jetty.deploy.containerScanJarPattern"; String CONTEXT_PATH = "jetty.deploy.contextPath"; String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; + String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; String DEFAULTS_DESCRIPTOR = "jetty.deploy.defaultsDescriptor"; String ENVIRONMENT = "environment"; String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 12e88f004ae1..379bd1560e3f 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -1,5 +1,4 @@ - - + @@ -9,124 +8,122 @@ - - - https - - 32768 - 8192 - 8192 - true - true - 1024 - - - - RFC2616 - - + + https + + + + 32768 + 8192 + 8192 + true + true + 1024 - - - - - - - - - - - - - /tests - - - VirtualHost - - - /virtualhost - - virtual - - - - - /tests - /default - - default - - - - - /echo - - echo - - - - - - - ee10 + + - + + + + + - - - - - - - + + + + /tests + + + VirtualHost + + + /virtualhost + + + + + virtual + + + + + /tests + /default + + + + + default + + + + + /echo + + + + echo + + + + + + + + + ee10 + + + + + + + + + + + - ee10 src/test/resources/ webapp-contexts/RFC2616/ - 1 - true - - + + + ee10 + true + org.eclipse.jetty.ee10.webapp.WebAppContext + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - +
        + + + + - - - + + + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index 23fe67b511e6..efb34d961c13 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -1,16 +1,9 @@ - - + ee10 - - - contextHandlerClass - org.eclipse.jetty.ee10.webapp.WebAppContext - - @@ -18,43 +11,34 @@ - - - - - ee10 - - - target - webapps - - - 1 - true + + + + + + target + webapps + + + 1 - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - - - - - - - - + + ee10 + org.eclipse.jetty.ee10.webapp.WebAppContext + true + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 757a2ca049db..62951771d39f 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -1,5 +1,4 @@ - - + @@ -78,12 +77,6 @@ ee11 - - - contextHandlerClass - org.eclipse.jetty.ee11.webapp.WebAppContext - - @@ -95,32 +88,27 @@ - ee11 src/test/resources/ webapp-contexts/RFC2616/ - 1 - true - - + + + ee11 + org.eclipse.jetty.ee11.webapp.WebAppContext + true + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - + + + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 3cec2d0d357b..dd84a5fa4912 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -1,16 +1,9 @@ - - + ee11 - - - contextHandlerClass - org.eclipse.jetty.ee11.webapp.WebAppContext - - @@ -22,7 +15,6 @@ - ee11 target @@ -31,30 +23,23 @@ 1 - true - - + + ee11 + org.eclipse.jetty.ee11.webapp.WebAppContext + true + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - + + + + + - - - diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 127923777ca8..51235bd714a7 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -1,5 +1,4 @@ - - + @@ -71,12 +70,6 @@ ee9 - - - contextHandlerClass - org.eclipse.jetty.ee9.webapp.WebAppContext - - @@ -88,32 +81,27 @@ - ee9 src/test/resources/ webapp-contexts/RFC2616/ - 1 - true - - + + + ee9 + org.eclipse.jetty.ee9.webapp.WebAppContext + true + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - + + + + + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 1656b8d75ed0..3e19a33ba574 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -1,16 +1,9 @@ - - + ee9 - - - contextHandlerClass - org.eclipse.jetty.ee9.webapp.WebAppContext - - @@ -22,33 +15,27 @@ - ee9 target webapps - 1 - true - - + + ee9 + org.eclipse.jetty.ee9.webapp.WebAppContext + true + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - - - - - - + + + + + From 9bfab80f1290d76f476a1d01a499948cf86db1cf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 27 Nov 2024 17:05:50 -0600 Subject: [PATCH 013/104] Fixing quickstart XML references to provider --- .../main/config/etc/jetty-ee10-quickstart.xml | 25 +++++++++++++----- .../main/config/etc/jetty-ee11-quickstart.xml | 26 +++++++++++++------ .../main/config/etc/jetty-ee8-quickstart.xml | 25 +++++++++++++----- .../main/config/etc/jetty-ee9-quickstart.xml | 25 +++++++++++++----- 4 files changed, 72 insertions(+), 29 deletions(-) diff --git a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml index a08f0c808c3a..0acd3b375cff 100644 --- a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml +++ b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml @@ -7,12 +7,23 @@ - - - - - - - + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + +
        diff --git a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml index 3ff08b63d59e..bfe26866106e 100644 --- a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml +++ b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml @@ -7,12 +7,22 @@ - - - - - - - - + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + +
        diff --git a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml index 4a36d28e5564..c1f1a38fd5e4 100644 --- a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml +++ b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml @@ -7,12 +7,23 @@ - - - - - - - + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + diff --git a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml index e47a22440b1a..33421816d96d 100644 --- a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml +++ b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml @@ -7,12 +7,23 @@ - - - - - - - + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + From 0e534f73d24c541ab193c6a9ec8c0d30bc86a58a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 2 Dec 2024 12:26:27 -0600 Subject: [PATCH 014/104] Fixing quickstart XML --- .../deploy/providers/ContextProvider.java | 26 +++++++------ .../main/config/etc/jetty-ee10-quickstart.xml | 38 ++++++++++--------- .../main/config/etc/jetty-ee11-quickstart.xml | 37 +++++++++--------- .../main/config/etc/jetty-ee8-quickstart.xml | 38 ++++++++++--------- .../main/config/etc/jetty-ee9-quickstart.xml | 38 ++++++++++--------- .../quickstart/QuickStartConfiguration.java | 1 - 6 files changed, 95 insertions(+), 83 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 04555a07f61a..8097ccab1bd8 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -103,16 +103,14 @@ public ContextProvider() private static Map asProperties(Attributes attributes) { Map props = new HashMap<>(); - attributes.getAttributeNameSet().stream() - .map((name) -> + attributes.getAttributeNameSet().forEach((name) -> { - // undo old prefixed entries - if (name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - return name.substring(Deployable.ATTRIBUTE_PREFIX.length()); - else - return name; - }) - .forEach((name) -> props.put(name, Objects.toString(attributes.getAttribute(name)))); + Object value = attributes.getAttribute(name); + String key = name.startsWith(Deployable.ATTRIBUTE_PREFIX) + ? name.substring(Deployable.ATTRIBUTE_PREFIX.length()) + : name; + props.put(key, Objects.toString(value)); + }); return props; } @@ -495,8 +493,14 @@ else if (Supplier.class.isAssignableFrom(context.getClass())) // pass through properties as attributes directly attributes.getAttributeNameSet().stream() .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - .map((name) -> name.substring(Deployable.ATTRIBUTE_PREFIX.length())) - .forEach((name) -> contextHandler.setAttribute(name, attributes.getAttribute(name))); + .forEach((name) -> + { + Object value = attributes.getAttribute(name); + String key = name.substring(Deployable.ATTRIBUTE_PREFIX.length()); + if (LOG.isDebugEnabled()) + LOG.debug("Setting attribute [{}] to [{}] in context {}", key, value, contextHandler); + contextHandler.setAttribute(key, value); + }); String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); if (StringUtil.isNotBlank(contextPath)) diff --git a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml index 0acd3b375cff..c92702f7a51c 100644 --- a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml +++ b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml @@ -7,23 +7,25 @@ - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode - - - + + ee10 + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin - - - - - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml - - - - - diff --git a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml index bfe26866106e..555a385fc059 100644 --- a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml +++ b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml @@ -7,22 +7,25 @@ - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode - - - - - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin - - - - - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml - - - + + ee11 + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + diff --git a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml index c1f1a38fd5e4..8eacb3b00bce 100644 --- a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml +++ b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml @@ -7,23 +7,25 @@ - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode - - - + + ee9 + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin - - - - - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml - - - - - diff --git a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml index 33421816d96d..3215a9ca1241 100644 --- a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml +++ b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml @@ -7,23 +7,25 @@ - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode - - - + + ee9 + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + + + + + + jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + + + + - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin - - - - - - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml - - - - - diff --git a/jetty-ee9/jetty-ee9-quickstart/src/main/java/org/eclipse/jetty/ee9/quickstart/QuickStartConfiguration.java b/jetty-ee9/jetty-ee9-quickstart/src/main/java/org/eclipse/jetty/ee9/quickstart/QuickStartConfiguration.java index 01fcf87f4035..dc3d0a9d709b 100644 --- a/jetty-ee9/jetty-ee9-quickstart/src/main/java/org/eclipse/jetty/ee9/quickstart/QuickStartConfiguration.java +++ b/jetty-ee9/jetty-ee9-quickstart/src/main/java/org/eclipse/jetty/ee9/quickstart/QuickStartConfiguration.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; -import org.eclipse.jetty.util.resource.Resources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; From 43eb6798fbf7780f23e2160c99303f59022e7524 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 2 Dec 2024 16:22:30 -0600 Subject: [PATCH 015/104] Fixing deploy containerScanJarPattern for ee9/ee8 --- .../jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml | 2 +- .../jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index d54f7394043c..f1b3b32fa3cd 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -23,7 +23,7 @@ - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jetty-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\.jar|.*jsp.jstl-[^/]*\.jar diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 506b7f427b69..0de7b4693173 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -23,7 +23,7 @@ - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ From a363189b34102ed2016d97d1f24e6a82b82077f8 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Dec 2024 13:38:38 -0600 Subject: [PATCH 016/104] Changes from reviews. Preparing for BulkListener impl. --- .../deploy/providers/ContextProvider.java | 121 ++++++++++-------- .../deploy/providers/ScanningAppProvider.java | 5 +- .../providers/ContextProviderStartupTest.java | 16 ++- 3 files changed, 77 insertions(+), 65 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 8097ccab1bd8..bd4645d3aa02 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -49,6 +49,7 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @@ -72,16 +73,21 @@ *
      • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
      • *
      • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * an unpacked WAR and only the XML is deployed (which may used the directory in its configuration)
      • + * an unpacked WAR and only the XML is deployed (which may use the directory in its configuration) *
      • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to * be configured by the XML and only the XML is deployed. *
      - *

      For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and - * properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps". - * The properties will be initialized with: + *

      For XML configured contexts, the following is available.

      + *
        + *
      • The XML Object ID Map will have a reference to the {@link Server} instance via the ID name {@code "Server"}
      • + *
      • The Default XML Properties are populated from a call to {@link XmlConfiguration#setJettyStandardIdsAndProperties(Object, Path)} (for things like {@code jetty.home} and {@code jetty.base})
      • + *
      • An extra XML Property named {@code "jetty.webapps"} is available, and points to the monitored path.
      • + *
      + *

      + * Context Deployment properties will be initialized with: *

      *
        - *
      • The properties set on the application via {@link App#getProperties()}
      • + *
      • The properties set on the application via embedded calls modifying {@link App#getProperties()}
      • *
      • The app specific properties file {@code webapps/.properties}
      • *
      • The environment specific properties file {@code webapps/[-zzz].properties}
      • *
      • The {@link Attributes} from the {@link Environment}
      • @@ -90,8 +96,8 @@ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") public class ContextProvider extends ScanningAppProvider { - private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ContextProvider.class); - private String defaultEnvironmentName; + private static final Logger LOG = LoggerFactory.getLogger(ContextProvider.class); + private String _defaultEnvironmentName; public ContextProvider() { @@ -202,45 +208,33 @@ public ContextHandler createContextHandler(final App app) throws Exception if (FileID.isXml(path)) { context = applyXml(context, path, environment, appAttributes); - - // Look for the contextHandler itself - ContextHandler contextHandler = null; - if (context instanceof ContextHandler c) - contextHandler = c; - else if (context instanceof Supplier supplier) - { - Object nestedContext = supplier.get(); - if (nestedContext instanceof ContextHandler c) - contextHandler = c; - } - if (contextHandler == null) - throw new IllegalStateException("Unknown context type of " + context); - - return contextHandler; } // Otherwise it must be a directory or an archive else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) { throw new IllegalStateException("unable to create ContextHandler for " + app); } - - // Build the web application if necessary - if (context == null) + else { - // Create the webapp from the default context class - contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT); - if (StringUtil.isBlank(contextHandlerClassName)) - throw new IllegalStateException("No ContextHandler classname for " + app); - Class contextHandlerClass = Loader.loadClass(contextHandlerClassName); - if (contextHandlerClass == null) - throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app); - - context = contextHandlerClass.getDeclaredConstructor().newInstance(); + // If we reach this point, we don't have a Context XML Deployable. + // We are either a directory or a web archive (war) + // Set the WAR attribute to point to this directory or web-archive. + appAttributes.setAttribute(Deployable.WAR, path.toString()); } - //set a backup value for the path to the war in case it hasn't already been set - appAttributes.setAttribute(Deployable.WAR, path.toString()); - return initializeContextHandler(context, path, appAttributes); + // Look for the contextHandler itself + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + + // Allow raw ContextHandler to be initialized properly + initializeContextHandler(contextHandler, path, appAttributes); + + // Initialize deployable + if (contextHandler instanceof Deployable deployable) + deployable.initializeDefaults(appAttributes); + + return contextHandler; } finally { @@ -261,19 +255,19 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) */ public String getDefaultEnvironmentName() { - if (defaultEnvironmentName == null) + if (_defaultEnvironmentName == null) { return Environment.getAll().stream() .map(Environment::getName) .max(Deployable.ENVIRONMENT_COMPARATOR) .orElse(null); } - return defaultEnvironmentName; + return _defaultEnvironmentName; } public void setDefaultEnvironmentName(String name) { - this.defaultEnvironmentName = name; + this._defaultEnvironmentName = name; } @Deprecated @@ -354,14 +348,23 @@ protected Object applyXml(Object context, Path xml, Environment environment, Att public void initializeDefaults(Object context) { super.initializeDefaults(context); - ContextProvider.this.initializeContextHandler(context, xml, attributes); + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a ContextHandler: Not initializing Context {}", context); + } + else + { + ContextProvider.this.initializeContextHandler(contextHandler, xml, attributes); + } } }; xmlc.getIdMap().put("Environment", environment.getName()); xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml); - // Put all Environment attributes into XMLC as properties that can be used. + // Put all Environment attributes into XmlConfiguration as properties that can be used. attributes.getAttributeNameSet() .stream() .filter(k -> !k.startsWith("jetty.home") && @@ -459,10 +462,8 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); } - protected ContextHandler initializeContextHandler(Object context, Path path, Attributes attributes) + private ContextHandler getContextHandler(Object context) { - if (LOG.isDebugEnabled()) - LOG.debug("initializeContextHandler {}", context); // find the ContextHandler ContextHandler contextHandler; if (context instanceof ContextHandler handler) @@ -480,14 +481,25 @@ else if (Supplier.class.isAssignableFrom(context.getClass())) return null; } + return contextHandler; + } + + protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) + { + if (LOG.isDebugEnabled()) + LOG.debug("initializeContextHandler {}", contextHandler); + assert contextHandler != null; initializeContextPath(contextHandler, path); - if (Files.isDirectory(path)) + if (contextHandler.getBaseResource() == null) { - ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); - contextHandler.setBaseResource(resourceFactory.newResource(path)); + if (Files.isDirectory(path)) + { + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); + } } // pass through properties as attributes directly @@ -505,11 +517,6 @@ else if (Supplier.class.isAssignableFrom(context.getClass())) String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); if (StringUtil.isNotBlank(contextPath)) contextHandler.setContextPath(contextPath); - - if (context instanceof Deployable deployable) - deployable.initializeDefaults(attributes); - - return contextHandler; } protected void initializeContextPath(ContextHandler context, Path path) @@ -537,15 +544,17 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) contextPath = "/" + contextPath; // Set the display name and context Path - context.setDisplayName(basename); - context.setContextPath(contextPath); + if (StringUtil.isBlank(context.getDisplayName())) + context.setDisplayName(basename); + if (StringUtil.isBlank(context.getContextPath())) + context.setContextPath(contextPath); } protected boolean isDeployable(Path path) { String basename = FileID.getBasename(path); - //is the file that changed a directory? + // is the file that changed a directory? if (Files.isDirectory(path)) { // deploy if there is not a .xml or .war file of the same basename? diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 283190714090..a79a13610e42 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -125,9 +125,8 @@ protected Map getDeployedApps() * Isolated in a method so that it is possible to override the default App * object for specialized implementations of the AppProvider. * - * @param path The file that is the context.xml. It is resolved by - * {@link org.eclipse.jetty.util.resource.ResourceFactory#newResource(String)} - * @return The App object for this particular context definition file. + * @param path The file that the main point of deployment (eg: a context XML, a WAR file, a directory, etc) + * @return The App object for this particular context. */ protected App createApp(Path path) { diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index dceaeb90eea5..57c55f1c7643 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.deploy.providers; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; @@ -212,7 +213,7 @@ public void testPropertyOverriding() throws Exception public void testPropertySubstitution() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsCoreAFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context-sub.xml\ntest.displayName=DisplayName Set By Property", StandardOpenOption.CREATE_NEW); + Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context-sub.xml\ntest.displayName=DisplayName Set By Property", StandardOpenOption.CREATE_NEW); Files.copy(MavenPaths.findTestResourceFile("etc/core-context-sub.xml"), jettyBase.resolve("etc/core-context-sub.xml"), StandardCopyOption.REPLACE_EXISTING); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -223,10 +224,13 @@ public void testPropertySubstitution() throws Exception private static void writeXmlDisplayName(Path filePath, String displayName) throws IOException { - String content = "\n" + - "\n" + - " "; - - Files.writeString(filePath, content + displayName + "\n", StandardOpenOption.CREATE_NEW); + String content = """ + + + @NAME@ + + """.replace("@NAME@", displayName); + + Files.writeString(filePath, content, StandardCharsets.UTF_8, StandardOpenOption.CREATE_NEW); } } From 5f6d504e485cc3f1bd64f5c5ffdfbe83d9c41be6 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Dec 2024 13:49:53 -0600 Subject: [PATCH 017/104] Using BulkListener with sorted paths for alphabetical deployment. --- .../deploy/providers/ScanningAppProvider.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index a79a13610e42..fbe309035b28 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -36,6 +37,7 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.PathCollators; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.resource.Resources; @@ -57,24 +59,28 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements private boolean _useRealPaths; private boolean _deferInitialScan = false; - private final Scanner.DiscreteListener _scannerListener = new Scanner.DiscreteListener() + private final Scanner.BulkListener _scannerBulkListener = new Scanner.BulkListener() { @Override - public void pathAdded(Path path) throws Exception + public void pathsChanged(Set paths) throws Exception { - ScanningAppProvider.this.pathAdded(path); - } + List sortedPaths = paths.stream() + .sorted(PathCollators.byName(true)) + .toList(); - @Override - public void pathChanged(Path path) throws Exception - { - ScanningAppProvider.this.pathChanged(path); + for (Path path : sortedPaths) + { + if (Files.exists(path)) + ScanningAppProvider.this.pathChanged(path); + else + ScanningAppProvider.this.pathRemoved(path); + } } @Override - public void pathRemoved(Path path) throws Exception + public void filesChanged(Set filenames) throws Exception { - ScanningAppProvider.this.pathRemoved(path); + // ignore, as we are using the pathsChanged() technique only. } }; @@ -176,7 +182,7 @@ protected void doStart() throws Exception _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.setScanDepth(1); //consider direct dir children of monitored dir - _scanner.addListener(_scannerListener); + _scanner.addListener(_scannerBulkListener); _scanner.setReportExistingFilesOnStartup(true); _scanner.setAutoStartScanning(!_deferInitialScan); addBean(_scanner); @@ -211,7 +217,7 @@ protected void doStop() throws Exception if (_scanner != null) { removeBean(_scanner); - _scanner.removeListener(_scannerListener); + _scanner.removeListener(_scannerBulkListener); _scanner = null; } } From 77c34c2a0d4a909f5884ab811ba77acf09abb92a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Dec 2024 13:54:40 -0600 Subject: [PATCH 018/104] Changes from review --- .../deploy/providers/ContextProvider.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index bd4645d3aa02..42f00819677f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -718,11 +718,11 @@ public static class EnvironmentConfig // syntax to skip setting of an environment attribute if property is unset, // allowing the in code values to be same defaults as they are in embedded-jetty. - private final Environment environment; + private final Environment _environment; private EnvironmentConfig(Environment environment) { - this.environment = environment; + this._environment = environment; } /** @@ -745,9 +745,9 @@ public void setConfigurationClasses(String configurations) public void setConfigurationClasses(String[] configurations) { if (configurations == null) - environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); + _environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); else - environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); + _environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); } /** @@ -758,7 +758,7 @@ public void setConfigurationClasses(String[] configurations) */ public void setContainerScanJarPattern(String pattern) { - environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); + _environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); } /** @@ -776,7 +776,7 @@ public void setContainerScanJarPattern(String pattern) */ public void setContextHandlerClass(String classname) { - environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); + _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); } /** @@ -788,7 +788,7 @@ public void setContextHandlerClass(String classname) */ public void setDefaultsDescriptor(String defaultsDescriptor) { - environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); + _environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); } /** @@ -799,7 +799,7 @@ public void setDefaultsDescriptor(String defaultsDescriptor) */ public void setExtractWars(boolean extractWars) { - environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); + _environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); } /** @@ -810,7 +810,7 @@ public void setExtractWars(boolean extractWars) */ public void setParentLoaderPriority(boolean parentLoaderPriority) { - environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); + _environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); } /** @@ -821,7 +821,7 @@ public void setParentLoaderPriority(boolean parentLoaderPriority) */ public void setServletContainerInitializerExclusionPattern(String pattern) { - environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); + _environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); } /** @@ -832,7 +832,7 @@ public void setServletContainerInitializerExclusionPattern(String pattern) */ public void setServletContainerInitializerOrder(String order) { - environment.setAttribute(Deployable.SCI_ORDER, order); + _environment.setAttribute(Deployable.SCI_ORDER, order); } /** @@ -843,7 +843,7 @@ public void setServletContainerInitializerOrder(String order) */ public void setWebInfScanJarPattern(String pattern) { - environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); + _environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); } } From 5306c3d612b469b2313f3814189828a3c4e4e566 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Dec 2024 17:11:14 -0600 Subject: [PATCH 019/104] More changes from review --- .../deploy/providers/ContextProvider.java | 60 +++++---- .../deploy/providers/ScanningAppProvider.java | 15 ++- .../jetty/osgi/AbstractContextProvider.java | 119 ++---------------- .../org/eclipse/jetty/server/Deployable.java | 4 - .../jetty/ee10/osgi/boot/EE10Activator.java | 8 +- .../jetty/ee10/webapp/WebAppContext.java | 38 +----- .../jetty/ee11/osgi/boot/EE11Activator.java | 8 +- .../test/websocket/JakartaWebSocketTest.java | 1 - .../jetty/ee11/webapp/WebAppContext.java | 34 +---- .../jetty/ee9/osgi/boot/EE9Activator.java | 8 +- .../jetty/ee9/webapp/WebAppContext.java | 33 +---- 11 files changed, 77 insertions(+), 251 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 42f00819677f..89dd8f52db29 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -154,24 +154,11 @@ public ContextHandler createContextHandler(final App app) throws Exception // prepare app attributes to use for app deployment Attributes appAttributes = initAttributes(environment, app); - Object context = null; - // check if there is a specific ContextHandler type to create set in the // properties associated with the webapp. If there is, we create it _before_ // applying the environment xml file. - String contextHandlerClassName = (String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS); - if (contextHandlerClassName != null) - { - Class contextClass = Loader.loadClass(contextHandlerClassName); - if (contextClass == null) - { - throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app + " in environment " + environment.getName()); - } - else - { - context = contextClass.getDeclaredConstructor().newInstance(); - } - } + Object context = newContextInstance((String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS), app); + initializeContextPath(getContextHandler(context), path); // Collect the optional environment context xml files. // Order them according to the name of their property key names. @@ -222,6 +209,17 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) appAttributes.setAttribute(Deployable.WAR, path.toString()); } + if (context == null) + { + context = newContextInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT), app); + initializeContextPath(getContextHandler(context), path); + } + + if (context == null) + { + throw new IllegalStateException("ContextHandler class is null for app " + app); + } + // Look for the contextHandler itself ContextHandler contextHandler = getContextHandler(context); if (contextHandler == null) @@ -242,6 +240,22 @@ else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) } } + private Object newContextInstance(String contextHandlerClassName, App app) throws Exception + { + if (StringUtil.isBlank(contextHandlerClassName)) + return null; + + Class contextClass = Loader.loadClass(contextHandlerClassName); + if (contextClass == null) + { + throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app + " in environment " + app.getEnvironmentName()); + } + else + { + return contextClass.getConstructor().newInstance(); + } + } + /** * Get the default {@link Environment} name for discovered web applications that * do not declare the {@link Environment} that they belong to. @@ -356,6 +370,7 @@ public void initializeDefaults(Object context) } else { + ContextProvider.this.initializeContextPath(contextHandler, xml); ContextProvider.this.initializeContextHandler(contextHandler, xml, attributes); } } @@ -464,6 +479,9 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException private ContextHandler getContextHandler(Object context) { + if (context == null) + return null; + // find the ContextHandler ContextHandler contextHandler; if (context instanceof ContextHandler handler) @@ -491,8 +509,6 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path assert contextHandler != null; - initializeContextPath(contextHandler, path); - if (contextHandler.getBaseResource() == null) { if (Files.isDirectory(path)) @@ -521,6 +537,9 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path protected void initializeContextPath(ContextHandler context, Path path) { + if (context == null) + return; + // Strip any 3 char extension from non directories String basename = FileID.getBasename(path); String contextPath = basename; @@ -543,11 +562,8 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) if (contextPath.charAt(0) != '/') contextPath = "/" + contextPath; - // Set the display name and context Path - if (StringUtil.isBlank(context.getDisplayName())) - context.setDisplayName(basename); - if (StringUtil.isBlank(context.getContextPath())) - context.setContextPath(contextPath); + context.setDisplayName(basename); + context.setContextPath(contextPath); } protected boolean isDeployable(Path path) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index fbe309035b28..3ae35d5c4b39 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -61,6 +62,8 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements private final Scanner.BulkListener _scannerBulkListener = new Scanner.BulkListener() { + private final Set _addedPaths = new HashSet<>(); + @Override public void pathsChanged(Set paths) throws Exception { @@ -71,14 +74,22 @@ public void pathsChanged(Set paths) throws Exception for (Path path : sortedPaths) { if (Files.exists(path)) - ScanningAppProvider.this.pathChanged(path); + { + if (_addedPaths.add(path)) + ScanningAppProvider.this.pathAdded(path); + else + ScanningAppProvider.this.pathChanged(path); + } else + { + _addedPaths.remove(path); ScanningAppProvider.this.pathRemoved(path); + } } } @Override - public void filesChanged(Set filenames) throws Exception + public void filesChanged(Set filenames) { // ignore, as we are using the pathsChanged() technique only. } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 1cdb626997ad..1e1e2ad7148f 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -13,16 +13,14 @@ package org.eclipse.jetty.osgi; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.osgi.framework.Bundle; @@ -46,7 +44,7 @@ public abstract class AbstractContextProvider extends AbstractLifeCycle implemen private Server _server; private ContextFactory _contextFactory; private String _environment; - private final Map _properties = new HashMap<>(); + private final Attributes _attributes = new Attributes.Mapped(); public AbstractContextProvider(String environment, Server server, ContextFactory contextFactory) { @@ -60,9 +58,9 @@ public Server getServer() return _server; } - public Map getProperties() + public Attributes getAttributes() { - return _properties; + return _attributes; } @Override @@ -82,10 +80,12 @@ public void setDeploymentManager(DeploymentManager deploymentManager) { _deploymentManager = deploymentManager; } - + @Override public String getEnvironmentName() { + // TODO: when AppProvider.getEnvironmentName is eventually removed, leave this method here for + // these OSGI based AppProviders to use. return _environment; } @@ -94,110 +94,13 @@ public DeploymentManager getDeploymentManager() return _deploymentManager; } - /** - * Get the extractWars. - * This is equivalent to getting the {@link Deployable#EXTRACT_WARS} property. - * - * @return the extractWars - */ - public boolean isExtractWars() - { - return Boolean.parseBoolean(_properties.get(Deployable.EXTRACT_WARS)); - } - - /** - * Set the extractWars. - * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} property. - * - * @param extractWars the extractWars to set - */ - public void setExtractWars(boolean extractWars) - { - _properties.put(Deployable.EXTRACT_WARS, Boolean.toString(extractWars)); - } - - /** - * Get the parentLoaderPriority. - * This is equivalent to getting the {@link Deployable#PARENT_LOADER_PRIORITY} property. - * - * @return the parentLoaderPriority - */ - public boolean isParentLoaderPriority() - { - return Boolean.parseBoolean(_properties.get(Deployable.PARENT_LOADER_PRIORITY)); - } - - /** - * Set the parentLoaderPriority. - * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} property. - * - * @param parentLoaderPriority the parentLoaderPriority to set - */ - public void setParentLoaderPriority(boolean parentLoaderPriority) - { - _properties.put(Deployable.PARENT_LOADER_PRIORITY, Boolean.toString(parentLoaderPriority)); - } - - /** - * Get the defaultsDescriptor. - * This is equivalent to getting the {@link Deployable#DEFAULTS_DESCRIPTOR} property. - * - * @return the defaultsDescriptor - */ - public String getDefaultsDescriptor() - { - return _properties.get(Deployable.DEFAULTS_DESCRIPTOR); - } - - /** - * Set the defaultsDescriptor. - * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} property. - * - * @param defaultsDescriptor the defaultsDescriptor to set - */ - public void setDefaultsDescriptor(String defaultsDescriptor) - { - _properties.put(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * @param configurations The configuration class names as a comma separated list - */ - public void setConfigurationClasses(String configurations) - { - setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * @param configurations The configuration class names. - */ - public void setConfigurationClasses(String[] configurations) - { - _properties.put(Deployable.CONFIGURATION_CLASSES, (configurations == null) - ? null - : String.join(",", configurations)); - } - - /** - * - * This is equivalent to getting the {@link Deployable#CONFIGURATION_CLASSES} property. - * @return The configuration class names. - */ - public String[] getConfigurationClasses() - { - String cc = _properties.get(Deployable.CONFIGURATION_CLASSES); - return cc == null ? new String[0] : cc.split(","); - } - /** * @param tldBundles Comma separated list of bundles that contain tld jars * that should be setup on the context instances created here. */ public void setTldBundles(String tldBundles) { - _properties.put(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, tldBundles); + _attributes.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, tldBundles); } /** @@ -206,7 +109,7 @@ public void setTldBundles(String tldBundles) */ public String getTldBundles() { - return _properties.get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + return (String)_attributes.getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); } public boolean isDeployable(Bundle bundle) @@ -220,8 +123,8 @@ public boolean isDeployable(Bundle bundle) return false; } - - public boolean isDeployable(ServiceReference service) + + public boolean isDeployable(ServiceReference service) { if (service == null) return false; diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index b42ff8ecbf3d..7cd65f5621b2 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.server; import java.util.Comparator; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -70,8 +69,5 @@ public interface Deployable String WAR = "jetty.deploy.war"; String WEBINF_SCAN_JARS = "jetty.deploy.webInfScanJarPattern"; - // TODO: should we deprecate this one? - void initializeDefaults(Map properties); - void initializeDefaults(Attributes attributes); } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index bb1a880a02b6..232c43599a9a 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -170,8 +170,8 @@ public Object addingService(ServiceReference sr) //ensure the providers are configured with the extra bundles that must be scanned from the container classpath if (containerScanBundlePattern != null) { - contextProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); - webAppProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + contextProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + webAppProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); } } else @@ -364,7 +364,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppContext webApp = new WebAppContext(); //Apply defaults from the deployer providers - webApp.initializeDefaults(provider.getProperties()); + webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -418,7 +418,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.setClassLoader(webAppLoader); //Take care of extra provider properties - webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getProperties().get(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); + webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 3bb3e7fb406d..727592c6d6ba 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -56,7 +56,6 @@ import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -209,41 +208,6 @@ public WebAppContext(String contextPath, SessionHandler sessionHandler, Security setProtectedTargets(__dftProtectedTargets); } - @Override - public void initializeDefaults(Map properties) - { - for (String property : properties.keySet()) - { - String value = properties.get(property); - if (LOG.isDebugEnabled()) - LOG.debug("init {}: {}", property, value); - - switch (property) - { - case Deployable.WAR -> - { - if (getWar() == null) - setWar(value); - } - case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); - case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses(value == null ? null : value.split(",")); - case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); - case Deployable.EXTRACT_WARS -> setExtractWAR(Boolean.parseBoolean(value)); - case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority(Boolean.parseBoolean(value)); - case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); - case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor(value); - case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); - case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); - default -> - { - if (LOG.isDebugEnabled() && StringUtil.isNotBlank(value)) - LOG.debug("unknown property {}={}", property, value); - } - } - } - _defaultContextPath = true; - } - @Override public void initializeDefaults(Attributes attributes) { @@ -271,7 +235,7 @@ public void initializeDefaults(Attributes attributes) case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); default -> { - if (LOG.isDebugEnabled() && value != null) + if (LOG.isDebugEnabled()) LOG.debug("unknown property {}={}", keyName, value); } } diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 2782247b908b..0982628430f4 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -170,8 +170,8 @@ public Object addingService(ServiceReference sr) //ensure the providers are configured with the extra bundles that must be scanned from the container classpath if (containerScanBundlePattern != null) { - contextProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); - webAppProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + contextProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + webAppProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); } } else @@ -364,7 +364,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppContext webApp = new WebAppContext(); //Apply defaults from the deployer providers - webApp.initializeDefaults(provider.getProperties()); + webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -418,7 +418,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.setClassLoader(webAppLoader); //Take care of extra provider properties - webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getProperties().get(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); + webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JakartaWebSocketTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JakartaWebSocketTest.java index fb03595abf44..8b3d0b684e99 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JakartaWebSocketTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JakartaWebSocketTest.java @@ -45,7 +45,6 @@ public static void setUpServer() throws Exception server.addXmlConfiguration("NIOHttp.xml"); server.load(); - // server.getServer().setDumpAfterStart(true); server.start(); } diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java index 98679e3bde27..c208582faa22 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java @@ -56,7 +56,6 @@ import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -195,37 +194,6 @@ public WebAppContext(String contextPath, SessionHandler sessionHandler, Security setProtectedTargets(__dftProtectedTargets); } - @Override - public void initializeDefaults(Map properties) - { - for (String property : properties.keySet()) - { - String value = properties.get(property); - if (LOG.isDebugEnabled()) - LOG.debug("init {}: {}", property, value); - - switch (property) - { - case Deployable.WAR -> setWar(value); - case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); - case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses(value == null ? null : value.split(",")); - case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); - case Deployable.EXTRACT_WARS -> setExtractWAR(Boolean.parseBoolean(value)); - case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority(Boolean.parseBoolean(value)); - case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); - case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor(value); - case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); - case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); - default -> - { - if (LOG.isDebugEnabled() && StringUtil.isNotBlank(value)) - LOG.debug("unknown property {}={}", property, value); - } - } - } - _defaultContextPath = true; - } - @Override public void initializeDefaults(Attributes attributes) { @@ -253,7 +221,7 @@ public void initializeDefaults(Attributes attributes) case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); default -> { - if (LOG.isDebugEnabled() && value != null) + if (LOG.isDebugEnabled()) LOG.debug("unknown property {}={}", keyName, value); } } diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index af543c6dcdfe..cb28ef215bb8 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -169,8 +169,8 @@ public Object addingService(ServiceReference sr) //ensure the providers are configured with the extra bundles that must be scanned from the container classpath if (containerScanBundlePattern != null) { - contextProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); - webAppProvider.getProperties().put(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + contextProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); + webAppProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); } } else @@ -362,7 +362,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppContext webApp = new WebAppContext(); //Apply defaults from the deployer providers - webApp.initializeDefaults(provider.getProperties()); + webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -415,7 +415,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.setClassLoader(webAppLoader); //Take care of extra provider properties - webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getProperties().get(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); + webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index 89be9babf757..8a1a696a1ede 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -57,7 +57,6 @@ import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.ClassLoaderDump; @@ -239,36 +238,6 @@ public WebAppContext(Handler.Container parent, String contextPath, SessionHandle HandlerWrapper.setAsParent(parent, this.get()); } - @Override - public void initializeDefaults(Map properties) - { - for (String property : properties.keySet()) - { - String value = properties.get(property); - if (LOG.isDebugEnabled()) - LOG.debug("init {}: {}", property, value); - switch (property) - { - case Deployable.WAR -> setWar(value); - case Deployable.TEMP_DIR -> setTempDirectory(IO.asFile(value)); - case Deployable.CONFIGURATION_CLASSES -> setConfigurationClasses(value == null ? null : value.split(",")); - case Deployable.CONTAINER_SCAN_JARS -> setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, value); - case Deployable.EXTRACT_WARS -> setExtractWAR(Boolean.parseBoolean(value)); - case Deployable.PARENT_LOADER_PRIORITY -> setParentLoaderPriority(Boolean.parseBoolean(value)); - case Deployable.WEBINF_SCAN_JARS -> setAttribute(MetaInfConfiguration.WEBINF_JAR_PATTERN, value); - case Deployable.DEFAULTS_DESCRIPTOR -> setDefaultsDescriptor(value); - case Deployable.SCI_EXCLUSION_PATTERN -> setAttribute("org.eclipse.jetty.containerInitializerExclusionPattern", value); - case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); - default -> - { - if (LOG.isDebugEnabled() && StringUtil.isNotBlank(value)) - LOG.debug("unknown property {}={}", property, value); - } - } - } - _defaultContextPath = true; - } - @Override public void initializeDefaults(Attributes attributes) { @@ -296,7 +265,7 @@ public void initializeDefaults(Attributes attributes) case Deployable.SCI_ORDER -> setAttribute("org.eclipse.jetty.containerInitializerOrder", value); default -> { - if (LOG.isDebugEnabled() && value != null) + if (LOG.isDebugEnabled()) LOG.debug("unknown property {}={}", keyName, value); } } From 96d5becd82ed36bce29cf61b29b5106e4ab8276f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Dec 2024 12:36:13 -0600 Subject: [PATCH 020/104] Simplify else --- .../org/eclipse/jetty/deploy/providers/ContextProvider.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 89dd8f52db29..fae882d5773f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -250,10 +250,7 @@ private Object newContextInstance(String contextHandlerClassName, App app) throw { throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app + " in environment " + app.getEnvironmentName()); } - else - { - return contextClass.getConstructor().newInstance(); - } + return contextClass.getConstructor().newInstance(); } /** From ee188456cfdddee6a68c5e40612f5b8749b4ac82 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 20 Dec 2024 15:10:21 -0600 Subject: [PATCH 021/104] Reworking deploy.createContextHandler to be easier to follow --- .../jetty/deploy/DeploymentManager.java | 3 +- .../deploy/providers/ContextProvider.java | 242 +++++++++++------- .../providers/ContextProviderStartupTest.java | 7 +- .../ee9/test/support/XmlBasedJettyServer.java | 2 +- .../test/websocket/JakartaWebSocketTest.java | 1 - .../src/test/resources/deploy.xml | 2 +- 6 files changed, 159 insertions(+), 98 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index dce0331884c4..1b65b486f09d 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -153,7 +153,8 @@ public String getDefaultEnvironmentName() */ public void addApp(App app) { - LOG.info("addApp: {}", app); + if (LOG.isDebugEnabled()) + LOG.debug("addApp: {}", app); AppEntry entry = new AppEntry(); entry.app = app; entry.setLifeCycleNode(_lifecycle.getNodeByName("undeployed")); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index fae882d5773f..e05663151a87 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -154,103 +154,165 @@ public ContextHandler createContextHandler(final App app) throws Exception // prepare app attributes to use for app deployment Attributes appAttributes = initAttributes(environment, app); - // check if there is a specific ContextHandler type to create set in the - // properties associated with the webapp. If there is, we create it _before_ - // applying the environment xml file. - Object context = newContextInstance((String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS), app); - initializeContextPath(getContextHandler(context), path); - - // Collect the optional environment context xml files. - // Order them according to the name of their property key names. - List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() - .stream() - .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) - .map(k -> - { - Path envXmlPath = Paths.get((String)appAttributes.getAttribute(k)); - if (!envXmlPath.isAbsolute()) - { - Path monitoredPath = getMonitoredDirResource().getPath(); - // not all Resource implementations support java.nio.file.Path. - if (monitoredPath != null) - { - envXmlPath = monitoredPath.getParent().resolve(envXmlPath); - } - } - return envXmlPath; - }) - .filter(Files::isRegularFile) - .sorted(PathCollators.byName(true)) - .toList(); - - // apply each environment context xml file - for (Path envXmlPath : sortedEnvXmlPaths) - { - if (LOG.isDebugEnabled()) - LOG.debug("Applying environment specific context file {}", envXmlPath); - context = applyXml(context, envXmlPath, environment, appAttributes); - } - - // Handle a context XML file - if (FileID.isXml(path)) - { - context = applyXml(context, path, environment, appAttributes); - } - // Otherwise it must be a directory or an archive - else if (!Files.isDirectory(path) && !FileID.isWebArchive(path)) + /* + * The process now is to figure out the context object to use. + * This can come from a number of places. + * 1. If an XML deployable, this is the entry. + * 2. If another deployable (like a web archive, or directory), then check attributes. + * a. use the app attributes to figure out the context handler class. + * b. use the environment attributes default context handler class. + */ + Object context = newContextInstance(environment, app, appAttributes, path); + if (context == null) { throw new IllegalStateException("unable to create ContextHandler for " + app); } - else - { - // If we reach this point, we don't have a Context XML Deployable. - // We are either a directory or a web archive (war) - // Set the WAR attribute to point to this directory or web-archive. - appAttributes.setAttribute(Deployable.WAR, path.toString()); - } + if (LOG.isDebugEnabled()) + LOG.debug("Context {} created from app {}", context.getClass().getName(), app); - if (context == null) + // Apply environment properties and XML to context + if (applyEnvironmentXml(context, environment, appAttributes)) { - context = newContextInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT), app); - initializeContextPath(getContextHandler(context), path); + // If an XML deployable, apply full XML over environment XML changes + if (FileID.isXml(path)) + context = applyXml(context, path, environment, appAttributes); } - if (context == null) - { - throw new IllegalStateException("ContextHandler class is null for app " + app); - } + // Set a backup value for the path to the war in case it hasn't already been set + // via a different means. This is especially important for a deployable App + // that is only a .war file (no XML). The eventual WebInfConfiguration + // will use this attribute. + appAttributes.setAttribute(Deployable.WAR, path.toString()); + + // Initialize any deployable + if (context instanceof Deployable deployable) + deployable.initializeDefaults(appAttributes); - // Look for the contextHandler itself + return getContextHandler(context); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + /** + * Initialize a new Context object instance. + * + *

        + * The search order is: + *

        + *
          + *
        1. If app attribute {@link Deployable#CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
        2. + *
        3. If App deployable path is XML, apply XML {@code }
        4. + *
        5. Fallback to environment attribute {@link Deployable#CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
        6. + *
        + * + * @param environment the environment context applies to + * @param app the App for the context + * @param appAttributes the Attributes for the App + * @param path the path of the deployable + * @return the Context Object. + * @throws Exception if unable to create Object instance. + */ + private Object newContextInstance(Environment environment, App app, Attributes appAttributes, Path path) throws Exception + { + Object context = newInstance((String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS)); + if (context != null) + { ContextHandler contextHandler = getContextHandler(context); if (contextHandler == null) throw new IllegalStateException("Unknown context type of " + context); - // Allow raw ContextHandler to be initialized properly + initializeContextPath(contextHandler, path); initializeContextHandler(contextHandler, path, appAttributes); + return context; + } - // Initialize deployable - if (contextHandler instanceof Deployable deployable) - deployable.initializeDefaults(appAttributes); - - return contextHandler; + if (FileID.isXml(path)) + { + context = applyXml(null, path, environment, appAttributes); + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + return context; } - finally + + // fallback to default from environment. + context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); + + if (context != null) { - Thread.currentThread().setContextClassLoader(old); + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + + initializeContextPath(contextHandler, path); + initializeContextHandler(contextHandler, path, appAttributes); + return context; } + + return null; } - private Object newContextInstance(String contextHandlerClassName, App app) throws Exception + private Object newInstance(String className) throws Exception { - if (StringUtil.isBlank(contextHandlerClassName)) + if (StringUtil.isBlank(className)) + return null; + if (LOG.isDebugEnabled()) + LOG.debug("Attempting to load class {}", className); + Class clazz = Loader.loadClass(className); + if (clazz == null) return null; + return clazz.getConstructor().newInstance(); + } - Class contextClass = Loader.loadClass(contextHandlerClassName); - if (contextClass == null) + /** + * Apply optional environment specific XML to context. + * + * @param context the context to apply environment specific behavior to + * @param environment the environment to use + * @param appAttributes the attributes of the app + * @return true it environment specific XML was applied. + * @throws Exception if unable to apply environment configuration. + */ + private boolean applyEnvironmentXml(Object context, Environment environment, Attributes appAttributes) throws Exception + { + // Collect the optional environment context xml files. + // Order them according to the name of their property key names. + List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() + .stream() + .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) + .map(k -> + { + Path envXmlPath = Paths.get((String)appAttributes.getAttribute(k)); + if (!envXmlPath.isAbsolute()) + { + Path monitoredPath = getMonitoredDirResource().getPath(); + // not all Resource implementations support java.nio.file.Path. + if (monitoredPath != null) + { + envXmlPath = monitoredPath.getParent().resolve(envXmlPath); + } + } + return envXmlPath; + }) + .filter(Files::isRegularFile) + .sorted(PathCollators.byName(true)) + .toList(); + + boolean xmlApplied = false; + + // apply each environment context xml file + for (Path envXmlPath : sortedEnvXmlPaths) { - throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app + " in environment " + app.getEnvironmentName()); + if (LOG.isDebugEnabled()) + LOG.debug("Applying environment specific context file {}", envXmlPath); + context = applyXml(context, envXmlPath, environment, appAttributes); + xmlApplied = true; } - return contextClass.getConstructor().newInstance(); + + return xmlApplied; } /** @@ -474,29 +536,30 @@ protected ClassLoader findCoreContextClassLoader(Path path) throws IOException return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); } + /** + * Find the {@link ContextHandler} for the provided {@link Object} + * + * @param context the raw context object + * @return the {@link ContextHandler} for the context, or null if no ContextHandler associated with context. + */ private ContextHandler getContextHandler(Object context) { if (context == null) return null; - // find the ContextHandler - ContextHandler contextHandler; if (context instanceof ContextHandler handler) - contextHandler = handler; - else if (Supplier.class.isAssignableFrom(context.getClass())) + return handler; + + if (Supplier.class.isAssignableFrom(context.getClass())) { @SuppressWarnings("unchecked") Supplier provider = (Supplier)context; - contextHandler = provider.get(); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Not a context {}", context); - return null; + return provider.get(); } - return contextHandler; + if (LOG.isDebugEnabled()) + LOG.debug("Not a context {}", context); + return null; } protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) @@ -506,13 +569,10 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path assert contextHandler != null; - if (contextHandler.getBaseResource() == null) + if (contextHandler.getBaseResource() == null && Files.isDirectory(path)) { - if (Files.isDirectory(path)) - { - ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); - contextHandler.setBaseResource(resourceFactory.newResource(path)); - } + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); } // pass through properties as attributes directly diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index 57c55f1c7643..723f45d9109d 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -35,6 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -112,7 +113,7 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception ContextHandler context = jetty.getContextHandler("/bar"); assertNotNull(context); assertThat(context.getAttribute("core-context-0"), equalTo("core-context-0")); - assertTrue(context instanceof BarContextHandler); + assertThat(context, instanceOf(BarContextHandler.class)); //check core-context-other.xml was applied to the produced context assertThat(context.getAttribute("other"), equalTo("othervalue")); //check core-context-other.xml was applied AFTER core-context.xml @@ -138,7 +139,7 @@ public void testStartupWithAbsoluteEnvironmentContext() throws Exception ContextHandler context = jetty.getContextHandler("/bar"); assertNotNull(context); assertThat(context.getAttribute("core-context-0"), equalTo("core-context-0")); - assertTrue(context instanceof BarContextHandler); + assertThat(context, instanceOf(BarContextHandler.class)); //check core-context-other.xml was applied to the produced context assertThat(context.getAttribute("other"), equalTo("othervalue")); //check core-context-other.xml was applied AFTER core-context.xml @@ -164,7 +165,7 @@ public void testNonEnvironmentPropertyFileNotApplied() throws Exception //test that the context was deployed as expected and that the non-applicable properties files were ignored ContextHandler context = jetty.getContextHandler("/bar"); assertNotNull(context); - assertTrue(context instanceof BarContextHandler); + assertThat(context, instanceOf(BarContextHandler.class)); } /** diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java index 78608b6c4a6a..0bea58b94e93 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/support/XmlBasedJettyServer.java @@ -176,7 +176,7 @@ public void start() throws Exception { assertNotNull(_server, "Server should not be null (failed load?)"); - _server.setDumpAfterStart(false); + _server.setDumpAfterStart(true); _server.start(); // Find the active server port. diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JakartaWebSocketTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JakartaWebSocketTest.java index e3fa0584b1a0..1e7696b04a2e 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JakartaWebSocketTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JakartaWebSocketTest.java @@ -45,7 +45,6 @@ public static void setUpServer() throws Exception server.addXmlConfiguration("NIOHttp.xml"); server.load(); - // server.getServer().setDumpAfterStart(true); server.start(); } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 3e19a33ba574..aea87fa1f578 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -29,7 +29,7 @@ true - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ From e498f2c2c0181b03481fc95fe6bc7642d51750bf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 20 Dec 2024 16:57:55 -0600 Subject: [PATCH 022/104] Fixing WebAppContext default-context-path mistake --- .../deploy/providers/ContextProvider.java | 18 +++++++++++++----- .../jetty/ee10/webapp/WebAppContext.java | 3 +-- .../jetty/ee11/webapp/WebAppContext.java | 3 +-- .../jetty/ee9/webapp/WebAppContext.java | 3 +-- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index e05663151a87..4193d5bc454b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -589,12 +589,16 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); if (StringUtil.isNotBlank(contextPath)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Context {} initialized with contextPath: {}", contextHandler, contextPath); contextHandler.setContextPath(contextPath); + } } - protected void initializeContextPath(ContextHandler context, Path path) + protected void initializeContextPath(ContextHandler contextHandler, Path path) { - if (context == null) + if (contextHandler == null) return; // Strip any 3 char extension from non directories @@ -611,7 +615,7 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) { int dash = contextPath.indexOf('-'); String virtual = contextPath.substring(dash + 1); - context.setVirtualHosts(Arrays.asList(virtual.split(","))); + contextHandler.setVirtualHosts(Arrays.asList(virtual.split(","))); contextPath = "/"; } @@ -619,8 +623,12 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) if (contextPath.charAt(0) != '/') contextPath = "/" + contextPath; - context.setDisplayName(basename); - context.setContextPath(contextPath); + if (LOG.isDebugEnabled()) + LOG.debug("ContextHandler {} initialized with displayName: {}", contextHandler, basename); + contextHandler.setDisplayName(basename); + if (LOG.isDebugEnabled()) + LOG.debug("ContextHandler {} initialized with contextPath: {}", contextHandler, contextPath); + contextHandler.setContextPath(contextPath); } protected boolean isDeployable(Path path) diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 727592c6d6ba..7eb6ebb322ea 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -236,11 +236,10 @@ public void initializeDefaults(Attributes attributes) default -> { if (LOG.isDebugEnabled()) - LOG.debug("unknown property {}={}", keyName, value); + LOG.debug("skipped init property {}={}", keyName, value); } } } - _defaultContextPath = true; } public boolean isContextPathDefault() diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java index c208582faa22..f95670ff9ae7 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebAppContext.java @@ -222,11 +222,10 @@ public void initializeDefaults(Attributes attributes) default -> { if (LOG.isDebugEnabled()) - LOG.debug("unknown property {}={}", keyName, value); + LOG.debug("skipped init property {}={}", keyName, value); } } } - _defaultContextPath = true; } public boolean isContextPathDefault() diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index 8a1a696a1ede..d6aff27746db 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -266,11 +266,10 @@ public void initializeDefaults(Attributes attributes) default -> { if (LOG.isDebugEnabled()) - LOG.debug("unknown property {}={}", keyName, value); + LOG.debug("skipped init property {}={}", keyName, value); } } } - _defaultContextPath = true; } public boolean isContextPathDefault() From cf69f2606b7b409aac2a3085a60b2f52f858f01e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 15 Jan 2025 13:57:08 -0600 Subject: [PATCH 023/104] Introducing DeploymentUnit to simplify added/changed/removed update behaviors. + A DeploymentUnit is all the paths for a specific basename that are being tracked for deployment reasons. + New DeploymentUnitsTest test cases + A new ContextProvider.getMainDeploymentPath for defining the main path deployment heuristics for a unit --- .../deploy/providers/ContextProvider.java | 95 +++--- .../deploy/providers/ScanningAppProvider.java | 77 +++-- .../providers/internal/DeploymentUnits.java | 206 +++++++++++++ .../ContextProviderRuntimeUpdatesTest.java | 15 +- .../deploy/providers/ContextProviderTest.java | 134 +++++++++ .../internal/DeploymentUnitsTest.java | 281 ++++++++++++++++++ .../java/org/eclipse/jetty/util/FileID.java | 14 +- 7 files changed, 727 insertions(+), 95 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 4193d5bc454b..30fe3cc757ea 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -24,12 +24,14 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -71,11 +73,11 @@ *
      • Directories with names ending in {@code ".d"} are ignored
      • *
      • Property files with names ending in {@code ".properties"} are not deployed.
      • *
      • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be - * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
      • + * the unpacked WAR and only the WAR file is deployed (which may reuse the unpacked directory) *
      • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * an unpacked WAR and only the XML is deployed (which may use the directory in its configuration)
      • - *
      • If a WAR file and a matching XML exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR is assumed to - * be configured by the XML and only the XML is deployed. + * an unpacked WAR and only the XML file is deployed (which may use the directory in its configuration)
      • + *
      • If a WAR file and a matching XML file exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR file is assumed to + * be configured by the XML file and only the XML file is deployed. *
      *

      For XML configured contexts, the following is available.

      *
        @@ -133,7 +135,7 @@ public ContextHandler createContextHandler(final App app) throws Exception app, Environment.getAll().stream() .map(Environment::getName) - .collect(Collectors.joining(",", "[", "]")) + .collect(Collectors.joining(", ", "[", "]")) ); return null; } @@ -631,44 +633,42 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) contextHandler.setContextPath(contextPath); } - protected boolean isDeployable(Path path) - { - String basename = FileID.getBasename(path); - - // is the file that changed a directory? - if (Files.isDirectory(path)) - { - // deploy if there is not a .xml or .war file of the same basename? - return !Files.exists(path.getParent().resolve(basename + ".xml")) && - !Files.exists(path.getParent().resolve(basename + ".XML")) && - !Files.exists(path.getParent().resolve(basename + ".war")) && - !Files.exists(path.getParent().resolve(basename + ".WAR")); - } - - // deploy if it is a .war and there is not a .xml for of the same basename - if (FileID.isWebArchive(path)) - { - // if a .xml file exists for it - return !Files.exists(path.getParent().resolve(basename + ".xml")) && - !Files.exists(path.getParent().resolve(basename + ".XML")); - } - - // otherwise only deploy an XML - return FileID.isXml(path); - } - + /** + * Apply the main deployable heuristics referenced in the main javadoc + * for this class. + * + * @param paths the set of paths that represent a single unit of deployment. + * @return the main deployable. + */ @Override - protected void pathAdded(Path path) throws Exception + protected Path getMainDeploymentPath(Set paths) { - if (isDeployable(path)) - super.pathAdded(path); - } + // XML always win. + List xmls = paths.stream() + .filter(FileID::isXml) + .toList(); + if (xmls.size() == 1) + return xmls.get(0); + else if (xmls.size() > 1) + throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); + // WAR files are next. + List wars = paths.stream() + .filter(FileID::isWebArchive) + .toList(); + if (wars.size() == 1) + return wars.get(0); + else if (wars.size() > 1) + throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); + // Directories next. + List dirs = paths.stream() + .filter(Files::isDirectory) + .toList(); + if (dirs.size() == 1) + return dirs.get(0); + if (dirs.size() > 1) + throw new IllegalStateException("More than 1 Directory for deployable " + asStringList(dirs)); - @Override - protected void pathChanged(Path path) throws Exception - { - if (isDeployable(path)) - super.pathChanged(path); + throw new IllegalStateException("Unable to determine main deployable " + asStringList(paths)); } /** @@ -784,6 +784,14 @@ private void loadProperties(Environment environment, InputStream inputStream) th environment.setAttribute(name, props.getProperty(name))); } + private static String asStringList(Collection paths) + { + return paths.stream() + .sorted(PathCollators.byName(true)) + .map(Path::toString) + .collect(Collectors.joining(", ", "[", "]")); + } + /** * Builder of a deployment configuration for a specific {@link Environment}. * @@ -964,13 +972,6 @@ public boolean accept(File dir, String name) if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) return false; - // ignore directories that have sibling war or XML file - if (Files.exists(dir.toPath().resolve(name + ".war")) || - Files.exists(dir.toPath().resolve(name + ".WAR")) || - Files.exists(dir.toPath().resolve(name + ".xml")) || - Files.exists(dir.toPath().resolve(name + ".XML"))) - return false; - return true; } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 3ae35d5c4b39..6e7d730425fc 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -31,6 +30,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.providers.internal.DeploymentUnits; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -38,7 +38,6 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.PathCollators; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.resource.Resources; @@ -46,11 +45,12 @@ import org.slf4j.LoggerFactory; @ManagedObject("Abstract Provider for loading webapps") -public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider +public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider, DeploymentUnits.Events { private static final Logger LOG = LoggerFactory.getLogger(ScanningAppProvider.class); - private final Map _appMap = new HashMap<>(); + private DeploymentUnits _units = new DeploymentUnits(); + private Map _appMap = new HashMap<>(); private DeploymentManager _deploymentManager; private FilenameFilter _filenameFilter; @@ -62,30 +62,10 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements private final Scanner.BulkListener _scannerBulkListener = new Scanner.BulkListener() { - private final Set _addedPaths = new HashSet<>(); - @Override - public void pathsChanged(Set paths) throws Exception + public void pathsChanged(Set paths) { - List sortedPaths = paths.stream() - .sorted(PathCollators.byName(true)) - .toList(); - - for (Path path : sortedPaths) - { - if (Files.exists(path)) - { - if (_addedPaths.add(path)) - ScanningAppProvider.this.pathAdded(path); - else - ScanningAppProvider.this.pathChanged(path); - } - else - { - _addedPaths.remove(path); - ScanningAppProvider.this.pathRemoved(path); - } - } + _units.processChanges(paths, ScanningAppProvider.this); } @Override @@ -132,7 +112,7 @@ protected void setFilenameFilter(FilenameFilter filter) /** * @return The index of currently deployed applications. */ - protected Map getDeployedApps() + protected Map getDeployedApps() { return _appMap; } @@ -238,41 +218,58 @@ protected boolean exists(String path) return _scanner.exists(path); } - protected void pathAdded(Path path) throws Exception + /** + * Given a set of Paths that belong to the same unit of deployment, + * pick the main Path that is actually the point of deployment. + * + * @param paths the set of paths that represent a single unit of deployment. (all paths in {@link Set} are of the same basename) + * @return the specific path that represents the main point of deployment (eg: xml if it exists) + */ + protected abstract Path getMainDeploymentPath(Set paths); + + public void unitAdded(String basename, Set paths) { - App app = ScanningAppProvider.this.createApp(path); + Path mainDeploymentPath = getMainDeploymentPath(paths); + App app = this.createApp(mainDeploymentPath); + if (LOG.isDebugEnabled()) - LOG.debug("fileAdded {}: {}", path, app); + LOG.debug("unitAdded {} -> {}: {}", basename, paths, app); if (app != null) { - _appMap.put(path, app); + _appMap.put(basename, app); _deploymentManager.addApp(app); } } - protected void pathChanged(Path path) throws Exception + @Override + public void unitChanged(String basename, Set paths) { - App oldApp = _appMap.remove(path); + App oldApp = _appMap.remove(basename); if (oldApp != null) _deploymentManager.removeApp(oldApp); - App app = ScanningAppProvider.this.createApp(path); + + Path mainDeploymentPath = getMainDeploymentPath(paths); + App app = ScanningAppProvider.this.createApp(mainDeploymentPath); if (LOG.isDebugEnabled()) - LOG.debug("fileChanged {}: {}", path, app); + LOG.debug("unitChanged {} -> {}: {}", basename, paths, app); if (app != null) { - _appMap.put(path, app); + _appMap.put(basename, app); _deploymentManager.addApp(app); } } - protected void pathRemoved(Path path) throws Exception + @Override + public void unitRemoved(String basename) { - App app = _appMap.remove(path); + App app = _appMap.remove(basename); if (LOG.isDebugEnabled()) - LOG.debug("fileRemoved {}: {}", path, app); + LOG.debug("unitRemoved {}: {}", basename, app); if (app != null) + { _deploymentManager.removeApp(app); + } } /** @@ -287,7 +284,7 @@ public DeploymentManager getDeploymentManager() public Resource getMonitoredDirResource() { - if (_monitored.size() == 0) + if (_monitored.isEmpty()) return null; if (_monitored.size() > 1) throw new IllegalStateException(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java new file mode 100644 index 000000000000..4fcd8a3a5d26 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java @@ -0,0 +1,206 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers.internal; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.resource.PathCollators; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The role of DeploymentUnits is to keep track groups of files (and directories) that represent + * a single unit of deployment, identifying additions/changes/deletions of files/dirs properly. + */ +public class DeploymentUnits +{ + enum EventType + { + UNCHANGED, + CHANGED, + ADDED, + REMOVED + } + + private static final Logger LOG = LoggerFactory.getLogger(DeploymentUnits.class); + + /** + * The units that are being tracked by this component. + * Key is the basename (case-insensitive) and the value are all the paths known for that unit. + */ + private HashMap> units = new HashMap<>(); + + public Set getUnit(String basename) + { + return units.get(basename); + } + + public synchronized void processChanges(Set rawPaths, Events events) + { + if (LOG.isDebugEnabled()) + { + LOG.debug("processChanges: rawPaths: {}", rawPaths.stream() + .sorted(PathCollators.byName(true)) + .map(Path::toString) + .collect(Collectors.joining(", ", "[", "]")) + ); + } + Map basenameEvents = new HashMap<>(); + + // Figure out what changed, and how. + for (Path path : rawPaths) + { + // to lower-case uses system Locale, as we are working with the system FS. + String basename = FileID.getBasename(path).toLowerCase(); + basenameEvents.putIfAbsent(basename, EventType.UNCHANGED); + + if (Files.exists(path)) + { + // A path that exists, either added, or changed. + if (!units.containsKey(basename)) + { + if (LOG.isDebugEnabled()) + LOG.debug("basename: {} - new - {}", basename, path); + // this is a new unit + Set unitPaths = units.computeIfAbsent(basename, k -> new HashSet<>()); + unitPaths.add(path); + basenameEvents.compute(basename, + (b, v) -> + { + if (v == null) + return EventType.ADDED; + return switch (v) + { + case ADDED, CHANGED: + { + yield v; // keep value + } + case UNCHANGED, REMOVED: + { + yield EventType.ADDED; + } + }; + }); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("basename: {} - existing - {}", basename, path); + // this is an existing unit + Set unitPaths = units.computeIfAbsent(basename, k -> new HashSet<>()); + unitPaths.add(path); + units.put(basename, unitPaths); + basenameEvents.compute(basename, + (b, v) -> + { + if (v == null) + return EventType.CHANGED; + return switch (v) + { + case ADDED, CHANGED -> + { + yield v; // keep value + } + case UNCHANGED, REMOVED -> + { + yield EventType.CHANGED; + } + }; + }); + } + } + else + { + // A path was removed + + // Only care about paths that belong to an existing unit + if (units.containsKey(basename)) + { + // this is an existing unit + Set unitPaths = units.get(basename); + unitPaths.remove(path); + if (unitPaths.isEmpty()) + { + if (LOG.isDebugEnabled()) + LOG.debug("basename: {} - removed - {}", basename, path); + // remove unit + basenameEvents.put(basename, EventType.REMOVED); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("basename: {} - changed - {}", basename, path); + // unit has changed. + basenameEvents.put(basename, EventType.CHANGED); + } + } + } + } + + // Notify of changes, alphabetically. + List sortedBasenames = basenameEvents.keySet() + .stream() + .distinct() + .sorted() + .toList(); + + for (String basename : sortedBasenames) + { + Set paths = getUnit(basename); + if (LOG.isDebugEnabled()) + { + LOG.debug("reporting unit{}: basename: {} - paths: {}", + basenameEvents.get(basename), + basename, + paths.stream() + .sorted(PathCollators.byName(true)) + .map(Path::toString) + .collect(Collectors.joining(", ", "[", "]")) + ); + } + switch (basenameEvents.get(basename)) + { + case ADDED -> + { + events.unitAdded(basename, paths); + } + case CHANGED -> + { + events.unitChanged(basename, getUnit(basename)); + } + case REMOVED -> + { + events.unitRemoved(basename); + } + } + } + } + + public interface Events + { + void unitAdded(String basename, Set paths); + + void unitChanged(String basename, Set paths); + + void unitRemoved(String basename); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java index 653c1a5a12a3..ffe6787a9fcd 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java @@ -39,7 +39,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertSame; /** * Similar in scope to {@link ContextProviderStartupTest}, except is concerned with the modification of existing @@ -124,8 +123,10 @@ public void waitForDirectoryScan() } /** - * Test that if a war file has a context xml sibling, it will only be redeployed when the - * context xml changes, not the war. + * Test that if a unit (called "simple" has both a war file and xml file), will be + * redeployed if the war file is touched (note: the XML is the main deployable path) + * + * This addresses issue https://github.com/jetty/jetty.project/issues/12543 */ @Test public void testSelectiveDeploy(WorkDir workDir) throws Exception @@ -143,20 +144,20 @@ public void testSelectiveDeploy(WorkDir workDir) throws Exception assertNotNull(contextHandler); assertEquals(jetty.getJettyBasePath().resolve("tmp").toFile().getAbsolutePath(), contextHandler.getTempDirectory().getAbsolutePath()); - //touch the context xml and check the context handler was redeployed + // touch the context xml and check the context handler was redeployed jetty.copyWebapp("simple.xml", "simple.xml"); waitForDirectoryScan(); ContextHandler contextHandler2 = jetty.getContextHandler("/simple"); assertNotNull(contextHandler2); assertNotSame(contextHandler, contextHandler2); - //touch the war file and check the context handler was NOT redeployed - Thread.sleep(1000L); //ensure at least a millisecond has passed + // touch the war file and check that the context handler was redeployed + Thread.sleep(1000L); // ensure at least a millisecond has passed Files.setLastModifiedTime(webappsDir.resolve("simple.war"), FileTime.fromMillis(System.currentTimeMillis())); waitForDirectoryScan(); ContextHandler contextHandler3 = jetty.getContextHandler("/simple"); assertNotNull(contextHandler3); - assertSame(contextHandler2, contextHandler3); + assertNotSame(contextHandler2, contextHandler3); } /** diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java new file mode 100644 index 000000000000..ab2e73d048ea --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +@ExtendWith(WorkDirExtension.class) +public class ContextProviderTest +{ + public WorkDir workDir; + + /** + * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. + *

        + * Test with only a single XML. + */ + @Test + public void testMainDeploymentPathOnlyXml() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + ContextProvider provider = new ContextProvider(); + Path main = provider.getMainDeploymentPath(Set.of(xml)); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. + *

        + * Test with an XML and WAR. + */ + @Test + public void testMainDeploymentPathXmlAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + ContextProvider provider = new ContextProvider(); + Path main = provider.getMainDeploymentPath(Set.of(xml, war)); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. + *

        + * Test with a Directory and WAR. + */ + @Test + public void testMainDeploymentPathDirAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + ContextProvider provider = new ContextProvider(); + Path main = provider.getMainDeploymentPath(Set.of(appDir, war)); + + assertThat("main path", main, equalTo(war)); + } + + /** + * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. + *

        + * Test with a Directory and XML. + */ + @Test + public void testMainDeploymentPathDirAndXml() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + ContextProvider provider = new ContextProvider(); + Path main = provider.getMainDeploymentPath(Set.of(appDir, xml)); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. + *

        + * Test with a Directory and XML and WAR + */ + @Test + public void testMainDeploymentPathDirAndXmlAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + ContextProvider provider = new ContextProvider(); + Path main = provider.getMainDeploymentPath(Set.of(appDir, xml, war)); + + assertThat("main path", main, equalTo(xml)); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java new file mode 100644 index 000000000000..1fd6c5c7b3f3 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java @@ -0,0 +1,281 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers.internal; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; + +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; + +@ExtendWith(WorkDirExtension.class) +public class DeploymentUnitsTest +{ + public WorkDir workDir; + + @Test + public void testNewXmlOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + DeploymentUnits units = new DeploymentUnits(); + units.processChanges(Set.of(xml), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(1)); + assertThat("paths", paths, contains(xml)); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + } + + @Test + public void testXmlThenRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + DeploymentUnits units = new DeploymentUnits(); + + // Initial deployment. + units.processChanges(Set.of(xml), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(1)); + assertThat("paths", paths, contains(xml)); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + + // Removed deployment. + Files.deleteIfExists(xml); + units.processChanges(Set.of(xml), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + fail("Should not cause a unitAdded"); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + assertThat("basename", basename, is("bar")); + } + }); + } + + @Test + public void testNewXmlAndWarOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DeploymentUnits units = new DeploymentUnits(); + units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(2)); + assertThat("paths", paths, containsInAnyOrder(xml, war)); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + } + + @Test + public void testXmlAndWarWithXmlUpdate() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DeploymentUnits units = new DeploymentUnits(); + + // Trigger first set of changes (both files) + units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(2)); + assertThat("paths", paths, containsInAnyOrder(xml, war)); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + + // Trigger second set of changes (only xml) + units.processChanges(Set.of(xml), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + fail("Should not cause a unitAdded"); + } + + @Override + public void unitChanged(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(2)); + assertThat("paths", paths, containsInAnyOrder(xml, war)); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + } + + @Test + public void testXmlAndWarWithXmlRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DeploymentUnits units = new DeploymentUnits(); + + // Trigger first set of changes (both files) + units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(2)); + assertThat("paths", paths, containsInAnyOrder(xml, war)); + } + + @Override + public void unitChanged(String basename, Set paths) + { + fail("Should not cause a unitChanged"); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + + // Trigger second set of changes (war & xml), XML is deleted + Files.deleteIfExists(xml); + units.processChanges(Set.of(war, xml), new DeploymentUnits.Events() + { + @Override + public void unitAdded(String basename, Set paths) + { + fail("Should not cause a unitAdded"); + } + + @Override + public void unitChanged(String basename, Set paths) + { + assertThat("basename", basename, is("bar")); + assertThat("paths.size", paths.size(), is(1)); + assertThat("paths", paths, containsInAnyOrder(war)); + } + + @Override + public void unitRemoved(String basename) + { + fail("Should not cause a unitRemoved"); + } + }); + } +} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java index dc0556a30056..e9a248c5162b 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java @@ -41,7 +41,19 @@ public static String getBasename(Path path) Path filename = path.getFileName(); if (filename == null) return ""; - String basename = filename.toString(); + return getBasename(filename.toString()); + } + + /** + * Retrieve the basename of a path. This is the name of the + * last segment of the path, with any dot suffix (e.g. ".war") removed + * + * @param filename The filename only (no paths) + * @return the filename without any dot suffix + */ + public static String getBasename(String filename) + { + String basename = filename; int dot = basename.lastIndexOf('.'); if (dot >= 0) basename = basename.substring(0, dot); From 1ebdd103b9d395e474e2f9a3222770d253bc7fa7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 16 Jan 2025 14:02:15 -0600 Subject: [PATCH 024/104] Fix deprecation warnings --- .../org/eclipse/jetty/util/ScannerTest.java | 80 +++++++++---------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java index c1780c07496a..01c6809846bf 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java @@ -13,13 +13,13 @@ package org.eclipse.jetty.util; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.PathMatcher; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -139,22 +139,18 @@ public String toString() @Test public void testDepth() throws Exception { - File root = new File(_directory.toFile(), "root"); + Path root = _directory.resolve("root"); FS.ensureDirExists(root); - FS.touch(new File(root, "foo.foo")); - FS.touch(new File(root, "foo2.foo")); - File dir = new File(root, "xxx"); + FS.touch(root.resolve("foo.foo")); + FS.touch(root.resolve("foo2.foo")); + Path dir = root.resolve("xxx"); FS.ensureDirExists(dir); - File x1 = new File(dir, "xxx.foo"); - FS.touch(x1); - File x2 = new File(dir, "xxx2.foo"); - FS.touch(x2); - File dir2 = new File(dir, "yyy"); + FS.touch(dir.resolve("xxx.foo")); + FS.touch(dir.resolve("xxx2.foo")); + Path dir2 = dir.resolve("yyy"); FS.ensureDirExists(dir2); - File y1 = new File(dir2, "yyy.foo"); - FS.touch(y1); - File y2 = new File(dir2, "yyy2.foo"); - FS.touch(y2); + FS.touch(dir2.resolve("yyy.foo")); + FS.touch(dir2.resolve("yyy2.foo")); BlockingQueue queue = new LinkedBlockingQueue<>(); Scanner scanner = new Scanner(); @@ -162,7 +158,7 @@ public void testDepth() throws Exception scanner.setScanDepth(0); scanner.setReportDirs(true); scanner.setReportExistingFilesOnStartup(true); - scanner.addDirectory(root.toPath()); + scanner.addDirectory(root); scanner.addListener(new Scanner.DiscreteListener() { @Override @@ -188,14 +184,14 @@ public void fileAdded(String filename) Event e = queue.take(); assertNotNull(e); assertEquals(Notification.ADDED, e._notification); - assertTrue(e._filename.endsWith(root.getName())); + assertTrue(e._filename.endsWith(root.getFileName().toString())); queue.clear(); scanner.stop(); scanner.reset(); //Depth one should report the dir itself and its file and dir direct children scanner.setScanDepth(1); - scanner.addDirectory(root.toPath()); + scanner.addDirectory(root); scanner.start(); assertEquals(4, queue.size()); queue.clear(); @@ -204,7 +200,7 @@ public void fileAdded(String filename) //Depth 2 should report the dir itself, all file children, xxx and xxx's children scanner.setScanDepth(2); - scanner.addDirectory(root.toPath()); + scanner.addDirectory(root); scanner.start(); assertEquals(7, queue.size()); @@ -214,34 +210,34 @@ public void fileAdded(String filename) @Test public void testPatterns() throws Exception { - //test include and exclude patterns - File root = new File(_directory.toFile(), "proot"); + // test include and exclude patterns + Path root = _directory.resolve("proot"); FS.ensureDirExists(root); - File ttt = new File(root, "ttt.txt"); + Path ttt = root.resolve("ttt.txt"); FS.touch(ttt); - FS.touch(new File(root, "ttt.foo")); - File dir = new File(root, "xxx"); + FS.touch(root.resolve("ttt.foo")); + Path dir = root.resolve("xxx"); FS.ensureDirExists(dir); - File x1 = new File(dir, "ttt.xxx"); + Path x1 = dir.resolve("ttt.xxx"); FS.touch(x1); - File x2 = new File(dir, "xxx.txt"); + Path x2 = dir.resolve("xxx.txt"); FS.touch(x2); - File dir2 = new File(dir, "yyy"); + Path dir2 = dir.resolve("yyy"); FS.ensureDirExists(dir2); - File y1 = new File(dir2, "ttt.yyy"); + Path y1 = dir2.resolve("ttt.yyy"); FS.touch(y1); - File y2 = new File(dir2, "yyy.txt"); + Path y2 = dir2.resolve("yyy.txt"); FS.touch(y2); BlockingQueue queue = new LinkedBlockingQueue<>(); //only scan the *.txt files for changes Scanner scanner = new Scanner(); - IncludeExcludeSet pattern = scanner.addDirectory(root.toPath()); - pattern.exclude(root.toPath().getFileSystem().getPathMatcher("glob:**/*.foo")); - pattern.exclude(root.toPath().getFileSystem().getPathMatcher("glob:**/ttt.xxx")); + IncludeExcludeSet pattern = scanner.addDirectory(root); + pattern.exclude(root.getFileSystem().getPathMatcher("glob:**/*.foo")); + pattern.exclude(root.getFileSystem().getPathMatcher("glob:**/ttt.xxx")); scanner.setScanInterval(0); scanner.setScanDepth(2); //should never see any files from subdir yyy scanner.setReportDirs(false); @@ -411,13 +407,14 @@ public void testSizeChange() throws Exception assertEquals(Notification.ADDED, event._notification); // Create a new file by writing to it. - long now = Instant.now().toEpochMilli(); - File file = new File(_directory.toFile(), "st"); - try (OutputStream out = new FileOutputStream(file, true)) + FileTime now = FileTime.from(Instant.now()); + Path file = _directory.resolve("st"); + try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { out.write('x'); out.flush(); - file.setLastModified(now); + FS.touch(file); + Files.setLastModifiedTime(file, now); // Not stable yet so no notification. _scanner.scan(); @@ -427,7 +424,7 @@ public void testSizeChange() throws Exception // Modify size only out.write('x'); out.flush(); - file.setLastModified(now); + Files.setLastModifiedTime(file, now); // Still not stable yet so no notification. _scanner.scan(); @@ -444,7 +441,7 @@ public void testSizeChange() throws Exception // Modify size only out.write('x'); out.flush(); - file.setLastModified(now); + Files.setLastModifiedTime(file, now); // Still not stable yet so no notification. _scanner.scan(); @@ -468,10 +465,7 @@ private void delete(String string) throws IOException private void touch(String string) throws IOException { - File file = new File(_directory.toFile(), string); - if (file.exists()) - file.setLastModified(Instant.now().toEpochMilli()); - else - file.createNewFile(); + Path file = _directory.resolve(string); + FS.touch(file); } } From d5def4cd3e163dfaee686b842e914dbfce1cc1c7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 16 Jan 2025 14:03:15 -0600 Subject: [PATCH 025/104] Introduce ChangeSetListener in Scanner --- .../java/org/eclipse/jetty/util/Scanner.java | 88 +++++++++++++------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java index 66134183b066..86cdc727c30e 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java @@ -84,7 +84,7 @@ private enum Status ADDED, CHANGED, REMOVED, STABLE } - enum Notification + public enum Notification { ADDED, CHANGED, REMOVED } @@ -339,6 +339,14 @@ default void pathsChanged(Set paths) throws Exception void filesChanged(Set filenames) throws Exception; } + /** + * Listener that notifies of the complete path change set. + */ + public interface ChangeSetListener extends Listener + { + void pathsChanged(Map changeSet); + } + /** * Listener that notifies when a scan has started and when it has ended. */ @@ -476,14 +484,12 @@ public IncludeExcludeSet addDirectory(Path p) } } - /** * Apply a filter to files found in the scan directory. * Only files matching the filter will be reported as added/changed/removed. * * @param filter the filename filter to use */ - @Deprecated public void setFilenameFilter(FilenameFilter filter) { _filter = filter; @@ -494,7 +500,6 @@ public void setFilenameFilter(FilenameFilter filter) * * @return the filename filter */ - @Deprecated public FilenameFilter getFilenameFilter() { return _filter; @@ -859,7 +864,7 @@ else if (previous._status == Status.CHANGED) if (LOG.isDebugEnabled()) LOG.debug("scanned {}", _scannables.keySet()); - //Call the DiscreteListeners + // Call the DiscreteListeners for (Map.Entry entry : changes.entrySet()) { switch (entry.getValue()) @@ -878,7 +883,10 @@ else if (previous._status == Status.CHANGED) break; } } - //Call the BulkListeners + + reportChangeSet(changes); + + // Call the BulkListeners reportBulkChanges(changes.keySet()); } @@ -894,16 +902,19 @@ private void warn(Object listener, Path path, Throwable th) */ private void reportAddition(Path path) { - for (Listener l : _listeners) + if (path == null) + return; + + for (Listener listener : _listeners) { try { - if (l instanceof DiscreteListener) - ((DiscreteListener)l).pathAdded(path); + if (listener instanceof DiscreteListener discreteListener) + discreteListener.pathAdded(path); } catch (Throwable e) { - warn(l, path, e); + warn(listener, path, e); } } } @@ -915,16 +926,19 @@ private void reportAddition(Path path) */ private void reportRemoval(Path path) { - for (Object l : _listeners) + if (path == null) + return; + + for (Listener listener : _listeners) { try { - if (l instanceof DiscreteListener) - ((DiscreteListener)l).pathRemoved(path); + if (listener instanceof DiscreteListener discreteListener) + discreteListener.pathRemoved(path); } catch (Throwable e) { - warn(l, path, e); + warn(listener, path, e); } } } @@ -939,16 +953,40 @@ private void reportChange(Path path) if (path == null) return; + for (Listener listener : _listeners) + { + try + { + if (listener instanceof DiscreteListener discreteListener) + discreteListener.pathChanged(path); + } + catch (Throwable e) + { + warn(listener, path, e); + } + } + } + + /** + * Report set of changes to the ChangeSetListener + * + * @param changes the changes that occurred. + */ + private void reportChangeSet(Map changes) + { + if (changes == null || changes.isEmpty()) + return; + for (Listener l : _listeners) { try { - if (l instanceof DiscreteListener) - ((DiscreteListener)l).pathChanged(path); + if (l instanceof ChangeSetListener changeSetListener) + changeSetListener.pathsChanged(changes); } catch (Throwable e) { - warn(l, path, e); + LOG.warn("{} failed on '{}'", l, changes, e); } } } @@ -963,16 +1001,16 @@ private void reportBulkChanges(Set paths) if (paths == null || paths.isEmpty()) return; - for (Listener l : _listeners) + for (Listener listener : _listeners) { try { - if (l instanceof BulkListener) - ((BulkListener)l).pathsChanged(paths); + if (listener instanceof BulkListener bulkListener) + bulkListener.pathsChanged(paths); } catch (Throwable e) { - LOG.warn("{} failed on '{}'", l, paths, e); + LOG.warn("{} failed on '{}'", listener, paths, e); } } } @@ -988,8 +1026,8 @@ private void reportScanStart(int cycle) { try { - if (listener instanceof ScanCycleListener) - ((ScanCycleListener)listener).scanStarted(cycle); + if (listener instanceof ScanCycleListener scanCycleListener) + scanCycleListener.scanStarted(cycle); } catch (Exception e) { @@ -1009,8 +1047,8 @@ private void reportScanEnd(int cycle) { try { - if (listener instanceof ScanCycleListener) - ((ScanCycleListener)listener).scanEnded(cycle); + if (listener instanceof ScanCycleListener scanCycleListener) + scanCycleListener.scanEnded(cycle); } catch (Exception e) From 54960bda679a85070864d3ecd0b6eeb396ac119e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 16 Jan 2025 14:18:16 -0600 Subject: [PATCH 026/104] Changes from review --- .../jetty/deploy/DeploymentManager.java | 1 - .../deploy/providers/ContextProvider.java | 94 ++++---- .../deploy/providers/ScanningAppProvider.java | 133 +++++------ .../eclipse/jetty/deploy/providers/Unit.java | 129 +++++++++++ .../providers/internal/DeploymentUnits.java | 213 ++++++------------ .../deploy/providers/ContextProviderTest.java | 31 ++- .../internal/DeploymentUnitsTest.java | 96 ++++++-- .../jetty/ee11/test/DeploymentErrorTest.java | 2 +- 8 files changed, 413 insertions(+), 286 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 1b65b486f09d..c630e7444638 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -401,7 +401,6 @@ public List getAppsWithSameContext(App app) if (entry.app.equals(app)) { // Its the input app. skip it. - // TODO: is this filter needed? continue; } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 30fe3cc757ea..af0da94a0fa3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -31,7 +31,6 @@ import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -398,9 +397,9 @@ public EnvironmentConfig configureEnvironment(String name) * *

        {@code
              * ContextProvider provider = new ContextProvider();
        -     * ContextProvider.EnvironmentConfig envbuilder = provider.configureEnvironment("ee10");
        -     * envbuilder.setExtractWars(true);
        -     * envbuilder.setParentLoaderPriority(false);
        +     * ContextProvider.EnvironmentConfig env10config = provider.configureEnvironment("ee10");
        +     * env10config.setExtractWars(true);
        +     * env10config.setParentLoaderPriority(false);
              * }
        * * @see #configureEnvironment(String) instead @@ -491,45 +490,55 @@ public void initializeDefaults(Object context) protected ClassLoader findCoreContextClassLoader(Path path) throws IOException { Path webapps = path.getParent(); - String basename = FileID.getBasename(path); + String basename = FileID.getBasename(path).toLowerCase(); // use system Locale as we are dealing with system FS. List urls = new ArrayList<>(); - // Is there a matching jar file? - // TODO: both files can be there depending on FileSystem, is this still sane? - // TODO: what about other capitalization? eg: ".Jar" ? - Path contextJar = webapps.resolve(basename + ".jar"); - if (!Files.exists(contextJar)) - contextJar = webapps.resolve(basename + ".JAR"); - if (Files.exists(contextJar)) - urls.add(contextJar.toUri().toURL()); - - // Is there a matching lib directory? - Path libDir = webapps.resolve(basename + ".d/lib"); - if (Files.exists(libDir) && Files.isDirectory(libDir)) + try (Stream listingStream = Files.list(webapps)) { - try (Stream paths = Files.list(libDir)) + // Collect all paths that match "webapps/*" + List basenamePaths = listingStream + .filter((p) -> + { + String filename = p.getFileName().toString().toLowerCase(); // use system Locale + return filename.startsWith(basename); + }) + .toList(); + + // Walk basename paths + for (Path p : basenamePaths) { - paths.filter(FileID::isJavaArchive) - .map(Path::toUri) - .forEach(uri -> + if (Files.isDirectory(p)) + { + if (p.getFileName().toString().toLowerCase().endsWith(".d")) { - try - { - urls.add(uri.toURL()); - } - catch (Exception e) + Path libDir = p.resolve("lib"); + // we have a possible lib directory. + if (Files.isDirectory(libDir)) { - throw new RuntimeException(e); + try (Stream libPaths = Files.list(libDir)) + { + List javaArchives = libPaths.filter(FileID::isJavaArchive).toList(); + + for (Path lib : javaArchives) + { + urls.add(lib.toUri().toURL()); + } + } } - }); + + Path classesDir = p.resolve(basename + "classes"); + if (Files.isDirectory(libDir)) + urls.add(classesDir.toUri().toURL()); + } + } + + if (FileID.isJavaArchive(p)) + { + urls.add(p.toUri().toURL()); + } } } - // Is there a matching lib directory? - Path classesDir = webapps.resolve(basename + ".d/classes"); - if (Files.exists(classesDir) && Files.isDirectory(libDir)) - urls.add(classesDir.toUri().toURL()); - if (LOG.isDebugEnabled()) LOG.debug("Core classloader for {}", urls); @@ -637,14 +646,14 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) * Apply the main deployable heuristics referenced in the main javadoc * for this class. * - * @param paths the set of paths that represent a single unit of deployment. - * @return the main deployable. + * @param unit the unit of deployment to interrogate. + * @return the main deployable path */ @Override - protected Path getMainDeploymentPath(Set paths) + protected Path getMainDeploymentPath(Unit unit) { // XML always win. - List xmls = paths.stream() + List xmls = unit.getPaths().keySet().stream() .filter(FileID::isXml) .toList(); if (xmls.size() == 1) @@ -652,7 +661,7 @@ protected Path getMainDeploymentPath(Set paths) else if (xmls.size() > 1) throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); // WAR files are next. - List wars = paths.stream() + List wars = unit.getPaths().keySet().stream() .filter(FileID::isWebArchive) .toList(); if (wars.size() == 1) @@ -660,7 +669,7 @@ else if (xmls.size() > 1) else if (wars.size() > 1) throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); // Directories next. - List dirs = paths.stream() + List dirs = unit.getPaths().keySet().stream() .filter(Files::isDirectory) .toList(); if (dirs.size() == 1) @@ -668,7 +677,7 @@ else if (wars.size() > 1) if (dirs.size() > 1) throw new IllegalStateException("More than 1 Directory for deployable " + asStringList(dirs)); - throw new IllegalStateException("Unable to determine main deployable " + asStringList(paths)); + throw new IllegalStateException("Unable to determine main deployable for " + unit); } /** @@ -700,7 +709,6 @@ private Attributes initAttributes(Environment environment, App app) throws IOExc environment.getAttributeNameSet().forEach((key) -> attributes.setAttribute(key, environment.getAttribute(key))); - // TODO: double check if an empty environment name makes sense. Will the default environment name? String env = app.getEnvironmentName(); if (StringUtil.isNotBlank(env)) @@ -748,9 +756,7 @@ private Properties loadEnvironmentProperties(Path directory, String env) throws String name = p.getName(0).toString(); if (!name.endsWith(".properties")) return false; - if (!name.startsWith(env)) - return false; - return true; + return name.startsWith(env); }).sorted().toList(); } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 6e7d730425fc..366c09dd3027 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -19,11 +19,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; @@ -45,36 +43,20 @@ import org.slf4j.LoggerFactory; @ManagedObject("Abstract Provider for loading webapps") -public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider, DeploymentUnits.Events +public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider, DeploymentUnits.Listener { private static final Logger LOG = LoggerFactory.getLogger(ScanningAppProvider.class); private DeploymentUnits _units = new DeploymentUnits(); - private Map _appMap = new HashMap<>(); - private DeploymentManager _deploymentManager; private FilenameFilter _filenameFilter; + private Comparator _unitComparator = Comparator.comparing(Unit::getBaseName); private final List _monitored = new CopyOnWriteArrayList<>(); private int _scanInterval = 10; private Scanner _scanner; private boolean _useRealPaths; private boolean _deferInitialScan = false; - private final Scanner.BulkListener _scannerBulkListener = new Scanner.BulkListener() - { - @Override - public void pathsChanged(Set paths) - { - _units.processChanges(paths, ScanningAppProvider.this); - } - - @Override - public void filesChanged(Set filenames) - { - // ignore, as we are using the pathsChanged() technique only. - } - }; - protected ScanningAppProvider() { this(null); @@ -83,7 +65,8 @@ protected ScanningAppProvider() protected ScanningAppProvider(FilenameFilter filter) { _filenameFilter = filter; - installBean(_appMap); + _units.setListener(this); + installBean(_units); } /** @@ -109,12 +92,25 @@ protected void setFilenameFilter(FilenameFilter filter) _filenameFilter = filter; } + public Comparator getUnitComparator() + { + return _unitComparator; + } + + public void setUnitComparator(Comparator comparator) + { + this._unitComparator = comparator; + } + /** * @return The index of currently deployed applications. */ - protected Map getDeployedApps() + protected Collection getDeployedApps() { - return _appMap; + return _units.getUnits() + .stream() + .map(Unit::getApp) + .collect(Collectors.toUnmodifiableSet()); } /** @@ -173,7 +169,7 @@ protected void doStart() throws Exception _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.setScanDepth(1); //consider direct dir children of monitored dir - _scanner.addListener(_scannerBulkListener); + _scanner.addListener(_units); _scanner.setReportExistingFilesOnStartup(true); _scanner.setAutoStartScanning(!_deferInitialScan); addBean(_scanner); @@ -208,7 +204,7 @@ protected void doStop() throws Exception if (_scanner != null) { removeBean(_scanner); - _scanner.removeListener(_scannerBulkListener); + _scanner.removeListener(_units); _scanner = null; } } @@ -219,56 +215,61 @@ protected boolean exists(String path) } /** - * Given a set of Paths that belong to the same unit of deployment, - * pick the main Path that is actually the point of deployment. + * Given a deployment unit, pick the main Path that is actually the point of deployment. * - * @param paths the set of paths that represent a single unit of deployment. (all paths in {@link Set} are of the same basename) + * @param unit a single unit of deployment. * @return the specific path that represents the main point of deployment (eg: xml if it exists) */ - protected abstract Path getMainDeploymentPath(Set paths); + protected abstract Path getMainDeploymentPath(Unit unit); - public void unitAdded(String basename, Set paths) + @Override + public void unitsChanged(List units) { - Path mainDeploymentPath = getMainDeploymentPath(paths); - App app = this.createApp(mainDeploymentPath); + units.sort(getUnitComparator()); - if (LOG.isDebugEnabled()) - LOG.debug("unitAdded {} -> {}: {}", basename, paths, app); - - if (app != null) + for (Unit unit : units) { - _appMap.put(basename, app); - _deploymentManager.addApp(app); - } - } + switch (unit.getState()) + { + case ADDED -> + { + Path mainDeploymentPath = getMainDeploymentPath(unit); + App app = this.createApp(mainDeploymentPath); + unit.setApp(app); + if (LOG.isDebugEnabled()) + LOG.debug("Unit ADDED: {}", unit); + + if (app != null) + _deploymentManager.addApp(app); + } + case CHANGED -> + { + App oldApp = unit.removeApp(); + if (oldApp != null) + _deploymentManager.removeApp(oldApp); - @Override - public void unitChanged(String basename, Set paths) - { - App oldApp = _appMap.remove(basename); - if (oldApp != null) - _deploymentManager.removeApp(oldApp); + Path mainDeploymentPath = getMainDeploymentPath(unit); + App app = this.createApp(mainDeploymentPath); + unit.setApp(app); - Path mainDeploymentPath = getMainDeploymentPath(paths); - App app = ScanningAppProvider.this.createApp(mainDeploymentPath); - if (LOG.isDebugEnabled()) - LOG.debug("unitChanged {} -> {}: {}", basename, paths, app); - if (app != null) - { - _appMap.put(basename, app); - _deploymentManager.addApp(app); - } - } + if (LOG.isDebugEnabled()) + LOG.debug("Unit CHANGED: {}", unit); - @Override - public void unitRemoved(String basename) - { - App app = _appMap.remove(basename); - if (LOG.isDebugEnabled()) - LOG.debug("unitRemoved {}: {}", basename, app); - if (app != null) - { - _deploymentManager.removeApp(app); + if (app != null) + _deploymentManager.addApp(app); + } + case REMOVED -> + { + App app = unit.removeApp(); + if (LOG.isDebugEnabled()) + LOG.debug("Unit REMOVED: {}", unit); + if (app != null) + _deploymentManager.removeApp(app); + } + } + + // we are done processing unit, reset its state + unit.setUnchanged(); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java new file mode 100644 index 000000000000..d05783719af7 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java @@ -0,0 +1,129 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.jetty.deploy.App; + +/** + * A Unit of deployment, a basename and all the associated + * paths that belong to that basename, along with state. + */ +public class Unit +{ + public enum State + { + UNCHANGED, + ADDED, + CHANGED, + REMOVED + } + + private final String baseName; + private final Map paths = new HashMap<>(); + private App app; + + public Unit(String basename) + { + this.baseName = basename; + } + + public String getBaseName() + { + return baseName; + } + + public State getState() + { + // Calculate state of unit from Path states. + Unit.State state = State.UNCHANGED; + for (Unit.State pathState : paths.values()) + { + switch (pathState) + { + case ADDED -> + { + if (state == State.UNCHANGED || state == State.REMOVED) + state = State.ADDED; + } + case CHANGED -> + { + state = State.CHANGED; + } + case REMOVED -> + { + if (state == State.UNCHANGED) + state = State.REMOVED; + } + } + } + return state; + } + + public Map getPaths() + { + return paths; + } + + public void putPath(Path path, State state) + { + this.paths.put(path, state); + } + + public App getApp() + { + return app; + } + + public void setApp(App app) + { + this.app = app; + } + + public App removeApp() + { + App oldApp = this.app; + this.app = null; + return oldApp; + } + + public void setUnchanged() + { + paths.replaceAll((p, v) -> State.UNCHANGED); + } + + @Override + public String toString() + { + StringBuilder str = new StringBuilder(); + str.append("Unit[").append(baseName); + str.append("|").append(getState()); + str.append(", paths="); + str.append(paths.entrySet().stream() + .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ", "[", "]")) + ); + str.append(", app="); + if (app == null) + str.append(""); + else + str.append(app); + str.append("]"); + return str.toString(); + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java index 4fcd8a3a5d26..571c4f58a0ca 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java @@ -13,194 +13,117 @@ package org.eclipse.jetty.deploy.providers.internal; -import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collection; +import java.util.EventListener; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import org.eclipse.jetty.deploy.providers.Unit; import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.resource.PathCollators; +import org.eclipse.jetty.util.Scanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The role of DeploymentUnits is to keep track groups of files (and directories) that represent - * a single unit of deployment, identifying additions/changes/deletions of files/dirs properly. + * The role of DeploymentUnits is to keep track of {@link Unit} instances, and + * process changes coming from {@link java.util.Scanner} into a set of units + * that will be processed via a listener. */ -public class DeploymentUnits +public class DeploymentUnits implements Scanner.ChangeSetListener { - enum EventType + private static final Logger LOG = LoggerFactory.getLogger(DeploymentUnits.class); + + private Map units = new HashMap<>(); + + private Listener listener; + + public Listener getListener() { - UNCHANGED, - CHANGED, - ADDED, - REMOVED + return listener; } - private static final Logger LOG = LoggerFactory.getLogger(DeploymentUnits.class); - - /** - * The units that are being tracked by this component. - * Key is the basename (case-insensitive) and the value are all the paths known for that unit. - */ - private HashMap> units = new HashMap<>(); + public void setListener(Listener listener) + { + this.listener = listener; + } - public Set getUnit(String basename) + public Unit getUnit(String basename) { return units.get(basename); } - public synchronized void processChanges(Set rawPaths, Events events) + public Collection getUnits() + { + return units.values(); + } + + @Override + public void pathsChanged(Map changeSet) { + Objects.requireNonNull(changeSet); if (LOG.isDebugEnabled()) { - LOG.debug("processChanges: rawPaths: {}", rawPaths.stream() - .sorted(PathCollators.byName(true)) - .map(Path::toString) - .collect(Collectors.joining(", ", "[", "]")) + LOG.debug("processChanges: changeSet: {}", + changeSet.entrySet() + .stream() + .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ", "[", "]")) ); } - Map basenameEvents = new HashMap<>(); - // Figure out what changed, and how. - for (Path path : rawPaths) + Set changedBaseNames = new HashSet<>(); + + for (Map.Entry entry : changeSet.entrySet()) { + Path path = entry.getKey(); + Unit.State state = toState(entry.getValue()); + // to lower-case uses system Locale, as we are working with the system FS. String basename = FileID.getBasename(path).toLowerCase(); - basenameEvents.putIfAbsent(basename, EventType.UNCHANGED); + changedBaseNames.add(basename); - if (Files.exists(path)) - { - // A path that exists, either added, or changed. - if (!units.containsKey(basename)) - { - if (LOG.isDebugEnabled()) - LOG.debug("basename: {} - new - {}", basename, path); - // this is a new unit - Set unitPaths = units.computeIfAbsent(basename, k -> new HashSet<>()); - unitPaths.add(path); - basenameEvents.compute(basename, - (b, v) -> - { - if (v == null) - return EventType.ADDED; - return switch (v) - { - case ADDED, CHANGED: - { - yield v; // keep value - } - case UNCHANGED, REMOVED: - { - yield EventType.ADDED; - } - }; - }); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("basename: {} - existing - {}", basename, path); - // this is an existing unit - Set unitPaths = units.computeIfAbsent(basename, k -> new HashSet<>()); - unitPaths.add(path); - units.put(basename, unitPaths); - basenameEvents.compute(basename, - (b, v) -> - { - if (v == null) - return EventType.CHANGED; - return switch (v) - { - case ADDED, CHANGED -> - { - yield v; // keep value - } - case UNCHANGED, REMOVED -> - { - yield EventType.CHANGED; - } - }; - }); - } - } - else - { - // A path was removed - - // Only care about paths that belong to an existing unit - if (units.containsKey(basename)) - { - // this is an existing unit - Set unitPaths = units.get(basename); - unitPaths.remove(path); - if (unitPaths.isEmpty()) - { - if (LOG.isDebugEnabled()) - LOG.debug("basename: {} - removed - {}", basename, path); - // remove unit - basenameEvents.put(basename, EventType.REMOVED); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("basename: {} - changed - {}", basename, path); - // unit has changed. - basenameEvents.put(basename, EventType.CHANGED); - } - } - } + Unit unit = units.computeIfAbsent(basename, Unit::new); + unit.putPath(path, state); } - // Notify of changes, alphabetically. - List sortedBasenames = basenameEvents.keySet() - .stream() - .distinct() - .sorted() - .toList(); + Listener listener = getListener(); + if (listener != null) + { + List changedUnits = changedBaseNames.stream() + .map(name -> units.get(name)) + .collect(Collectors.toList()); + + listener.unitsChanged(changedUnits); + } + } - for (String basename : sortedBasenames) + private Unit.State toState(Scanner.Notification notification) + { + return switch (notification) { - Set paths = getUnit(basename); - if (LOG.isDebugEnabled()) + case ADDED -> { - LOG.debug("reporting unit{}: basename: {} - paths: {}", - basenameEvents.get(basename), - basename, - paths.stream() - .sorted(PathCollators.byName(true)) - .map(Path::toString) - .collect(Collectors.joining(", ", "[", "]")) - ); + yield Unit.State.ADDED; } - switch (basenameEvents.get(basename)) + case CHANGED -> { - case ADDED -> - { - events.unitAdded(basename, paths); - } - case CHANGED -> - { - events.unitChanged(basename, getUnit(basename)); - } - case REMOVED -> - { - events.unitRemoved(basename); - } + yield Unit.State.CHANGED; } - } + case REMOVED -> + { + yield Unit.State.REMOVED; + } + }; } - public interface Events + public interface Listener extends EventListener { - void unitAdded(String basename, Set paths); - - void unitChanged(String basename, Set paths); - - void unitRemoved(String basename); + void unitsChanged(List units); } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java index ab2e73d048ea..ea0e0b20e09a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Set; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -44,8 +43,11 @@ public void testMainDeploymentPathOnlyXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); + Unit unit = new Unit("bar"); + unit.putPath(xml, Unit.State.UNCHANGED); + ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(Set.of(xml)); + Path main = provider.getMainDeploymentPath(unit); assertThat("main path", main, equalTo(xml)); } @@ -64,8 +66,12 @@ public void testMainDeploymentPathXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); + Unit unit = new Unit("bar"); + unit.putPath(xml, Unit.State.UNCHANGED); + unit.putPath(war, Unit.State.UNCHANGED); + ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(Set.of(xml, war)); + Path main = provider.getMainDeploymentPath(unit); assertThat("main path", main, equalTo(xml)); } @@ -84,8 +90,12 @@ public void testMainDeploymentPathDirAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); + Unit unit = new Unit("bar"); + unit.putPath(appDir, Unit.State.UNCHANGED); + unit.putPath(war, Unit.State.UNCHANGED); + ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(Set.of(appDir, war)); + Path main = provider.getMainDeploymentPath(unit); assertThat("main path", main, equalTo(war)); } @@ -104,8 +114,12 @@ public void testMainDeploymentPathDirAndXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); + Unit unit = new Unit("bar"); + unit.putPath(appDir, Unit.State.UNCHANGED); + unit.putPath(xml, Unit.State.UNCHANGED); + ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(Set.of(appDir, xml)); + Path main = provider.getMainDeploymentPath(unit); assertThat("main path", main, equalTo(xml)); } @@ -126,8 +140,13 @@ public void testMainDeploymentPathDirAndXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); + Unit unit = new Unit("bar"); + unit.putPath(appDir, Unit.State.UNCHANGED); + unit.putPath(xml, Unit.State.UNCHANGED); + unit.putPath(war, Unit.State.UNCHANGED); + ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(Set.of(appDir, xml, war)); + Path main = provider.getMainDeploymentPath(unit); assertThat("main path", main, equalTo(xml)); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java index 1fd6c5c7b3f3..e0b97803b451 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java @@ -16,25 +16,84 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Set; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.jetty.deploy.providers.Unit; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.Scanner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.fail; @ExtendWith(WorkDirExtension.class) public class DeploymentUnitsTest { public WorkDir workDir; + /* + * foo.war + * foo.xml + * bar.war + * bar.xml + * + * Discrete Listener (get 1 path) - 4 add events. (no sorting) + * Bulk Listener (get 4 paths) - 2 unit add events. (bar before foo) + * + * Change bar.xml + * Discrete Listener - 1 change event + * Bulk Listener - 1 change event + * + * Change bar.war + * Discrete Listener - 0 change events + * Bulk Listener - 1 change event (deploys bar.xml) + * + * --- + * ChangeSet #1 + * bar.xml (added) + * bar/ (added) + * foo.xml (added) + * foo.properties (added) + * ee8.xml // (ENV) + * ee8.properties + * ee8-blah.xml + * + * eventChangedUnits (List) + * Unit[0] - "bar"|ADDED - (bar.xml|ADDED, bar/|ADDED) + * Unit[1] - "foo"|ADDED - (foo.xml|ADDED, foo.properties|ADDED) + * + * ChangeSet #2 + * bar.properties (added) + * bar.xml (changed) + * bar/ (removed) + * foo.properties (changed) + * + * eventChangedUnits (List) + * Unit[0] - "bar"|CHANGED (bar.xml|CHANGED, bar.properties|CHANGED) + * Unit[1] - "foo"|CHANGED (foo.xml|UNCHANGED, foo.properties|CHANGED) + * + * ChangeSet #3 + * foo.properties (removed) + * + * eventChangedUnits (List) + * Unit[0] - "foo"|CHANGED (foo.xml|UNCHANGED) + * + * ChangeSet #4 + * foo.xml (removed) + * + * eventChangedUnits (List) + * Unit[0] - "foo"|REMOVED () + * + * + * + */ + + @Test public void testNewXmlOnly() throws IOException { @@ -43,30 +102,20 @@ public void testNewXmlOnly() throws IOException Files.writeString(xml, "XML for bar", UTF_8); DeploymentUnits units = new DeploymentUnits(); - units.processChanges(Set.of(xml), new DeploymentUnits.Events() + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(1)); - assertThat("paths", paths, contains(xml)); - } - - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } - - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), contains(xml)); }); + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + units.pathsChanged(changeSet); } + /* @Test public void testXmlThenRemoved() throws IOException { @@ -278,4 +327,5 @@ public void unitRemoved(String basename) } }); } + */ } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index 9a6e3ab00a7d..60ab0fa816f9 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -107,7 +107,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); ContextProvider appProvider = new ContextProvider(); - appProvider.setEnvironmentName("ee11"); + Environment.ensure("ee11"); appProvider.setScanInterval(1); appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); From c16fdf7b200a19194f1910c934447f1e421609bb Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 16 Jan 2025 15:39:10 -0600 Subject: [PATCH 027/104] Changes from review --- .../deploy/providers/ScanningAppProvider.java | 8 +- .../eclipse/jetty/deploy/providers/Unit.java | 39 ++- .../providers/internal/DeploymentUnits.java | 3 + .../internal/DeploymentUnitsTest.java | 328 +++++++----------- 4 files changed, 167 insertions(+), 211 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 366c09dd3027..10323b952ef4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -227,8 +227,14 @@ public void unitsChanged(List units) { units.sort(getUnitComparator()); + if (LOG.isDebugEnabled()) + LOG.debug("unitsChanged: {}", units); + for (Unit unit : units) { + if (LOG.isDebugEnabled()) + LOG.debug("unit changed: {}", unit); + switch (unit.getState()) { case ADDED -> @@ -269,7 +275,7 @@ public void unitsChanged(List units) } // we are done processing unit, reset its state - unit.setUnchanged(); + unit.resetStates(); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java index d05783719af7..79d09891d530 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -50,29 +51,41 @@ public String getBaseName() public State getState() { + if (paths.isEmpty()) + return State.REMOVED; + // Calculate state of unit from Path states. - Unit.State state = State.UNCHANGED; + Unit.State ret = null; for (Unit.State pathState : paths.values()) { switch (pathState) { + case UNCHANGED -> + { + if (ret == null) + ret = State.UNCHANGED; + } case ADDED -> { - if (state == State.UNCHANGED || state == State.REMOVED) - state = State.ADDED; + if (ret == null) + ret = State.ADDED; + else if (ret == State.UNCHANGED || ret == State.REMOVED) + ret = State.ADDED; } case CHANGED -> { - state = State.CHANGED; + ret = State.CHANGED; } case REMOVED -> { - if (state == State.UNCHANGED) - state = State.REMOVED; + if (ret == null) + ret = State.REMOVED; + else if (ret != State.REMOVED) + ret = State.CHANGED; } } } - return state; + return ret != null ? ret : State.UNCHANGED; } public Map getPaths() @@ -102,8 +115,18 @@ public App removeApp() return oldApp; } - public void setUnchanged() + public void resetStates() { + // Drop paths that were removed. + List removedPaths = paths.entrySet() + .stream().filter(e -> e.getValue() == State.REMOVED) + .map(Map.Entry::getKey) + .toList(); + for (Path removedPath : removedPaths) + { + paths.remove(removedPath); + } + // Set all remaining path states to UNCHANGED paths.replaceAll((p, v) -> State.UNCHANGED); } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java index 571c4f58a0ca..004d1db4a425 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java @@ -39,6 +39,9 @@ public class DeploymentUnits implements Scanner.ChangeSetListener { private static final Logger LOG = LoggerFactory.getLogger(DeploymentUnits.class); + /** + * Basename of unit, to the Unit instance. + */ private Map units = new HashMap<>(); private Listener listener; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java index e0b97803b451..f7792ab139f1 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java @@ -29,6 +29,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) @@ -36,64 +37,6 @@ public class DeploymentUnitsTest { public WorkDir workDir; - /* - * foo.war - * foo.xml - * bar.war - * bar.xml - * - * Discrete Listener (get 1 path) - 4 add events. (no sorting) - * Bulk Listener (get 4 paths) - 2 unit add events. (bar before foo) - * - * Change bar.xml - * Discrete Listener - 1 change event - * Bulk Listener - 1 change event - * - * Change bar.war - * Discrete Listener - 0 change events - * Bulk Listener - 1 change event (deploys bar.xml) - * - * --- - * ChangeSet #1 - * bar.xml (added) - * bar/ (added) - * foo.xml (added) - * foo.properties (added) - * ee8.xml // (ENV) - * ee8.properties - * ee8-blah.xml - * - * eventChangedUnits (List) - * Unit[0] - "bar"|ADDED - (bar.xml|ADDED, bar/|ADDED) - * Unit[1] - "foo"|ADDED - (foo.xml|ADDED, foo.properties|ADDED) - * - * ChangeSet #2 - * bar.properties (added) - * bar.xml (changed) - * bar/ (removed) - * foo.properties (changed) - * - * eventChangedUnits (List) - * Unit[0] - "bar"|CHANGED (bar.xml|CHANGED, bar.properties|CHANGED) - * Unit[1] - "foo"|CHANGED (foo.xml|UNCHANGED, foo.properties|CHANGED) - * - * ChangeSet #3 - * foo.properties (removed) - * - * eventChangedUnits (List) - * Unit[0] - "foo"|CHANGED (foo.xml|UNCHANGED) - * - * ChangeSet #4 - * foo.xml (removed) - * - * eventChangedUnits (List) - * Unit[0] - "foo"|REMOVED () - * - * - * - */ - - @Test public void testNewXmlOnly() throws IOException { @@ -113,64 +56,48 @@ public void testNewXmlOnly() throws IOException Map changeSet = new HashMap<>(); changeSet.put(xml, Scanner.Notification.ADDED); units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); } - /* @Test public void testXmlThenRemoved() throws IOException { Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); + Path xml = dir.resolve("foo.xml"); + Files.writeString(xml, "XML for foo", UTF_8); DeploymentUnits units = new DeploymentUnits(); // Initial deployment. - units.processChanges(Set.of(xml), new DeploymentUnits.Events() + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(1)); - assertThat("paths", paths, contains(xml)); - } - - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } - - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("foo")); + assertThat("changedUnit[foo].state", unit.getState(), is(Unit.State.ADDED)); + assertThat("changedUnit[foo].paths", unit.getPaths().keySet(), contains(xml)); + assertThat("changedUnit[foo].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); // Removed deployment. Files.deleteIfExists(xml); - units.processChanges(Set.of(xml), new DeploymentUnits.Events() + changeSet.clear(); + changeSet.put(xml, Scanner.Notification.REMOVED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - fail("Should not cause a unitAdded"); - } - - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } - - @Override - public void unitRemoved(String basename) - { - assertThat("basename", basename, is("bar")); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("foo")); + assertThat("changedUnit[foo].state", unit.getState(), is(Unit.State.REMOVED)); + assertThat("changedUnit[foo].paths", unit.getPaths().keySet(), contains(xml)); + assertThat("changedUnit[foo].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.REMOVED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); } @Test @@ -183,28 +110,23 @@ public void testNewXmlAndWarOnly() throws IOException Files.writeString(war, "WAR for bar", UTF_8); DeploymentUnits units = new DeploymentUnits(); - units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() - { - @Override - public void unitAdded(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(2)); - assertThat("paths", paths, containsInAnyOrder(xml, war)); - } - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } - - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + units.setListener(changedUnits -> + { + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); } @Test @@ -218,53 +140,38 @@ public void testXmlAndWarWithXmlUpdate() throws IOException DeploymentUnits units = new DeploymentUnits(); - // Trigger first set of changes (both files) - units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(2)); - assertThat("paths", paths, containsInAnyOrder(xml, war)); - } - - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } - - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); - // Trigger second set of changes (only xml) - units.processChanges(Set.of(xml), new DeploymentUnits.Events() + // Change/Touch war + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - fail("Should not cause a unitAdded"); - } - - @Override - public void unitChanged(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(2)); - assertThat("paths", paths, containsInAnyOrder(xml, war)); - } - - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.UNCHANGED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.CHANGED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); } @Test @@ -278,54 +185,71 @@ public void testXmlAndWarWithXmlRemoved() throws IOException DeploymentUnits units = new DeploymentUnits(); - // Trigger first set of changes (both files) - units.processChanges(Set.of(xml, war), new DeploymentUnits.Events() + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(2)); - assertThat("paths", paths, containsInAnyOrder(xml, war)); - } - - @Override - public void unitChanged(String basename, Set paths) - { - fail("Should not cause a unitChanged"); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); + }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + // Change/Touch war and xml + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + changeSet.put(xml, Scanner.Notification.CHANGED); + units.setListener(changedUnits -> + { + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.CHANGED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.CHANGED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); - // Trigger second set of changes (war & xml), XML is deleted + // Delete XML Files.deleteIfExists(xml); - units.processChanges(Set.of(war, xml), new DeploymentUnits.Events() + changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.REMOVED); + units.setListener(changedUnits -> { - @Override - public void unitAdded(String basename, Set paths) - { - fail("Should not cause a unitAdded"); - } - - @Override - public void unitChanged(String basename, Set paths) - { - assertThat("basename", basename, is("bar")); - assertThat("paths.size", paths.size(), is(1)); - assertThat("paths", paths, containsInAnyOrder(war)); - } + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.REMOVED)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.UNCHANGED)); + }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); - @Override - public void unitRemoved(String basename) - { - fail("Should not cause a unitRemoved"); - } + // Delete WAR + Files.deleteIfExists(war); + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.REMOVED); + units.setListener(changedUnits -> + { + assertThat("changedUnits.size", changedUnits.size(), is(1)); + Unit unit = changedUnits.get(0); + assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); + assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.REMOVED)); + assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(war)); + assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.REMOVED)); }); + units.pathsChanged(changeSet); + units.getUnits().forEach(Unit::resetStates); } - */ } From bc1b50f7f6d947bd9c4577b22ac18fdbf768b148 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 17 Jan 2025 14:53:14 -0600 Subject: [PATCH 028/104] Adding environment configuration hotreload ability --- .../java/org/eclipse/jetty/deploy/App.java | 10 ++ .../deploy/providers/ContextProvider.java | 96 +++++++++++++------ .../deploy/providers/ScanningAppProvider.java | 7 ++ .../eclipse/jetty/deploy/providers/Unit.java | 60 ++++++++++++ .../providers/internal/DeploymentUnits.java | 37 ++++++- .../ContextProviderRuntimeUpdatesTest.java | 52 ++++++++++ .../src/test/resources/webapps/simple.xml | 3 + 7 files changed, 233 insertions(+), 32 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java index e2f399a646a3..79770e16d93b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java @@ -140,6 +140,16 @@ public Path getPath() return _path; } + /** + * Set the environment name. + * + * @param name the name of the environment. + */ + public void setEnvironmentName(String name) + { + getProperties().put(Deployable.ENVIRONMENT, name); + } + @Override public String toString() { diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index af0da94a0fa3..64bd4118370d 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -27,10 +27,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -147,6 +147,8 @@ public ContextHandler createContextHandler(final App app) throws Exception { Thread.currentThread().setContextClassLoader(environment.getClassLoader()); + app.setEnvironmentName(environment.getName()); + // Create de-aliased file Path path = app.getPath().toRealPath(); if (!Files.exists(path)) @@ -680,6 +682,49 @@ else if (wars.size() > 1) throw new IllegalStateException("Unable to determine main deployable for " + unit); } + @Override + public void unitsChanged(List units) + { + Set envsChanges = units.stream() + .map(Unit::getEnvironmentConfigName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + if (envsChanges.isEmpty()) + { + // normal set of changes, no environment configuration changes + // present in the incoming list of changed units. + super.unitsChanged(units); + } + else + { + // ensure that the units reported to super.unitsChanged + // includes all (known) units that belong to the list of changed environments, + // while filtering out the environment config specific units here. + List unitsWithEnvChanges = new ArrayList<>(units.stream() + .filter(Unit::isDeployable) + .toList()); + + // Add any missing units into change set. + for (String envChange : envsChanges) + { + for (Unit unit : getUnitsForEnvironment(envChange)) + { + if (!unitsWithEnvChanges.contains(unit)) + { + // unit is not part of changeSet but is + // in need of a redeploy due to an environment configuration + // file being changed. + unit.setState(Unit.State.CHANGED); + unitsWithEnvChanges.add(unit); + } + } + } + + super.unitsChanged(unitsWithEnvChanges); + } + } + /** * Get the ClassLoader appropriate for applying Jetty XML. * @@ -750,12 +795,10 @@ private Properties loadEnvironmentProperties(Path directory, String env) throws try (Stream paths = Files.list(directory)) { envPropertyFiles = paths.filter(Files::isRegularFile) - .map(directory::relativize) + .filter(p -> FileID.isExtension(p, "properties")) .filter(p -> { - String name = p.getName(0).toString(); - if (!name.endsWith(".properties")) - return false; + String name = p.getFileName().toString(); return name.startsWith(env); }).sorted().toList(); } @@ -942,7 +985,7 @@ public void setWebInfScanJarPattern(String pattern) } } - public class Filter implements FilenameFilter + public static class Filter implements FilenameFilter { @Override public boolean accept(File dir, String name) @@ -950,35 +993,32 @@ public boolean accept(File dir, String name) if (dir == null || !dir.canRead()) return false; - // Accept XML files and WARs - if (FileID.isXml(name) || FileID.isWebArchive(name)) - return true; - Path path = dir.toPath().resolve(name); - // Ignore any other file that are not directories - if (!Files.isDirectory(path)) - return false; - - // Don't deploy monitored resources - if (getMonitoredResources().stream().map(Resource::getPath).anyMatch(path::equals)) - return false; - - // Ignore hidden directories - if (name.startsWith(".")) + // We don't monitor subdirectories. + if (Files.isDirectory(path)) return false; - String lowerName = name.toLowerCase(Locale.ENGLISH); - - // is it a nominated config directory - if (lowerName.endsWith(".d")) + // Synthetic files (like consoles, printers, serial ports, etc) are ignored. + if (!Files.isRegularFile(path)) return false; - // ignore source control directories - if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) - return false; + try + { + // ignore traditional "hidden" path entries. + if (name.startsWith(".")) + return false; + // ignore path tagged as hidden by FS + if (Files.isHidden(path)) + return false; + } + catch (IOException ignore) + { + // ignore + } - return true; + // The filetypes that are monitored, and we want updates for when they change. + return FileID.isExtension(name, "jar", "war", "xml", "properties"); } } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 10323b952ef4..93a776553029 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -85,6 +85,13 @@ public void setUseRealPaths(boolean useRealPaths) _useRealPaths = useRealPaths; } + protected List getUnitsForEnvironment(String envName) + { + return _units.getUnits().stream() + .filter((unit) -> envName.equals(unit.getEnvironmentName())) + .toList(); + } + protected void setFilenameFilter(FilenameFilter filter) { if (isRunning()) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java index 79d09891d530..c4f0669fac43 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java @@ -17,9 +17,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.util.StringUtil; /** * A Unit of deployment, a basename and all the associated @@ -37,7 +39,14 @@ public enum State private final String baseName; private final Map paths = new HashMap<>(); + /** + * Indicates a synthetic Unit, used to track Environment specific configurations. + * Such as {@code .xml}, {@code .properties}, {@code -.xml}, {@code -.properties}, + * all under a single unit that will not actually be deployed. + */ + private String envConfig; private App app; + private State state = State.UNCHANGED; public Unit(String basename) { @@ -50,6 +59,11 @@ public String getBaseName() } public State getState() + { + return state; + } + + private State calcState() { if (paths.isEmpty()) return State.REMOVED; @@ -96,6 +110,29 @@ public Map getPaths() public void putPath(Path path, State state) { this.paths.put(path, state); + setState(calcState()); + } + + public boolean isDeployable() + { + return StringUtil.isBlank(envConfig); + } + + public String getEnvironmentName() + { + if (app == null) + return null; + return app.getEnvironmentName(); + } + + public String getEnvironmentConfigName() + { + return envConfig; + } + + public void setEnvironmentConfigName(String environmentConfigName) + { + this.envConfig = environmentConfigName; } public App getApp() @@ -117,6 +154,7 @@ public App removeApp() public void resetStates() { + state = State.UNCHANGED; // Drop paths that were removed. List removedPaths = paths.entrySet() .stream().filter(e -> e.getValue() == State.REMOVED) @@ -130,12 +168,34 @@ public void resetStates() paths.replaceAll((p, v) -> State.UNCHANGED); } + @Override + public boolean equals(Object o) + { + if (o == null || getClass() != o.getClass()) + return false; + Unit unit = (Unit)o; + return Objects.equals(baseName, unit.baseName); + } + + @Override + public int hashCode() + { + return Objects.hashCode(baseName); + } + + public void setState(State state) + { + this.state = state; + } + @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("Unit[").append(baseName); str.append("|").append(getState()); + if (envConfig != null) + str.append(", envConfig=").append(envConfig); str.append(", paths="); str.append(paths.entrySet().stream() .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java index 004d1db4a425..418e4b85d63b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java @@ -27,6 +27,8 @@ import org.eclipse.jetty.deploy.providers.Unit; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.Environment; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,18 +83,33 @@ public void pathsChanged(Map changeSet) } Set changedBaseNames = new HashSet<>(); + Set environmentNames = Environment.getAll().stream() + .map(Environment::getName).collect(Collectors.toUnmodifiableSet()); for (Map.Entry entry : changeSet.entrySet()) { Path path = entry.getKey(); Unit.State state = toState(entry.getValue()); - // to lower-case uses system Locale, as we are working with the system FS. + // Using lower-case as defined by System Locale, as the files themselves from System FS. String basename = FileID.getBasename(path).toLowerCase(); - changedBaseNames.add(basename); - Unit unit = units.computeIfAbsent(basename, Unit::new); - unit.putPath(path, state); + String envname = getEnvironmentName(basename, environmentNames); + if (StringUtil.isNotBlank(envname)) + { + // we have an environment configuration specific path entry. + changedBaseNames.add(envname); + Unit unit = units.computeIfAbsent(envname, Unit::new); + unit.setEnvironmentConfigName(envname); + unit.putPath(path, state); + } + else + { + // we have a normal path entry + changedBaseNames.add(basename); + Unit unit = units.computeIfAbsent(basename, Unit::new); + unit.putPath(path, state); + } } Listener listener = getListener(); @@ -106,6 +123,18 @@ public void pathsChanged(Map changeSet) } } + protected String getEnvironmentName(String basename, Set environmentNames) + { + // Handle the case where the basename is part of an environment configuration + for (String envname : environmentNames) + { + if (basename.startsWith(envname)) + return envname; + } + + return null; + } + private Unit.State toState(Scanner.Notification notification) { return switch (notification) diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java index ffe6787a9fcd..ae2a39e55bef 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -29,6 +30,7 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.component.Environment; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -36,6 +38,8 @@ import org.slf4j.LoggerFactory; import static org.awaitility.Awaitility.await; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; @@ -116,6 +120,22 @@ public void teardownEnvironment() throws Exception jetty.stop(); } + /** + * Cleanup after any tests that modify the {@link Environment} singleton + */ + @AfterEach + public void clearEnvironments() + { + List envnames = Environment.getAll().stream() + .map(Environment::getName) + .toList(); + for (String envname : envnames) + { + Environment.remove(envname); + } + assertEquals(0, Environment.getAll().size()); + } + public void waitForDirectoryScan() { int scan = _scans.get() + _providerCount; @@ -160,6 +180,38 @@ public void testSelectiveDeploy(WorkDir workDir) throws Exception assertNotSame(contextHandler2, contextHandler3); } + @Test + public void testWebAppsWithAddedEnvConfig(WorkDir workDir) throws Exception + { + Path testdir = workDir.getEmptyPathDir(); + createJettyBase(testdir); + + Environment.ensure("core").setAttribute("testname", "Initial"); + + // Setup initial webapp, with XML + Path webappsDir = jetty.getJettyBasePath().resolve("webapps"); + Files.createFile(webappsDir.resolve("simple.war")); + jetty.copyWebapp("simple.xml", "simple.xml"); + + // Start jetty + startJetty(); + + // Verify existence + jetty.assertContextHandlerExists("/simple"); + ContextHandler contextHandler = jetty.getContextHandler("/simple"); + assertNotNull(contextHandler); + assertThat("displayname", contextHandler.getDisplayName(), is("Simple Initial")); + + // Add environment configuration + Path coreProp = jetty.getJettyBasePath().resolve("webapps/core.properties"); + Files.writeString(coreProp, "testname=New EnvConfig"); + + waitForDirectoryScan(); + contextHandler = jetty.getContextHandler("/simple"); + assertNotNull(contextHandler); + assertThat("displayname", contextHandler.getDisplayName(), is("Simple New EnvConfig")); + } + /** * Simple webapp deployment after startup of server. * diff --git a/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml b/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml index 3aac3b002145..2ba4b3be952b 100644 --- a/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml +++ b/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml @@ -7,4 +7,7 @@ + Simple + + From 24eb7919adaebbf371a251ced00fd1248ae41b9a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 17 Jan 2025 16:45:22 -0600 Subject: [PATCH 029/104] Correct syntax --- jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml b/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml index 2ba4b3be952b..b2f3d4080b71 100644 --- a/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml +++ b/jetty-core/jetty-deploy/src/test/resources/webapps/simple.xml @@ -7,7 +7,5 @@ - Simple - - + Simple From 4d286d7e668b95c808e64b819bd3928afad030e1 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 20 Jan 2025 15:59:57 -0600 Subject: [PATCH 030/104] Updating Unit.calcState javadoc and unit tests. --- .../eclipse/jetty/deploy/providers/Unit.java | 41 +++++- .../jetty/deploy/providers/UnitTest.java | 131 ++++++++++++++++++ 2 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java index c4f0669fac43..fabd82d33523 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java @@ -22,6 +22,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.resource.PathCollators; /** * A Unit of deployment, a basename and all the associated @@ -46,11 +47,12 @@ public enum State */ private String envConfig; private App app; - private State state = State.UNCHANGED; + private State state; public Unit(String basename) { this.baseName = basename; + this.state = calcState(); } public String getBaseName() @@ -63,6 +65,23 @@ public State getState() return state; } + /** + *

        + * Calculate the State of the overall Unit based on the States in the Paths. + *

        + *
        + *
        UNCHANGED
        + *
        All Path states are in UNCHANGED state
        + *
        ADDED
        + *
        All Path states are in ADDED state
        + *
        CHANGED
        + *
        At least one Path state is CHANGED, or there is a variety of states
        + *
        REMOVED
        + *
        All Path states are in REMOVED state, or there are no Paths being tracked
        + *
        + * + * @return the state of the Unit. + */ private State calcState() { if (paths.isEmpty()) @@ -78,6 +97,8 @@ private State calcState() { if (ret == null) ret = State.UNCHANGED; + else if (ret != State.UNCHANGED) + ret = State.CHANGED; } case ADDED -> { @@ -107,6 +128,22 @@ public Map getPaths() return paths; } + /** + * Return list of Paths being tracked that are live (not-removed). + * + * @return the list of live paths. + */ + public List getLivePaths() + { + return paths + .entrySet() + .stream() + .filter((e) -> e.getValue() != Unit.State.REMOVED) + .map(Map.Entry::getKey) + .sorted(PathCollators.byName(true)) + .toList(); + } + public void putPath(Path path, State state) { this.paths.put(path, state); @@ -154,7 +191,6 @@ public App removeApp() public void resetStates() { - state = State.UNCHANGED; // Drop paths that were removed. List removedPaths = paths.entrySet() .stream().filter(e -> e.getValue() == State.REMOVED) @@ -166,6 +202,7 @@ public void resetStates() } // Set all remaining path states to UNCHANGED paths.replaceAll((p, v) -> State.UNCHANGED); + state = calcState(); } @Override diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java new file mode 100644 index 000000000000..587c19ed9367 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java @@ -0,0 +1,131 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class UnitTest +{ + @Test + public void testUnitStateUnchanged() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); + unit.putPath(Path.of("test-b"), Unit.State.UNCHANGED); + + assertThat(unit.getState(), is(Unit.State.UNCHANGED)); + } + + @Test + public void testUnitStateInitialEmpty() + { + Unit unit = new Unit("test"); + // intentionally empty of Paths + + assertThat(unit.getState(), is(Unit.State.REMOVED)); + } + + @Test + public void testUnitStatePutThenRemoveAll() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.ADDED); + assertThat(unit.getState(), is(Unit.State.ADDED)); + + // Now it gets flagged as removed. (eg: by a Scanner change) + unit.putPath(Path.of("test-a"), Unit.State.REMOVED); + // Then it gets processed, which results in a state reset. + unit.resetStates(); + + // The resulting Unit should have no paths, and be flagged as removed. + assertThat(unit.getPaths().size(), is(0)); + assertThat(unit.getState(), is(Unit.State.REMOVED)); + } + + @Test + public void testUnitStateAddedOnly() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.ADDED); + unit.putPath(Path.of("test-b"), Unit.State.ADDED); + + assertThat(unit.getState(), is(Unit.State.ADDED)); + } + + @Test + public void testUnitStateAddedRemoved() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.REMOVED); // existing file removed in this scan + unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } + + @Test + public void testUnitStateAddedChanged() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.CHANGED); // existing file changed in this scan + unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } + + @Test + public void testUnitStateUnchangedAdded() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan + unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } + + @Test + public void testUnitStateUnchangedChanged() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan + unit.putPath(Path.of("test-b"), Unit.State.CHANGED); // existing file changed in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } + + @Test + public void testUnitStateUnchangedRemoved() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan + unit.putPath(Path.of("test-b"), Unit.State.REMOVED); // existing file removed in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } + + @Test + public void testUnitStateUnchangedRemovedAdded() + { + Unit unit = new Unit("test"); + unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan + unit.putPath(Path.of("test-b"), Unit.State.REMOVED); // existing file changed in this scan event + unit.putPath(Path.of("test-c"), Unit.State.ADDED); // new file introduced in this scan event + + assertThat(unit.getState(), is(Unit.State.CHANGED)); + } +} From 40052bdbed71199972cd0383bd36f73293cc231d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 20 Jan 2025 16:00:34 -0600 Subject: [PATCH 031/104] Use new Unit.getLivePaths() impl --- .../eclipse/jetty/deploy/providers/ContextProvider.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 64bd4118370d..a907e015e48d 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -654,8 +654,10 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) @Override protected Path getMainDeploymentPath(Unit unit) { + List livePaths = unit.getLivePaths(); + // XML always win. - List xmls = unit.getPaths().keySet().stream() + List xmls = livePaths.stream() .filter(FileID::isXml) .toList(); if (xmls.size() == 1) @@ -663,7 +665,7 @@ protected Path getMainDeploymentPath(Unit unit) else if (xmls.size() > 1) throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); // WAR files are next. - List wars = unit.getPaths().keySet().stream() + List wars = livePaths.stream() .filter(FileID::isWebArchive) .toList(); if (wars.size() == 1) @@ -671,7 +673,7 @@ else if (xmls.size() > 1) else if (wars.size() > 1) throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); // Directories next. - List dirs = unit.getPaths().keySet().stream() + List dirs = livePaths.stream() .filter(Files::isDirectory) .toList(); if (dirs.size() == 1) From 6806b5865689a0863d611afe430e5a80f9ceb240 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 21 Jan 2025 07:29:49 -0600 Subject: [PATCH 032/104] Isolate and cleanup deploy test that manipulate the Environment singleton. --- .../deploy/AbstractCleanEnvironmentTest.java | 49 +++++++++++++++++++ .../ContextProviderDeferredStartupTest.java | 3 +- .../ContextProviderRuntimeUpdatesTest.java | 20 +------- .../providers/ContextProviderStartupTest.java | 3 +- .../deploy/providers/ContextProviderTest.java | 3 +- 5 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractCleanEnvironmentTest.java diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractCleanEnvironmentTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractCleanEnvironmentTest.java new file mode 100644 index 000000000000..42754a9e6efe --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractCleanEnvironmentTest.java @@ -0,0 +1,49 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import java.util.List; + +import org.eclipse.jetty.util.component.Environment; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.parallel.Isolated; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Ensure that each test is using a clean (empty) Environment, + * and in the case of parallel test execution, is not using + * a shared Environment. + */ +@Isolated +public abstract class AbstractCleanEnvironmentTest +{ + /** + * Cleanup the {@link Environment} singleton + */ + @BeforeEach + @AfterEach + public void clearEnvironments() + { + List envnames = Environment.getAll().stream() + .map(Environment::getName) + .toList(); + for (String envname : envnames) + { + Environment.remove(envname); + } + assertEquals(0, Environment.getAll().size()); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java index 620223a1e319..7b957380255e 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java @@ -21,6 +21,7 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; @@ -47,7 +48,7 @@ * Tests {@link ContextProvider} behaviors when in Deferred Startup mode */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderDeferredStartupTest +public class ContextProviderDeferredStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java index ae2a39e55bef..c54ec97d769e 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java @@ -17,10 +17,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; @@ -49,7 +49,7 @@ * deployed contexts due to incoming changes identified by the {@link ContextProvider}. */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderRuntimeUpdatesTest +public class ContextProviderRuntimeUpdatesTest extends AbstractCleanEnvironmentTest { private static final Logger LOG = LoggerFactory.getLogger(ContextProviderRuntimeUpdatesTest.class); @@ -120,22 +120,6 @@ public void teardownEnvironment() throws Exception jetty.stop(); } - /** - * Cleanup after any tests that modify the {@link Environment} singleton - */ - @AfterEach - public void clearEnvironments() - { - List envnames = Environment.getAll().stream() - .map(Environment::getName) - .toList(); - for (String envname : envnames) - { - Environment.remove(envname); - } - assertEquals(0, Environment.getAll().size()); - } - public void waitForDirectoryScan() { int scan = _scans.get() + _providerCount; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java index 723f45d9109d..d864ff87fa55 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java @@ -20,6 +20,7 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; import org.eclipse.jetty.deploy.BarContextHandler; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.Deployable; @@ -44,7 +45,7 @@ * Tests {@link ContextProvider} as it starts up for the first time. */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderStartupTest +public class ContextProviderStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java index ea0e0b20e09a..6a30dc6d1c71 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java @@ -17,6 +17,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.junit.jupiter.api.Test; @@ -27,7 +28,7 @@ import static org.hamcrest.Matchers.equalTo; @ExtendWith(WorkDirExtension.class) -public class ContextProviderTest +public class ContextProviderTest extends AbstractCleanEnvironmentTest { public WorkDir workDir; From f92901d4fbedccff201c48f91c0e5782924ff6be Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 21 Jan 2025 15:34:07 -0600 Subject: [PATCH 033/104] Correct XML from merge --- .../jetty-ee11-test-integration/src/test/resources/deploy.xml | 2 +- .../jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index dd84a5fa4912..2d169e3376a9 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -30,7 +30,7 @@ true - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index 66a563ed7fff..117abad9dd0c 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -23,7 +23,7 @@ - .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ From c7fdc9209adb7a98cbc43bfc5bc02eb3f48b12a9 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 21 Jan 2025 16:17:39 -0600 Subject: [PATCH 034/104] Debug for failing `JettyWebSocketTest` on CI --- .../src/test/resources/jetty-logging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties index 0003cced627a..a3b4ac63db8d 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties @@ -1,4 +1,4 @@ # Jetty Logging using jetty-slf4j-impl -#org.eclipse.jetty.LEVEL=DEBUG +org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG #org.eclipse.jetty.util.ssl.KeyStoreScanner.LEVEL=DEBUG From 745f4fe37e3a42e93175c96ed365557346600391 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 22 Jan 2025 02:51:04 -0600 Subject: [PATCH 035/104] Issue warning for overlap on reserved env configuration names in the webapps/ directory. --- .../providers/internal/DeploymentUnits.java | 27 ++++++++-- .../src/main/webapp/WEB-INF/web.xml | 7 ++- .../jetty-ee10-test-integration/pom.xml | 4 +- .../jetty-ee11-test-integration/pom.xml | 4 +- .../src/test/resources/deploy.xml | 53 +++++++++---------- .../jetty-ee9-test-integration/pom.xml | 4 +- 6 files changed, 57 insertions(+), 42 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java index 418e4b85d63b..aadcdea2a4b0 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java @@ -97,11 +97,28 @@ public void pathsChanged(Map changeSet) String envname = getEnvironmentName(basename, environmentNames); if (StringUtil.isNotBlank(envname)) { - // we have an environment configuration specific path entry. - changedBaseNames.add(envname); - Unit unit = units.computeIfAbsent(envname, Unit::new); - unit.setEnvironmentConfigName(envname); - unit.putPath(path, state); + // The file starts with a known environment name. + // We only care if it is a Environment Configuration file, namely an XML or Properties file. + if (FileID.isExtension(path, "xml", "properties")) + { + // we have an environment configuration specific path entry. + changedBaseNames.add(envname); + Unit unit = units.computeIfAbsent(envname, Unit::new); + unit.setEnvironmentConfigName(envname); + unit.putPath(path, state); + } + else + { + LOG.warn("Encountered Path that uses reserved Environment name prefix " + + "(This is seen as a configuration for the environment) [{}]: {}", envname, path); + // This is something like ee10-app.war + // For now, we add it to the basename, but in the future, this will be rejected + // as it prevents the use of a configuration file like ee10-app.xml next to it (which + // will be seen as a XML to configure the ee10 environment, not an XML for deploying that war) + changedBaseNames.add(basename); + Unit unit = units.computeIfAbsent(basename, Unit::new); + unit.putPath(path, state); + } } else { diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-websocket-webapp/src/main/webapp/WEB-INF/web.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-websocket-webapp/src/main/webapp/WEB-INF/web.xml index 74eb00ae504e..79f3afa95e65 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-websocket-webapp/src/main/webapp/WEB-INF/web.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-websocket-webapp/src/main/webapp/WEB-INF/web.xml @@ -1,12 +1,11 @@ + version="6.0"> -EE10 Demo Jetty Websocket WebApp + EE10 Demo Jetty Websocket WebApp WSChat diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml index c492c51b6a08..22d0a7a82171 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/pom.xml @@ -145,7 +145,7 @@ jetty-ee10-test-webapp-rfc2616 ${project.version} war - ee10-test-rfc2616.war + jetty-ee10-test-rfc2616.war org.eclipse.jetty.demos @@ -157,7 +157,7 @@ org.eclipse.jetty.ee10.demos jetty-ee10-demo-jetty-websocket-webapp war - ee10-demo-jetty-websocket.war + jetty-ee10-demo-jetty-websocket.war true diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/pom.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/pom.xml index 099a833975c2..8f1601f5228c 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/pom.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/pom.xml @@ -145,7 +145,7 @@ jetty-ee11-test-webapp-rfc2616 ${project.version} war - ee11-test-rfc2616.war + jetty-ee11-test-rfc2616.war org.eclipse.jetty.demos @@ -157,7 +157,7 @@ org.eclipse.jetty.ee11.demos jetty-ee11-demo-jetty-websocket-webapp war - ee11-demo-jetty-websocket.war + jetty-ee11-demo-jetty-websocket.war true diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 2d169e3376a9..23b1f84ef03e 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -11,35 +11,34 @@ - - - - - - - target - webapps - - - - 1 - - ee11 - org.eclipse.jetty.ee11.webapp.WebAppContext - true - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - + + + + + + target + webapps + - - - - - - - + 1 + + + ee11 + org.eclipse.jetty.ee11.webapp.WebAppContext + true + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + + + + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/pom.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/pom.xml index ee192f63ac55..79897d06f9fb 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/pom.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/pom.xml @@ -155,7 +155,7 @@ jetty-ee9-test-webapp-rfc2616 ${project.version} war - ee9-test-rfc2616.war + jetty-ee9-test-rfc2616.war org.eclipse.jetty.demos @@ -167,7 +167,7 @@ org.eclipse.jetty.ee9.demos jetty-ee9-demo-jetty-websocket-webapp war - ee9-demo-jetty-websocket.war + jetty-ee9-demo-jetty-websocket.war true From 456132fef33ec271253c4fb8773c54842ff07816 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 22 Jan 2025 03:10:48 -0600 Subject: [PATCH 036/104] Rollback DEBUG logging --- .../src/test/resources/jetty-logging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties index a3b4ac63db8d..0003cced627a 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/jetty-logging.properties @@ -1,4 +1,4 @@ # Jetty Logging using jetty-slf4j-impl -org.eclipse.jetty.LEVEL=DEBUG +#org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG #org.eclipse.jetty.util.ssl.KeyStoreScanner.LEVEL=DEBUG From dfd741b06b82d8a72cca45b1f80a0ae2cb50c143 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 22 Jan 2025 03:25:07 -0600 Subject: [PATCH 037/104] Use new war filenames properly --- .../eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java | 2 +- .../test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml | 3 ++- .../eclipse/jetty/ee11/test/websocket/JettyWebSocketTest.java | 2 +- .../test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml | 3 ++- .../eclipse/jetty/ee9/test/websocket/JettyWebSocketTest.java | 2 +- .../test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml | 3 ++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java index 802a9b34f636..cc65fcde3ea6 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/websocket/JettyWebSocketTest.java @@ -55,7 +55,7 @@ public static void tearDownServer() throws Exception @Test public void testChatEndpoint() throws Exception { - URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/ee10-demo-jetty-websocket/jetty.websocket/foo")); + URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/jetty-ee10-demo-jetty-websocket/jetty.websocket/foo")); WebSocketClient client = new WebSocketClient(); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml index bf4a0135326e..b2602b731a8b 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml @@ -2,7 +2,8 @@ /rfc2616-webapp - /ee10-test-rfc2616.war + /jetty-ee10-test-rfc2616.war + org.slf4j. org.eclipse.jetty.logging. diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JettyWebSocketTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JettyWebSocketTest.java index 35ca466c28cb..8cfe92c4f557 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JettyWebSocketTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/websocket/JettyWebSocketTest.java @@ -55,7 +55,7 @@ public static void tearDownServer() throws Exception @Test public void testChatEndpoint() throws Exception { - URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/ee11-demo-jetty-websocket/jetty.websocket/foo")); + URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/jetty-ee11-demo-jetty-websocket/jetty.websocket/foo")); WebSocketClient client = new WebSocketClient(); diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml index b9cd82a639d7..246599caacd8 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml @@ -2,7 +2,8 @@ /rfc2616-webapp - /ee11-test-rfc2616.war + /jetty-ee11-test-rfc2616.war + org.slf4j. org.eclipse.jetty.logging. diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JettyWebSocketTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JettyWebSocketTest.java index 2a5f254e3b60..47e4cad75214 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JettyWebSocketTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/websocket/JettyWebSocketTest.java @@ -55,7 +55,7 @@ public static void tearDownServer() throws Exception @Test public void testChatEndpoint() throws Exception { - URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/ee9-demo-jetty-websocket/jetty.websocket/foo")); + URI uri = WSURI.toWebsocket(server.getServerURI().resolve("/jetty-ee9-demo-jetty-websocket/jetty.websocket/foo")); WebSocketClient client = new WebSocketClient(); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml index 43c26f98787d..784b2c86a010 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml @@ -2,7 +2,8 @@ /rfc2616-webapp - /ee9-test-rfc2616.war + /jetty-ee9-test-rfc2616.war + org.slf4j. org.eclipse.jetty.logging. From 72c73045badf9b574e746c77e56563d60a0ae1dd Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 07:04:39 -0600 Subject: [PATCH 038/104] Overhaul based on last PR review --- .../src/main/config/etc/jetty-deploy.xml | 28 +- .../java/org/eclipse/jetty/deploy/App.java | 249 ++-- .../eclipse/jetty/deploy/AppLifeCycle.java | 6 +- .../org/eclipse/jetty/deploy/AppProvider.java | 13 +- .../jetty/deploy/DeploymentManager.java | 120 +- .../jetty/deploy/bindings/DebugBinding.java | 3 +- .../deploy/bindings/OrderedGroupBinding.java | 11 +- .../deploy/bindings/StandardDeployer.java | 5 +- .../deploy/bindings/StandardStarter.java | 5 +- .../deploy/bindings/StandardStopper.java | 5 +- .../deploy/bindings/StandardUndeployer.java | 5 +- .../deploy/jmx/DeploymentManagerMBean.java | 4 +- .../deploy/providers/ContextProvider.java | 1026 ---------------- .../jetty/deploy/providers/DefaultApp.java | 332 ++++++ .../DefaultContextHandlerFactory.java | 449 +++++++ .../deploy/providers/DefaultProvider.java | 1054 +++++++++++++++++ .../jetty/deploy/providers/DeployAction.java | 50 + .../providers/DeployActionComparator.java | 57 + .../deploy/providers/ScanningAppProvider.java | 422 ------- .../eclipse/jetty/deploy/providers/Unit.java | 249 ---- .../providers/internal/DeploymentUnits.java | 178 --- .../jetty/deploy/lifecycle-bindings.txt | 1 - .../deploy/AppLifeCyclePathCollector.java | 2 +- .../DeploymentManagerLifeCycleRouteTest.java | 14 +- .../jetty/deploy/DeploymentManagerTest.java | 85 +- .../org/eclipse/jetty/deploy/MockApp.java | 50 + .../eclipse/jetty/deploy/MockAppProvider.java | 31 +- .../deploy/providers/ContextProviderTest.java | 154 --- .../deploy/providers/DefaultAppTest.java | 257 ++++ ...> DefaultProviderDeferredStartupTest.java} | 16 +- ...=> DefaultProviderRuntimeUpdatesTest.java} | 21 +- ...t.java => DefaultProviderStartupTest.java} | 108 +- .../deploy/providers/DefaultProviderTest.java | 371 ++++++ .../providers/DeployActionComparatorTest.java | 196 +++ .../jetty/deploy/providers/UnitTest.java | 131 -- .../internal/DeploymentUnitsTest.java | 255 ---- .../jetty/deploy/test/XmlConfiguredJetty.java | 6 - .../resources/jetty-core-deploy-custom.xml | 21 +- .../webapps/bar-core-context.properties | 4 +- .../src/main/config/etc/jetty-core-deploy.xml | 4 +- .../src/main/config/modules/core-deploy.mod | 7 +- .../server/handler/CoreWebAppContext.java | 214 ++++ .../eclipse/jetty/xml/XmlConfiguration.java | 18 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee11-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee8-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 2 +- 47 files changed, 3346 insertions(+), 2899 deletions(-) delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployAction.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployActionComparator.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java delete mode 100644 jetty-core/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy/lifecycle-bindings.txt create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/{ContextProviderDeferredStartupTest.java => DefaultProviderDeferredStartupTest.java} (92%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/{ContextProviderRuntimeUpdatesTest.java => DefaultProviderRuntimeUpdatesTest.java} (92%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/{ContextProviderStartupTest.java => DefaultProviderStartupTest.java} (64%) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DeployActionComparatorTest.java delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java rename jetty-core/{jetty-deploy => jetty-server}/src/main/config/etc/jetty-core-deploy.xml (77%) rename jetty-core/{jetty-deploy => jetty-server}/src/main/config/modules/core-deploy.mod (69%) create mode 100644 jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index afc5c4e338f7..e7040ab9692b 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -2,20 +2,32 @@ - + - - + + - - - + + + + + + + + + + + + + + + - + @@ -25,7 +37,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java index 79770e16d93b..7a7db0c75ff5 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java @@ -13,146 +13,133 @@ package org.eclipse.jetty.deploy; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.FileID; /** - * The information about an App that is managed by the {@link DeploymentManager}. + * An abstract App, the component that moves through the DeploymentManager. */ -public class App +public interface App { - private final DeploymentManager _manager; - private final AppProvider _provider; - private final Path _path; - private final Map _properties = new HashMap<>(); - private ContextHandler _context; - - /** - * Create an App with specified Origin ID and archivePath - *

        - * Any properties file that exists with the same {@link FileID#getBasename(Path)} as the - * filename passed will be used to initialize the properties returned by {@link #getProperties()}. - * @param manager the deployment manager - * @param provider the app provider - * @param path the path to the application directory, war file or XML descriptor - * @see App#getContextPath() - */ - public App(DeploymentManager manager, AppProvider provider, Path path) - { - _manager = manager; - _provider = provider; - _path = path; - - try - { - String basename = FileID.getBasename(path); - Path properties = path.getParent().resolve(basename + ".properties"); - if (Files.exists(properties)) - { - try (InputStream stream = Files.newInputStream(properties)) - { - Properties p = new Properties(); - p.load(stream); - p.stringPropertyNames().forEach(k -> _properties.put(k, p.getProperty(k))); - } - } - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - - /** - * @return The deployment manager - */ - public DeploymentManager getDeploymentManager() - { - return _manager; - } - - /** - * @return The AppProvider - */ - public AppProvider getAppProvider() - { - return _provider; - } - - public Map getProperties() - { - return _properties; - } + String getName(); /** - * Get ContextHandler for the App. - * - * Create it if needed. - * - * @return the {@link ContextHandler} to use for the App when fully started. - * (Portions of which might be ignored when App is not yet - * {@link AppLifeCycle#DEPLOYED} or {@link AppLifeCycle#STARTED}) - * @throws Exception if unable to get the context handler + * Get the active ContextHandler for this App. + * The App is not responsible for creating the ContextHandler, the AppProvider is. */ - public ContextHandler getContextHandler() throws Exception + // TODO: make not default + default ContextHandler getContextHandler() { - if (_context == null) - _context = getAppProvider().createContextHandler(this); - return _context; + return null; } - /** - * The context path {@link App} relating to how it is installed on the - * jetty server side. - * - * @return the contextPath for the App - */ - public String getContextPath() - { - return _context == null ? null : _context.getContextPath(); - } - - /** - * Get the environment name. - * @return The {@link org.eclipse.jetty.util.component.Environment} name for the application - * if set with the {@link Deployable#ENVIRONMENT} property, else null. - */ - public String getEnvironmentName() - { - return getProperties().get(Deployable.ENVIRONMENT); - } - - /** - * The origin of this {@link App} as specified by the {@link AppProvider} - * - * @return String representing the origin of this app. - */ - public Path getPath() - { - return _path; - } - - /** - * Set the environment name. - * - * @param name the name of the environment. - */ - public void setEnvironmentName(String name) - { - getProperties().put(Deployable.ENVIRONMENT, name); - } - - @Override - public String toString() - { - return "App@%x[%s,%s,%s]".formatted(hashCode(), getEnvironmentName(), _context, _path); - } +// private final DeploymentManager _manager; +// private final AppProvider _provider; +// private final Path _path; +// private final Map _properties = new HashMap<>(); +// private ContextHandler _context; +// +// /** +// * Create an App with specified Origin ID and archivePath +// *

        +// * Any properties file that exists with the same {@link FileID#getBasename(Path)} as the +// * filename passed will be used to initialize the properties returned by {@link #getProperties()}. +// * +// * @param manager the deployment manager +// * @param provider the app provider +// * @param path the path to the application directory, war file or XML descriptor +// * @see App#getContextPath() +// */ +// public App(DeploymentManager manager, AppProvider provider, Path path) +// { +// _manager = manager; +// _provider = provider; +// _path = path; +// +// if (path != null) +// { +// try +// { +// String basename = FileID.getBasename(path); +// Path properties = path.getParent().resolve(basename + ".properties"); +// if (Files.exists(properties)) +// { +// try (InputStream stream = Files.newInputStream(properties)) +// { +// Properties p = new Properties(); +// p.load(stream); +// p.stringPropertyNames().forEach(k -> _properties.put(k, p.getProperty(k))); +// } +// } +// } +// catch (Exception e) +// { +// throw new RuntimeException(e); +// } +// } +// } +// +// /** +// * @return The deployment manager +// */ +// public DeploymentManager getDeploymentManager() +// { +// return _manager; +// } +// +// /** +// * @return The AppProvider +// */ +// public AppProvider getAppProvider() +// { +// return _provider; +// } +// +// protected ContextHandler newContextHandler() +// { +// return getDeploymentManager().getContextHandlerFactory().newContextHandler(this); +// } +// +// /** +// * Get ContextHandler for the App. +// * +// * Create it if needed. +// * +// * @return the {@link ContextHandler} to use for the App when fully started. +// * (Portions of which might be ignored when App is not yet +// * {@link AppLifeCycle#DEPLOYED} or {@link AppLifeCycle#STARTED}) +// * @throws Exception if unable to get the context handler +// */ +// public ContextHandler getContextHandler() throws Exception +// { +// if (_context == null) +// _context = newContextHandler(); +// return _context; +// } +// +// /** +// * The context path {@link App} relating to how it is installed on the +// * jetty server side. +// * +// * @return the contextPath for the App +// */ +// public String getContextPath() +// { +// return _context == null ? null : _context.getContextPath(); +// } +// +// /** +// * The origin of this {@link App} as specified by the {@link AppProvider} +// * +// * @return String representing the origin of this app. +// */ +// public Path getPath() +// { +// return _path; +// } +// +// @Override +// public String toString() +// { +// return "App@%x[%s,%s,%s]".formatted(hashCode(), getEnvironmentName(), _context, _path); +// } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java index 64911b144207..c59af5b4a0ac 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java @@ -39,7 +39,7 @@ public class AppLifeCycle extends Graph private static final String ALL_NODES = "*"; - public static interface Binding + public interface Binding { /** * Get a list of targets that this implementation should bind to. @@ -55,7 +55,7 @@ public static interface Binding * @param app the app being processed * @throws Exception if any problem severe enough to halt the AppLifeCycle processing */ - void processBinding(Node node, App app) throws Exception; + void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception; } // Well known existing lifecycle Nodes @@ -180,7 +180,7 @@ public void runBindings(Node node, App app, DeploymentManager deploymentManager) { if (LOG.isDebugEnabled()) LOG.debug("Calling {} for {}", binding.getClass().getName(), app); - binding.processBinding(node, app); + binding.processBinding(deploymentManager, node, app); } } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java index e65e07e3920b..4a8447d17baf 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java @@ -15,7 +15,6 @@ import java.io.IOException; -import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.component.LifeCycle; /** @@ -39,15 +38,5 @@ public interface AppProvider extends LifeCycle * @throws IOException if unable to create context * @throws Exception if unable to create context */ - ContextHandler createContextHandler(App app) throws Exception; - - /** - * @return The name of the {@link org.eclipse.jetty.util.component.Environment} this provider is for. - * @deprecated not used by all AppProviders, no generic replacement provided. - */ - @Deprecated(forRemoval = true, since = "12.1.0") - default String getEnvironmentName() - { - return ""; - } + // ContextHandler createContextHandler(App app) throws Exception; } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index c630e7444638..ba2074bb07a4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -13,14 +13,11 @@ package org.eclipse.jetty.deploy; -import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; @@ -32,8 +29,6 @@ import org.eclipse.jetty.deploy.graph.Edge; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.deploy.graph.Route; -import org.eclipse.jetty.deploy.providers.ContextProvider; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.ExceptionUtil; @@ -42,7 +37,6 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.thread.AutoLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,13 +65,6 @@ public class DeploymentManager extends ContainerLifeCycle */ public class AppEntry { - /** - * Version of the app. - * - * Note: Auto-increments on each {@link DeploymentManager#addApp(App)} - */ - private int version; - /** * The app being tracked. */ @@ -88,11 +75,6 @@ public class AppEntry */ private Node lifecyleNode; - /** - * Tracking the various AppState timestamps (in system milliseconds) - */ - private Map stateTimestamps = new HashMap(); - public App getApp() { return app; @@ -103,20 +85,9 @@ public Node getLifecyleNode() return lifecyleNode; } - public Map getStateTimestamps() - { - return stateTimestamps; - } - - public int getVersion() - { - return version; - } - void setLifeCycleNode(Node node) { this.lifecyleNode = node; - this.stateTimestamps.put(node, System.currentTimeMillis()); } } @@ -129,21 +100,6 @@ void setLifeCycleNode(Node node) private boolean _useStandardBindings = true; private String _defaultLifeCycleGoal = AppLifeCycle.STARTED; - /** - * Get the default {@link Environment} name for deployed applications, which is - * the maximal name when using the {@link Deployable#ENVIRONMENT_COMPARATOR}. - * @return The default {@link Environment} name or null. - * @deprecated not used, replacement at {@link ContextProvider#getDefaultEnvironmentName()} - */ - @Deprecated(since = "12.1.0", forRemoval = true) - public String getDefaultEnvironmentName() - { - return _providers.stream() - .map(AppProvider::getEnvironmentName) - .max(Deployable.ENVIRONMENT_COMPARATOR) - .orElse(null); - } - /** * Receive an app for processing. * @@ -188,16 +144,6 @@ public void setAppProviders(Collection providers) } } - /** - * @deprecated No replacement. AppProvider interface no longer has getEnvironmentName. - */ - @Deprecated(since = "12.1.0", forRemoval = true) - public boolean hasAppProviderFor(String environmentName) - { - return environmentName != null && getAppProviders().stream() - .map(AppProvider::getEnvironmentName).anyMatch(environmentName::equalsIgnoreCase); - } - public Collection getAppProviders() { return Collections.unmodifiableList(_providers); @@ -297,49 +243,26 @@ protected void doStop() throws Exception super.doStop(); } - private AppEntry findApp(String appId) + private AppEntry findAppEntry(String appId) { if (appId == null) return null; for (AppEntry entry : _apps) { - Path path = entry.app.getPath(); - if (appId.equals(path.getName(path.getNameCount() - 1).toString())) - return entry; - } - return null; - } - - private AppEntry findApp(Path path) - { - if (path == null) - return null; - - for (AppEntry entry : _apps) - { - if (path.equals(entry.app.getPath())) - { + String name = entry.app.getName(); + if (appId.equals(name)) return entry; - } } return null; } public App getApp(String appId) { - AppEntry entry = findApp(appId); + AppEntry entry = findAppEntry(appId); return entry == null ? null : entry.getApp(); } - public App getApp(Path path) - { - AppEntry entry = findApp(path); - if (entry == null) - return null; - return entry.app; - } - public Collection getAppEntries() { return Collections.unmodifiableCollection(_apps); @@ -381,37 +304,6 @@ public Collection getApps(String nodeName) return getApps(_lifecycle.getNodeByName(nodeName)); } - public List getAppsWithSameContext(App app) - { - List ret = new ArrayList(); - if (app == null) - { - return ret; - } - - String contextId = app.getContextPath(); - if (contextId == null) - { - // No context? Likely not deployed or started yet. - return ret; - } - - for (AppEntry entry : _apps) - { - if (entry.app.equals(app)) - { - // Its the input app. skip it. - continue; - } - - if (contextId.equals(entry.app.getContextPath())) - { - ret.add(entry.app); - } - } - return ret; - } - @ManagedAttribute("Deployed Contexts") public ContextHandlerCollection getContexts() { @@ -482,7 +374,7 @@ public void removeAppProvider(AppProvider provider) */ public void requestAppGoal(App app, String nodeName) { - AppEntry appentry = findApp(app.getPath()); + AppEntry appentry = findAppEntry(app.getName()); if (appentry == null) { throw new IllegalStateException("App not being tracked by Deployment Manager: " + app); @@ -573,7 +465,7 @@ private void addOnStartupError(Throwable cause) @ManagedOperation(value = "request the app to be moved to the specified lifecycle node", impact = "ACTION") public void requestAppGoal(@Name("appId") String appId, @Name("nodeName") String nodeName) { - AppEntry appentry = findApp(appId); + AppEntry appentry = findAppEntry(appId); if (appentry == null) { throw new IllegalStateException("App not being tracked by Deployment Manager: " + appId); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java index 3eee9b1a7158..af9f2a7e47d2 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java @@ -15,6 +15,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +43,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { LOG.info("processBinding {} {}", node, app.getContextHandler()); } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java index 241d1bcfde76..f5db0a3df18b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java @@ -13,10 +13,12 @@ package org.eclipse.jetty.deploy.bindings; +import java.util.Arrays; import java.util.LinkedList; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; /** @@ -51,10 +53,7 @@ public void addBindings(AppLifeCycle.Binding[] bindings) _orderedBindings = new LinkedList(); } - for (AppLifeCycle.Binding binding : bindings) - { - _orderedBindings.add(binding); - } + _orderedBindings.addAll(Arrays.asList(bindings)); } @Override @@ -64,11 +63,11 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { for (AppLifeCycle.Binding binding : _orderedBindings) { - binding.processBinding(node, app); + binding.processBinding(deploymentManager, node, app); } } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java index 0a9597fa45cb..0df238d17712 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java @@ -15,6 +15,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Callback; @@ -28,14 +29,14 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); if (handler == null) throw new NullPointerException("No Handler created for App: " + app); Callback.Completable blocker = new Callback.Completable(); - app.getDeploymentManager().getContexts().deployHandler(handler, blocker); + deploymentManager.getContexts().deployHandler(handler, blocker); blocker.get(); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java index ec408b41cb19..5cd28565ceeb 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java @@ -15,6 +15,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -28,9 +29,9 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { - ContextHandlerCollection contexts = app.getDeploymentManager().getContexts(); + ContextHandlerCollection contexts = deploymentManager.getContexts(); ContextHandler handler = app.getContextHandler(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java index 4cc0fabd22c6..d086486b6f65 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java @@ -15,6 +15,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; @@ -27,12 +28,12 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); // Before stopping, take back management from the context - app.getDeploymentManager().getContexts().unmanage(handler); + deploymentManager.getContexts().unmanage(handler); // Stop it handler.stop(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java index 564921b037e6..9de570187f00 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java @@ -15,6 +15,7 @@ import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -29,9 +30,9 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { - ContextHandlerCollection contexts = app.getDeploymentManager().getContexts(); + ContextHandlerCollection contexts = deploymentManager.getContexts(); ContextHandler context = app.getContextHandler(); Callback.Completable blocker = new Callback.Completable(); contexts.undeployHandler(context, blocker); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java index 67b9fa57a0cd..a05178fe6af9 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java @@ -79,12 +79,12 @@ public Collection getNodes() private String toRef(App app) { - return String.format("contextPath=%s,path=%s,appProvider=%s", app.getContextPath(), app.getPath(), app.getAppProvider().getClass().getName()); + return String.format("%s,name=%s,%s", app.getClass().getSimpleName(), app.getName(), app.getContextHandler()); } public Collection getContexts() throws Exception { - List apps = new ArrayList(); + List apps = new ArrayList<>(); for (App app : _manager.getApps()) { apps.add(app.getContextHandler()); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java deleted file mode 100644 index a907e015e48d..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ /dev/null @@ -1,1026 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -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.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.io.IOResources; -import org.eclipse.jetty.server.Deployable; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.Attributes; -import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.component.Environment; -import org.eclipse.jetty.util.resource.PathCollators; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceFactory; -import org.eclipse.jetty.xml.XmlConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - *

        Jetty Environment WebApp Hot Deployment Provider.

        - * - *

        This provider scans one or more directories (typically "webapps") for contexts to - * deploy, which may be:

        - *
          - *
        • A standard WAR file (must end in ".war")
        • - *
        • A directory containing an expanded WAR file
        • - *
        • A directory containing static content
        • - *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • - *
        - *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider - * implements some heuristics to ignore some files found in the scans: - *

        - *
          - *
        • Hidden files (starting with {@code "."}) are ignored
        • - *
        • Directories with names ending in {@code ".d"} are ignored
        • - *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • - *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be - * the unpacked WAR and only the WAR file is deployed (which may reuse the unpacked directory)
        • - *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be - * an unpacked WAR and only the XML file is deployed (which may use the directory in its configuration)
        • - *
        • If a WAR file and a matching XML file exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR file is assumed to - * be configured by the XML file and only the XML file is deployed. - *
        - *

        For XML configured contexts, the following is available.

        - *
          - *
        • The XML Object ID Map will have a reference to the {@link Server} instance via the ID name {@code "Server"}
        • - *
        • The Default XML Properties are populated from a call to {@link XmlConfiguration#setJettyStandardIdsAndProperties(Object, Path)} (for things like {@code jetty.home} and {@code jetty.base})
        • - *
        • An extra XML Property named {@code "jetty.webapps"} is available, and points to the monitored path.
        • - *
        - *

        - * Context Deployment properties will be initialized with: - *

        - *
          - *
        • The properties set on the application via embedded calls modifying {@link App#getProperties()}
        • - *
        • The app specific properties file {@code webapps/.properties}
        • - *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • - *
        • The {@link Attributes} from the {@link Environment}
        • - *
        - */ -@ManagedObject("Provider for start-up deployment of webapps based on presence in directory") -public class ContextProvider extends ScanningAppProvider -{ - private static final Logger LOG = LoggerFactory.getLogger(ContextProvider.class); - private String _defaultEnvironmentName; - - public ContextProvider() - { - super(); - setFilenameFilter(new Filter()); - setScanInterval(0); - } - - private static Map asProperties(Attributes attributes) - { - Map props = new HashMap<>(); - attributes.getAttributeNameSet().forEach((name) -> - { - Object value = attributes.getAttribute(name); - String key = name.startsWith(Deployable.ATTRIBUTE_PREFIX) - ? name.substring(Deployable.ATTRIBUTE_PREFIX.length()) - : name; - props.put(key, Objects.toString(value)); - }); - return props; - } - - @Override - public ContextHandler createContextHandler(final App app) throws Exception - { - String envName = app.getEnvironmentName(); - Environment environment = Environment.get(StringUtil.isNotBlank(envName) ? envName : getDefaultEnvironmentName()); - - if (environment == null) - { - LOG.warn("Environment [{}] is not available for app [{}]. The available environments are: {}", - app.getEnvironmentName(), - app, - Environment.getAll().stream() - .map(Environment::getName) - .collect(Collectors.joining(", ", "[", "]")) - ); - return null; - } - - if (LOG.isDebugEnabled()) - LOG.debug("createContextHandler {} in {}", app, environment.getName()); - - ClassLoader old = Thread.currentThread().getContextClassLoader(); - try - { - Thread.currentThread().setContextClassLoader(environment.getClassLoader()); - - app.setEnvironmentName(environment.getName()); - - // Create de-aliased file - Path path = app.getPath().toRealPath(); - if (!Files.exists(path)) - throw new IllegalStateException("App resource does not exist " + path); - - // prepare app attributes to use for app deployment - Attributes appAttributes = initAttributes(environment, app); - - /* - * The process now is to figure out the context object to use. - * This can come from a number of places. - * 1. If an XML deployable, this is the entry. - * 2. If another deployable (like a web archive, or directory), then check attributes. - * a. use the app attributes to figure out the context handler class. - * b. use the environment attributes default context handler class. - */ - Object context = newContextInstance(environment, app, appAttributes, path); - if (context == null) - { - throw new IllegalStateException("unable to create ContextHandler for " + app); - } - if (LOG.isDebugEnabled()) - LOG.debug("Context {} created from app {}", context.getClass().getName(), app); - - // Apply environment properties and XML to context - if (applyEnvironmentXml(context, environment, appAttributes)) - { - // If an XML deployable, apply full XML over environment XML changes - if (FileID.isXml(path)) - context = applyXml(context, path, environment, appAttributes); - } - - // Set a backup value for the path to the war in case it hasn't already been set - // via a different means. This is especially important for a deployable App - // that is only a .war file (no XML). The eventual WebInfConfiguration - // will use this attribute. - appAttributes.setAttribute(Deployable.WAR, path.toString()); - - // Initialize any deployable - if (context instanceof Deployable deployable) - deployable.initializeDefaults(appAttributes); - - return getContextHandler(context); - } - finally - { - Thread.currentThread().setContextClassLoader(old); - } - } - - /** - * Initialize a new Context object instance. - * - *

        - * The search order is: - *

        - *
          - *
        1. If app attribute {@link Deployable#CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
        2. - *
        3. If App deployable path is XML, apply XML {@code }
        4. - *
        5. Fallback to environment attribute {@link Deployable#CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
        6. - *
        - * - * @param environment the environment context applies to - * @param app the App for the context - * @param appAttributes the Attributes for the App - * @param path the path of the deployable - * @return the Context Object. - * @throws Exception if unable to create Object instance. - */ - private Object newContextInstance(Environment environment, App app, Attributes appAttributes, Path path) throws Exception - { - Object context = newInstance((String)appAttributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS)); - if (context != null) - { - ContextHandler contextHandler = getContextHandler(context); - if (contextHandler == null) - throw new IllegalStateException("Unknown context type of " + context); - - initializeContextPath(contextHandler, path); - initializeContextHandler(contextHandler, path, appAttributes); - return context; - } - - if (FileID.isXml(path)) - { - context = applyXml(null, path, environment, appAttributes); - ContextHandler contextHandler = getContextHandler(context); - if (contextHandler == null) - throw new IllegalStateException("Unknown context type of " + context); - return context; - } - - // fallback to default from environment. - context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); - - if (context != null) - { - ContextHandler contextHandler = getContextHandler(context); - if (contextHandler == null) - throw new IllegalStateException("Unknown context type of " + context); - - initializeContextPath(contextHandler, path); - initializeContextHandler(contextHandler, path, appAttributes); - return context; - } - - return null; - } - - private Object newInstance(String className) throws Exception - { - if (StringUtil.isBlank(className)) - return null; - if (LOG.isDebugEnabled()) - LOG.debug("Attempting to load class {}", className); - Class clazz = Loader.loadClass(className); - if (clazz == null) - return null; - return clazz.getConstructor().newInstance(); - } - - /** - * Apply optional environment specific XML to context. - * - * @param context the context to apply environment specific behavior to - * @param environment the environment to use - * @param appAttributes the attributes of the app - * @return true it environment specific XML was applied. - * @throws Exception if unable to apply environment configuration. - */ - private boolean applyEnvironmentXml(Object context, Environment environment, Attributes appAttributes) throws Exception - { - // Collect the optional environment context xml files. - // Order them according to the name of their property key names. - List sortedEnvXmlPaths = appAttributes.getAttributeNameSet() - .stream() - .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) - .map(k -> - { - Path envXmlPath = Paths.get((String)appAttributes.getAttribute(k)); - if (!envXmlPath.isAbsolute()) - { - Path monitoredPath = getMonitoredDirResource().getPath(); - // not all Resource implementations support java.nio.file.Path. - if (monitoredPath != null) - { - envXmlPath = monitoredPath.getParent().resolve(envXmlPath); - } - } - return envXmlPath; - }) - .filter(Files::isRegularFile) - .sorted(PathCollators.byName(true)) - .toList(); - - boolean xmlApplied = false; - - // apply each environment context xml file - for (Path envXmlPath : sortedEnvXmlPaths) - { - if (LOG.isDebugEnabled()) - LOG.debug("Applying environment specific context file {}", envXmlPath); - context = applyXml(context, envXmlPath, environment, appAttributes); - xmlApplied = true; - } - - return xmlApplied; - } - - /** - * Get the default {@link Environment} name for discovered web applications that - * do not declare the {@link Environment} that they belong to. - * - *

        - * Falls back to {@link Environment#getAll()} list, and returns - * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} - *

        - * - * @return the default environment name. - */ - public String getDefaultEnvironmentName() - { - if (_defaultEnvironmentName == null) - { - return Environment.getAll().stream() - .map(Environment::getName) - .max(Deployable.ENVIRONMENT_COMPARATOR) - .orElse(null); - } - return _defaultEnvironmentName; - } - - public void setDefaultEnvironmentName(String name) - { - this._defaultEnvironmentName = name; - } - - @Deprecated - public Map getProperties(Environment environment) - { - return asProperties(environment); - } - - public void loadProperties(Environment environment, Path path) throws IOException - { - try (InputStream inputStream = Files.newInputStream(path)) - { - loadProperties(environment, inputStream); - } - } - - public void loadProperties(Environment environment, Resource resource) throws IOException - { - try (InputStream inputStream = IOResources.asInputStream(resource)) - { - loadProperties(environment, inputStream); - } - } - - public void loadPropertiesFromString(Environment environment, String path) throws IOException - { - loadProperties(environment, Path.of(path)); - } - - /** - * Configure the Environment specific Deploy settings. - * - * @param name the name of the environment. - * @return the deployment configuration for the {@link Environment}. - */ - public EnvironmentConfig configureEnvironment(String name) - { - return new EnvironmentConfig(Environment.ensure(name)); - } - - /** - * To enable support for an {@link Environment}, just ensure it exists. - * - *

        - * Eg: {@code Environment.ensure("ee11");} - *

        - * - *

        - * To configure Environment specific deployment {@link Attributes}, - * either set the appropriate {@link Deployable} attribute via {@link Attributes#setAttribute(String, Object)}, - * or use the convenience class {@link EnvironmentConfig}. - *

        - * - *
        {@code
        -     * ContextProvider provider = new ContextProvider();
        -     * ContextProvider.EnvironmentConfig env10config = provider.configureEnvironment("ee10");
        -     * env10config.setExtractWars(true);
        -     * env10config.setParentLoaderPriority(false);
        -     * }
        - * - * @see #configureEnvironment(String) instead - * @deprecated not used anymore. - */ - @Deprecated(since = "12.1.0", forRemoval = true) - public void setEnvironmentName(String name) - { - Environment.ensure(name); - } - - protected Object applyXml(Object context, Path xml, Environment environment, Attributes attributes) throws Exception - { - if (!FileID.isXml(xml)) - return null; - - XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, asProperties(attributes)) - { - @Override - public void initializeDefaults(Object context) - { - super.initializeDefaults(context); - ContextHandler contextHandler = getContextHandler(context); - if (contextHandler == null) - { - if (LOG.isDebugEnabled()) - LOG.debug("Not a ContextHandler: Not initializing Context {}", context); - } - else - { - ContextProvider.this.initializeContextPath(contextHandler, xml); - ContextProvider.this.initializeContextHandler(contextHandler, xml, attributes); - } - } - }; - - xmlc.getIdMap().put("Environment", environment.getName()); - xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml); - - // Put all Environment attributes into XmlConfiguration as properties that can be used. - attributes.getAttributeNameSet() - .stream() - .filter(k -> !k.startsWith("jetty.home") && - !k.startsWith("jetty.base") && - !k.startsWith("jetty.webapps")) - .forEach(k -> - { - String v = Objects.toString(attributes.getAttribute(k)); - xmlc.getProperties().put(k, v); - }); - - // Run configure against appropriate classloader. - ClassLoader xmlClassLoader = getXmlClassLoader(environment, xml); - ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(xmlClassLoader); - - try - { - // Create or configure the context - if (context == null) - return xmlc.configure(); - - return xmlc.configure(context); - } - finally - { - Thread.currentThread().setContextClassLoader(oldClassLoader); - } - } - - /** - * Return a ClassLoader that can load a {@link Environment#CORE} based webapp - * that is entirely defined within the {@code webapps/} directory. - * - *

        The resulting ClassLoader consists of the following entries:

        - *
          - *
        1. The java archive {@code .jar}
        2. - *
        3. The java archives {@code .d/lib/*.jar}
        4. - *
        5. The directory {@code .d/classes/}
        6. - *
        - * - * @param path to XML defining this webapp, must be absolute, and cannot be in root directory of drive. - * filename of XML will be used to determine the {@code } of the other entries in this - * ClassLoader. - * @return the classloader for this CORE environment webapp. - * @throws IOException if unable to apply to create classloader. - */ - protected ClassLoader findCoreContextClassLoader(Path path) throws IOException - { - Path webapps = path.getParent(); - String basename = FileID.getBasename(path).toLowerCase(); // use system Locale as we are dealing with system FS. - List urls = new ArrayList<>(); - - try (Stream listingStream = Files.list(webapps)) - { - // Collect all paths that match "webapps/*" - List basenamePaths = listingStream - .filter((p) -> - { - String filename = p.getFileName().toString().toLowerCase(); // use system Locale - return filename.startsWith(basename); - }) - .toList(); - - // Walk basename paths - for (Path p : basenamePaths) - { - if (Files.isDirectory(p)) - { - if (p.getFileName().toString().toLowerCase().endsWith(".d")) - { - Path libDir = p.resolve("lib"); - // we have a possible lib directory. - if (Files.isDirectory(libDir)) - { - try (Stream libPaths = Files.list(libDir)) - { - List javaArchives = libPaths.filter(FileID::isJavaArchive).toList(); - - for (Path lib : javaArchives) - { - urls.add(lib.toUri().toURL()); - } - } - } - - Path classesDir = p.resolve(basename + "classes"); - if (Files.isDirectory(libDir)) - urls.add(classesDir.toUri().toURL()); - } - } - - if (FileID.isJavaArchive(p)) - { - urls.add(p.toUri().toURL()); - } - } - } - - if (LOG.isDebugEnabled()) - LOG.debug("Core classloader for {}", urls); - - if (urls.isEmpty()) - return null; - return new URLClassLoader(urls.toArray(new URL[0]), Environment.CORE.getClassLoader()); - } - - /** - * Find the {@link ContextHandler} for the provided {@link Object} - * - * @param context the raw context object - * @return the {@link ContextHandler} for the context, or null if no ContextHandler associated with context. - */ - private ContextHandler getContextHandler(Object context) - { - if (context == null) - return null; - - if (context instanceof ContextHandler handler) - return handler; - - if (Supplier.class.isAssignableFrom(context.getClass())) - { - @SuppressWarnings("unchecked") - Supplier provider = (Supplier)context; - return provider.get(); - } - - if (LOG.isDebugEnabled()) - LOG.debug("Not a context {}", context); - return null; - } - - protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) - { - if (LOG.isDebugEnabled()) - LOG.debug("initializeContextHandler {}", contextHandler); - - assert contextHandler != null; - - if (contextHandler.getBaseResource() == null && Files.isDirectory(path)) - { - ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); - contextHandler.setBaseResource(resourceFactory.newResource(path)); - } - - // pass through properties as attributes directly - attributes.getAttributeNameSet().stream() - .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - .forEach((name) -> - { - Object value = attributes.getAttribute(name); - String key = name.substring(Deployable.ATTRIBUTE_PREFIX.length()); - if (LOG.isDebugEnabled()) - LOG.debug("Setting attribute [{}] to [{}] in context {}", key, value, contextHandler); - contextHandler.setAttribute(key, value); - }); - - String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); - if (StringUtil.isNotBlank(contextPath)) - { - if (LOG.isDebugEnabled()) - LOG.debug("Context {} initialized with contextPath: {}", contextHandler, contextPath); - contextHandler.setContextPath(contextPath); - } - } - - protected void initializeContextPath(ContextHandler contextHandler, Path path) - { - if (contextHandler == null) - return; - - // Strip any 3 char extension from non directories - String basename = FileID.getBasename(path); - String contextPath = basename; - - // special case of archive (or dir) named "root" is / context - if (contextPath.equalsIgnoreCase("root")) - { - contextPath = "/"; - } - // handle root with virtual host form - else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) - { - int dash = contextPath.indexOf('-'); - String virtual = contextPath.substring(dash + 1); - contextHandler.setVirtualHosts(Arrays.asList(virtual.split(","))); - contextPath = "/"; - } - - // Ensure "/" is Prepended to all context paths. - if (contextPath.charAt(0) != '/') - contextPath = "/" + contextPath; - - if (LOG.isDebugEnabled()) - LOG.debug("ContextHandler {} initialized with displayName: {}", contextHandler, basename); - contextHandler.setDisplayName(basename); - if (LOG.isDebugEnabled()) - LOG.debug("ContextHandler {} initialized with contextPath: {}", contextHandler, contextPath); - contextHandler.setContextPath(contextPath); - } - - /** - * Apply the main deployable heuristics referenced in the main javadoc - * for this class. - * - * @param unit the unit of deployment to interrogate. - * @return the main deployable path - */ - @Override - protected Path getMainDeploymentPath(Unit unit) - { - List livePaths = unit.getLivePaths(); - - // XML always win. - List xmls = livePaths.stream() - .filter(FileID::isXml) - .toList(); - if (xmls.size() == 1) - return xmls.get(0); - else if (xmls.size() > 1) - throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); - // WAR files are next. - List wars = livePaths.stream() - .filter(FileID::isWebArchive) - .toList(); - if (wars.size() == 1) - return wars.get(0); - else if (wars.size() > 1) - throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); - // Directories next. - List dirs = livePaths.stream() - .filter(Files::isDirectory) - .toList(); - if (dirs.size() == 1) - return dirs.get(0); - if (dirs.size() > 1) - throw new IllegalStateException("More than 1 Directory for deployable " + asStringList(dirs)); - - throw new IllegalStateException("Unable to determine main deployable for " + unit); - } - - @Override - public void unitsChanged(List units) - { - Set envsChanges = units.stream() - .map(Unit::getEnvironmentConfigName) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); - - if (envsChanges.isEmpty()) - { - // normal set of changes, no environment configuration changes - // present in the incoming list of changed units. - super.unitsChanged(units); - } - else - { - // ensure that the units reported to super.unitsChanged - // includes all (known) units that belong to the list of changed environments, - // while filtering out the environment config specific units here. - List unitsWithEnvChanges = new ArrayList<>(units.stream() - .filter(Unit::isDeployable) - .toList()); - - // Add any missing units into change set. - for (String envChange : envsChanges) - { - for (Unit unit : getUnitsForEnvironment(envChange)) - { - if (!unitsWithEnvChanges.contains(unit)) - { - // unit is not part of changeSet but is - // in need of a redeploy due to an environment configuration - // file being changed. - unit.setState(Unit.State.CHANGED); - unitsWithEnvChanges.add(unit); - } - } - } - - super.unitsChanged(unitsWithEnvChanges); - } - } - - /** - * Get the ClassLoader appropriate for applying Jetty XML. - * - * @param environment the environment to use - * @param xml the path to the XML - * @return the appropriate ClassLoader. - * @throws IOException if unable to create the ClassLoader - */ - private ClassLoader getXmlClassLoader(Environment environment, Path xml) throws IOException - { - if (Environment.CORE.equals(environment)) - { - // this XML belongs to a CORE deployment. - return findCoreContextClassLoader(xml); - } - else - { - return environment.getClassLoader(); - } - } - - private Attributes initAttributes(Environment environment, App app) throws IOException - { - Attributes attributes = new Attributes.Mapped(); - - // Start appAttributes with Attributes from Environment - environment.getAttributeNameSet().forEach((key) -> - attributes.setAttribute(key, environment.getAttribute(key))); - - String env = app.getEnvironmentName(); - - if (StringUtil.isNotBlank(env)) - { - // Load environment specific properties files - Path parent = app.getPath().getParent(); - Properties envProps = loadEnvironmentProperties(parent, env); - - envProps.stringPropertyNames().forEach( - k -> attributes.setAttribute(k, envProps.getProperty(k)) - ); - } - - // Overlay the app properties - app.getProperties().forEach(attributes::setAttribute); - - return attributes; - } - - /** - * Load all of the {@link Environment} specific {@code [-].properties} files - * found in the directory provided. - * - *

        - * All found properties files are first sorted by filename, then loaded one by one into - * a single {@link Properties} instance. - *

        - * - * @param directory the directory to load environment properties from. - * @param env the environment name - */ - private Properties loadEnvironmentProperties(Path directory, String env) throws IOException - { - Properties props = new Properties(); - List envPropertyFiles = new ArrayList<>(); - - // Get all environment specific properties files for this environment, - // order them according to the lexical ordering of the filenames - try (Stream paths = Files.list(directory)) - { - envPropertyFiles = paths.filter(Files::isRegularFile) - .filter(p -> FileID.isExtension(p, "properties")) - .filter(p -> - { - String name = p.getFileName().toString(); - return name.startsWith(env); - }).sorted().toList(); - } - - if (LOG.isDebugEnabled()) - LOG.debug("Environment property files {}", envPropertyFiles); - - // Load each *.properties file - for (Path file : envPropertyFiles) - { - Path resolvedFile = directory.resolve(file); - if (Files.exists(resolvedFile)) - { - Properties tmp = new Properties(); - try (InputStream stream = Files.newInputStream(resolvedFile)) - { - tmp.load(stream); - //put each property into our substitution pool - tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); - } - } - } - - return props; - } - - private void loadProperties(Environment environment, InputStream inputStream) throws IOException - { - Properties props = new Properties(); - props.load(inputStream); - props.stringPropertyNames().forEach((name) -> - environment.setAttribute(name, props.getProperty(name))); - } - - private static String asStringList(Collection paths) - { - return paths.stream() - .sorted(PathCollators.byName(true)) - .map(Path::toString) - .collect(Collectors.joining(", ", "[", "]")); - } - - /** - * Builder of a deployment configuration for a specific {@link Environment}. - * - *

        - * Results in {@link Attributes} for {@link Environment} containing the - * deployment configuration (as {@link Deployable} keys) that is applied to all deployable - * apps belonging to that {@link Environment}. - *

        - */ - public static class EnvironmentConfig - { - // Using setters in this class to allow jetty-xml - // syntax to skip setting of an environment attribute if property is unset, - // allowing the in code values to be same defaults as they are in embedded-jetty. - - private final Environment _environment; - - private EnvironmentConfig(Environment environment) - { - this._environment = environment; - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} attribute. - * - * @param configurations The configuration class names as a comma separated list - * @see Deployable#CONFIGURATION_CLASSES - */ - public void setConfigurationClasses(String configurations) - { - setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); - } - - /** - * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. - * - * @param configurations The configuration class names. - * @see Deployable#CONFIGURATION_CLASSES - */ - public void setConfigurationClasses(String[] configurations) - { - if (configurations == null) - _environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); - else - _environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); - } - - /** - * This is equivalent to setting the {@link Deployable#CONTAINER_SCAN_JARS} property. - * - * @param pattern The regex pattern to use when bytecode scanning container jars - * @see Deployable#CONTAINER_SCAN_JARS - */ - public void setContainerScanJarPattern(String pattern) - { - _environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); - } - - /** - * The name of the class that this environment uses to create {@link ContextHandler} - * instances (can be class that implements {@code java.util.function.Supplier} - * as well). - * - *

        - * This is the fallback class used, if the context class itself isn't defined by - * the web application being deployed. - *

        - * - * @param classname the classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT - */ - public void setContextHandlerClass(String classname) - { - _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); - } - - /** - * Set the defaultsDescriptor. - * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} attribute. - * - * @param defaultsDescriptor the defaultsDescriptor to set - * @see Deployable#DEFAULTS_DESCRIPTOR - */ - public void setDefaultsDescriptor(String defaultsDescriptor) - { - _environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); - } - - /** - * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} attribute. - * - * @param extractWars the extractWars to set - * @see Deployable#EXTRACT_WARS - */ - public void setExtractWars(boolean extractWars) - { - _environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); - } - - /** - * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} attribute. - * - * @param parentLoaderPriority the parentLoaderPriority to set - * @see Deployable#PARENT_LOADER_PRIORITY - */ - public void setParentLoaderPriority(boolean parentLoaderPriority) - { - _environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); - } - - /** - * This is equivalent to setting the {@link Deployable#SCI_EXCLUSION_PATTERN} property. - * - * @param pattern The regex pattern to exclude ServletContainerInitializers from executing - * @see Deployable#SCI_EXCLUSION_PATTERN - */ - public void setServletContainerInitializerExclusionPattern(String pattern) - { - _environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); - } - - /** - * This is equivalent to setting the {@link Deployable#SCI_ORDER} property. - * - * @param order The ordered list of ServletContainerInitializer classes to run - * @see Deployable#SCI_ORDER - */ - public void setServletContainerInitializerOrder(String order) - { - _environment.setAttribute(Deployable.SCI_ORDER, order); - } - - /** - * This is equivalent to setting the {@link Deployable#WEBINF_SCAN_JARS} property. - * - * @param pattern The regex pattern to use when bytecode scanning web-inf jars - * @see Deployable#WEBINF_SCAN_JARS - */ - public void setWebInfScanJarPattern(String pattern) - { - _environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); - } - } - - public static class Filter implements FilenameFilter - { - @Override - public boolean accept(File dir, String name) - { - if (dir == null || !dir.canRead()) - return false; - - Path path = dir.toPath().resolve(name); - - // We don't monitor subdirectories. - if (Files.isDirectory(path)) - return false; - - // Synthetic files (like consoles, printers, serial ports, etc) are ignored. - if (!Files.isRegularFile(path)) - return false; - - try - { - // ignore traditional "hidden" path entries. - if (name.startsWith(".")) - return false; - // ignore path tagged as hidden by FS - if (Files.isHidden(path)) - return false; - } - catch (IOException ignore) - { - // ignore - } - - // The filetypes that are monitored, and we want updates for when they change. - return FileID.isExtension(name, "jar", "war", "xml", "properties"); - } - } -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java new file mode 100644 index 000000000000..cd513809862d --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java @@ -0,0 +1,332 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +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.stream.Collectors; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.resource.PathCollators; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Default {@link App} represents all the components that make up + * a from-file-system App deployment that the {@link DefaultProvider} + * creates and uses. + */ +public class DefaultApp implements App +{ + public enum State + { + UNCHANGED, + ADDED, + CHANGED, + REMOVED + } + + private static final Logger LOG = LoggerFactory.getLogger(DefaultApp.class); + private final String name; + private final Map paths = new HashMap<>(); + private final Attributes attributes = new Attributes.Mapped(); + private State state; + private ContextHandler contextHandler; + + public DefaultApp(String name) + { + this.name = name; + this.state = calcState(); + } + + private static String asStringList(Collection paths) + { + return paths.stream() + .sorted(PathCollators.byName(true)) + .map(Path::toString) + .collect(Collectors.joining(", ", "[", "]")); + } + + @Override + public boolean equals(Object o) + { + if (o == null || getClass() != o.getClass()) + return false; + DefaultApp that = (DefaultApp)o; + return Objects.equals(name, that.name); + } + + public Attributes getAttributes() + { + return this.attributes; + } + + @Override + public ContextHandler getContextHandler() + { + return contextHandler; + } + + public void setContextHandler(ContextHandler contextHandler) + { + this.contextHandler = contextHandler; + } + + public String getEnvironmentName() + { + return (String)getAttributes().getAttribute(Deployable.ENVIRONMENT); + } + + public void setEnvironmentName(String name) + { + getAttributes().setAttribute(Deployable.ENVIRONMENT, name); + } + + /** + * Get the main path used for deployment. + *

        + * Applies the heuristics reference in the main + * javadoc for {@link DefaultProvider} + *

        + * + * @return the main deployable path + */ + public Path getMainPath() + { + List livePaths = paths + .entrySet() + .stream() + .filter((e) -> e.getValue() != State.REMOVED) + .map(Map.Entry::getKey) + .sorted(PathCollators.byName(true)) + .toList(); + + if (livePaths.isEmpty()) + return null; + + // XML always win. + List xmls = livePaths.stream() + .filter(FileID::isXml) + .toList(); + if (xmls.size() == 1) + return xmls.get(0); + else if (xmls.size() > 1) + throw new IllegalStateException("More than 1 XML for deployable " + asStringList(xmls)); + + // WAR files are next. + List wars = livePaths.stream() + .filter(FileID::isWebArchive) + .toList(); + if (wars.size() == 1) + return wars.get(0); + else if (wars.size() > 1) + throw new IllegalStateException("More than 1 WAR for deployable " + asStringList(wars)); + + // Directories next. + List dirs = livePaths.stream() + .filter(Files::isDirectory) + .toList(); + if (dirs.size() == 1) + return dirs.get(0); + if (dirs.size() > 1) + throw new IllegalStateException("More than 1 Directory for deployable " + asStringList(dirs)); + + LOG.warn("Unable to determine main deployable for {}", this); + return null; + } + + @Override + public String getName() + { + return name; + } + + public Map getPaths() + { + return paths; + } + + public State getState() + { + return state; + } + + public void setState(State state) + { + this.state = state; + } + + @Override + public int hashCode() + { + return Objects.hashCode(name); + } + + /** + * Load all {@code properties} files belonging to this DefaultApp + * into the {@link Attributes} for this App. + * + * @see #getAttributes() + */ + public void loadProperties() + { + // look for properties file for main basename. + String propFilename = String.format("%s.properties", getName()); + List propFiles = paths.keySet().stream() + .filter(Files::isRegularFile) + .filter(p -> p.getFileName().toString().equalsIgnoreCase(propFilename)) + .sorted(PathCollators.byName(true)) + .toList(); + + if (propFiles.isEmpty()) + { + // No properties file found + return; + } + + if (propFiles.size() > 1) + { + LOG.warn("Multiple matching files with name [{}]: {}", propFilename, + asStringList(propFiles)); + } + + for (Path propFile : propFiles) + { + try (InputStream inputStream = Files.newInputStream(propFile)) + { + Properties props = new Properties(); + props.load(inputStream); + props.stringPropertyNames().forEach( + (name) -> getAttributes().setAttribute(name, props.getProperty(name))); + } + catch (IOException e) + { + LOG.warn("Unable to read properties file: {}", propFile, e); + } + } + } + + public void putPath(Path path, State state) + { + this.paths.put(path, state); + setState(calcState()); + } + + public void resetStates() + { + // Drop paths that were removed. + List removedPaths = paths.entrySet() + .stream().filter(e -> e.getValue() == State.REMOVED) + .map(Map.Entry::getKey) + .toList(); + for (Path removedPath : removedPaths) + { + paths.remove(removedPath); + } + // Set all remaining path states to UNCHANGED + paths.replaceAll((p, v) -> State.UNCHANGED); + state = calcState(); + } + + @Override + public String toString() + { + StringBuilder str = new StringBuilder("%s@%x".formatted(this.getClass().getSimpleName(), hashCode())); + str.append("[").append(name); + str.append("|").append(getState()); + str.append(", env=").append(getEnvironmentName()); + str.append(", paths="); + str.append(paths.entrySet().stream() + .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ", "[", "]")) + ); + str.append(", contextHandler="); + if (contextHandler == null) + str.append(""); + else + str.append(contextHandler); + str.append("]"); + return str.toString(); + } + + /** + *

        + * Calculate the State of the overall Unit based on the States in the Paths. + *

        + *
        + *
        UNCHANGED
        + *
        All Path states are in UNCHANGED state
        + *
        ADDED
        + *
        All Path states are in ADDED state
        + *
        CHANGED
        + *
        At least one Path state is CHANGED, or there is a variety of states
        + *
        REMOVED
        + *
        All Path states are in REMOVED state, or there are no Paths being tracked
        + *
        + * + * @return the state of the Unit. + */ + private DefaultApp.State calcState() + { + if (paths.isEmpty()) + return DefaultApp.State.REMOVED; + + // Calculate state of unit from Path states. + State ret = null; + for (State pathState : paths.values()) + { + switch (pathState) + { + case UNCHANGED -> + { + if (ret == null) + ret = State.UNCHANGED; + else if (ret != State.UNCHANGED) + ret = State.CHANGED; + } + case ADDED -> + { + if (ret == null) + ret = State.ADDED; + else if (ret == State.UNCHANGED || ret == State.REMOVED) + ret = State.ADDED; + } + case CHANGED -> + { + ret = State.CHANGED; + } + case REMOVED -> + { + if (ret == null) + ret = State.REMOVED; + else if (ret != State.REMOVED) + ret = State.CHANGED; + } + } + } + return ret != null ? ret : DefaultApp.State.UNCHANGED; + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java new file mode 100644 index 000000000000..c2cd79752539 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -0,0 +1,449 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultContextHandlerFactory +{ + private static final Logger LOG = LoggerFactory.getLogger(DefaultContextHandlerFactory.class); + private static final String ENV_XML_PATHS = "jetty.deploy.defaultApp.envXmls"; + + private String _defaultEnvironmentName; + + private static Map asProperties(Attributes attributes) + { + Map props = new HashMap<>(); + attributes.getAttributeNameSet().forEach((name) -> + { + Object value = attributes.getAttribute(name); + String key = name.startsWith(Deployable.ATTRIBUTE_PREFIX) + ? name.substring(Deployable.ATTRIBUTE_PREFIX.length()) + : name; + props.put(key, Objects.toString(value)); + }); + return props; + } + + public static List getEnvironmentXmlPaths(Attributes attributes) + { + //noinspection unchecked + return (List)attributes.getAttribute(ENV_XML_PATHS); + } + + public static void setEnvironmentXmlPaths(Attributes attributes, List paths) + { + attributes.setAttribute(ENV_XML_PATHS, paths); + } + + /** + * Get the default {@link Environment} name for discovered web applications that + * do not declare the {@link Environment} that they belong to. + * + *

        + * Falls back to {@link Environment#getAll()} list, and returns + * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} + *

        + * + * @return the default environment name. + */ + public String getDefaultEnvironmentName() + { + if (_defaultEnvironmentName == null) + { + return Environment.getAll().stream() + .map(Environment::getName) + .max(Deployable.ENVIRONMENT_COMPARATOR) + .orElse(null); + } + return _defaultEnvironmentName; + } + + public void setDefaultEnvironmentName(String name) + { + this._defaultEnvironmentName = name; + } + + public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app, Attributes deployAttributes) throws Exception + { + Path mainPath = app.getMainPath(); + if (mainPath == null) + { + LOG.warn("Unable to create ContextHandler for app with no main path: {}", app); + return null; + } + + // Resolve real file (hopefully eliminating alias issues) + mainPath = mainPath.toRealPath(); + + // Can happen if the file existed when notified by scanner (as either an ADD or CHANGE), + // and then the file was deleted before reaching this code. + if (!Files.exists(mainPath)) + throw new IllegalStateException("App path does not exist " + mainPath); + + String envName = app.getEnvironmentName(); + if (StringUtil.isBlank(envName)) + { + envName = getDefaultEnvironmentName(); + app.setEnvironmentName(envName); + } + + // Verify that referenced Environment even exists. + Environment environment = Environment.get(envName); + + if (environment == null) + { + LOG.warn("Environment [{}] does not exist (referenced in app [{}]). The available environments are: {}", + app.getEnvironmentName(), + app, + Environment.getAll().stream() + .map(Environment::getName) + .collect(Collectors.joining(", ", "[", "]")) + ); + return null; + } + + if (LOG.isDebugEnabled()) + LOG.debug("createContextHandler {} in {}", app, environment.getName()); + + ClassLoader old = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(environment.getClassLoader()); + + /* + * The process now is to figure out the context object to use. + * This can come from a number of places. + * 1. If an XML deployable, this is the entry. + * 2. If another deployable (like a web archive, or directory), then check attributes. + * a. use the app attributes to figure out the context handler class. + * b. use the environment attributes default context handler class. + */ + Object context = newContextInstance(provider, environment, app, deployAttributes, mainPath); + if (context == null) + throw new IllegalStateException("unable to create ContextHandler for " + app); + + if (LOG.isDebugEnabled()) + LOG.debug("Context {} created from app {}", context.getClass().getName(), app); + + // Apply environment properties and XML to context + if (applyEnvironmentXml(provider, app, context, environment, deployAttributes)) + { + // If an XML deployable, apply full XML over environment XML changes + if (FileID.isXml(mainPath)) + context = applyXml(provider, context, mainPath, environment, deployAttributes); + } + + // Set a backup value for the path to the war in case it hasn't already been set + // via a different means. This is especially important for a deployable App + // that is only a .war file (no XML). The eventual WebInfConfiguration + // will use this attribute. + app.getAttributes().setAttribute(Deployable.WAR, mainPath.toString()); + + // Initialize any deployable + if (context instanceof Deployable deployable) + deployable.initializeDefaults(deployAttributes); + + return getContextHandler(context); + } + finally + { + Thread.currentThread().setContextClassLoader(old); + } + } + + protected Object applyXml(DefaultProvider provider, Object context, Path xml, Environment environment, Attributes attributes) throws Exception + { + if (!FileID.isXml(xml)) + return null; + + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + XmlConfiguration xmlc = new XmlConfiguration(resourceFactory.newResource(xml), null, asProperties(attributes)) + { + @Override + public void initializeDefaults(Object context) + { + super.initializeDefaults(context); + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Not a ContextHandler: Not initializing Context {}", context); + } + else + { + DefaultContextHandlerFactory.this.initializeContextPath(contextHandler, xml); + DefaultContextHandlerFactory.this.initializeContextHandler(contextHandler, xml, attributes); + } + } + }; + + xmlc.getIdMap().put("Environment", environment.getName()); + xmlc.setJettyStandardIdsAndProperties(provider.getDeploymentManager().getServer(), xml); + + // Put all Environment attributes into XmlConfiguration as properties that can be used. + attributes.getAttributeNameSet() + .stream() + .filter(k -> !k.startsWith("jetty.home") && + !k.startsWith("jetty.base") && + !k.startsWith("jetty.webapps")) + .forEach(k -> + { + String v = Objects.toString(attributes.getAttribute(k)); + xmlc.getProperties().put(k, v); + }); + + // Run configure against appropriate classloader. + ClassLoader xmlClassLoader = environment.getClassLoader(); + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(xmlClassLoader); + + try + { + // Create or configure the context + if (context == null) + return xmlc.configure(); + + return xmlc.configure(context); + } + finally + { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + } + + protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) + { + if (LOG.isDebugEnabled()) + LOG.debug("initializeContextHandler {}", contextHandler); + + assert contextHandler != null; + + if (contextHandler.getBaseResource() == null && Files.isDirectory(path)) + { + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); + } + + // pass through properties as attributes directly + attributes.getAttributeNameSet().stream() + .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) + .forEach((name) -> + { + Object value = attributes.getAttribute(name); + String key = name.substring(Deployable.ATTRIBUTE_PREFIX.length()); + if (LOG.isDebugEnabled()) + LOG.debug("Setting attribute [{}] to [{}] in context {}", key, value, contextHandler); + contextHandler.setAttribute(key, value); + }); + + String contextPath = (String)attributes.getAttribute(Deployable.CONTEXT_PATH); + if (StringUtil.isNotBlank(contextPath)) + { + if (LOG.isDebugEnabled()) + LOG.debug("Context {} initialized with contextPath: {}", contextHandler, contextPath); + contextHandler.setContextPath(contextPath); + } + } + + protected void initializeContextPath(ContextHandler contextHandler, Path path) + { + if (contextHandler == null) + return; + + // Strip any 3 char extension from non directories + String basename = FileID.getBasename(path); + String contextPath = basename; + + // special case of archive (or dir) named "root" is / context + if (contextPath.equalsIgnoreCase("root")) + { + contextPath = "/"; + } + // handle root with virtual host form + else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) + { + int dash = contextPath.indexOf('-'); + String virtual = contextPath.substring(dash + 1); + contextHandler.setVirtualHosts(Arrays.asList(virtual.split(","))); + contextPath = "/"; + } + + // Ensure "/" is Prepended to all context paths. + if (contextPath.charAt(0) != '/') + contextPath = "/" + contextPath; + + if (LOG.isDebugEnabled()) + LOG.debug("ContextHandler {} initialized with displayName: {}", contextHandler, basename); + contextHandler.setDisplayName(basename); + if (LOG.isDebugEnabled()) + LOG.debug("ContextHandler {} initialized with contextPath: {}", contextHandler, contextPath); + contextHandler.setContextPath(contextPath); + } + + /** + * Apply optional environment specific XML to context. + * + * @param provider the DefaultProvider responsible for this context creation + * @param app the default app + * @param context the context to apply environment specific behavior to + * @param environment the environment to use + * @param attributes the attributes used to deploy the app + * @return true if environment specific XML was applied. + * @throws Exception if unable to apply environment configuration. + */ + private boolean applyEnvironmentXml(DefaultProvider provider, DefaultApp app, Object context, Environment environment, Attributes attributes) throws Exception + { + // Collect the optional environment context xml files. + // Order them according to the name of their property key names. + List sortedEnvXmlPaths = getEnvironmentXmlPaths(attributes); + + if (sortedEnvXmlPaths == null || sortedEnvXmlPaths.isEmpty()) + // nothing to do here + return false; + + boolean xmlApplied = false; + + // apply each context environment xml file + for (Path envXmlPath : sortedEnvXmlPaths) + { + if (LOG.isDebugEnabled()) + LOG.debug("Applying environment specific context file {}", envXmlPath); + context = applyXml(provider, context, envXmlPath, environment, attributes); + xmlApplied = true; + } + + return xmlApplied; + } + + /** + * Find the {@link ContextHandler} for the provided {@link Object} + * + * @param context the raw context object + * @return the {@link ContextHandler} for the context, or null if no ContextHandler associated with context. + */ + private ContextHandler getContextHandler(Object context) + { + if (context == null) + return null; + + if (context instanceof ContextHandler handler) + return handler; + + if (Supplier.class.isAssignableFrom(context.getClass())) + { + @SuppressWarnings("unchecked") + Supplier provider = (Supplier)context; + return provider.get(); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Not a context {}", context); + return null; + } + + /** + * Initialize a new Context object instance. + * + *

        + * The search order is: + *

        + *
          + *
        1. If app attribute {@link Deployable#CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
        2. + *
        3. If App deployable path is XML, apply XML {@code }
        4. + *
        5. Fallback to environment attribute {@link Deployable#CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
        6. + *
        + * + * @param environment the environment context applies to + * @param app the App for the context + * @param attributes the Attributes used to deploy the App + * @param path the path of the deployable + * @return the Context Object. + * @throws Exception if unable to create Object instance. + */ + private Object newContextInstance(DefaultProvider provider, Environment environment, App app, Attributes attributes, Path path) throws Exception + { + Object context = newInstance((String)attributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS)); + if (context != null) + { + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + + initializeContextPath(contextHandler, path); + initializeContextHandler(contextHandler, path, attributes); + return context; + } + + if (FileID.isXml(path)) + { + context = applyXml(provider, null, path, environment, attributes); + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + return context; + } + + // fallback to default from environment. + context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); + + if (context != null) + { + ContextHandler contextHandler = getContextHandler(context); + if (contextHandler == null) + throw new IllegalStateException("Unknown context type of " + context); + + initializeContextPath(contextHandler, path); + initializeContextHandler(contextHandler, path, attributes); + return context; + } + + return null; + } + + private Object newInstance(String className) throws Exception + { + if (StringUtil.isBlank(className)) + return null; + if (LOG.isDebugEnabled()) + LOG.debug("Attempting to load class {}", className); + Class clazz = Loader.loadClass(className); + if (clazz == null) + return null; + return clazz.getConstructor().newInstance(); + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java new file mode 100644 index 000000000000..f6ff93c8fd01 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -0,0 +1,1054 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.PathCollators; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

        Default Jetty Environment WebApp Hot Deployment Provider.

        + * + *

        This provider scans one or more directories (typically "webapps") for contexts to + * deploy, which may be:

        + *
          + *
        • A standard WAR file (must end in ".war")
        • + *
        • A directory containing an expanded WAR file
        • + *
        • A directory containing static content
        • + *
        • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
        • + *
        + *

        To avoid double deployments and allow flexibility of the content of the scanned directories, the provider + * implements some heuristics to ignore some files found in the scans: + *

        + *
          + *
        • Hidden files (starting with {@code "."}) are ignored
        • + *
        • Directories with names ending in {@code ".d"} are ignored
        • + *
        • Property files with names ending in {@code ".properties"} are not deployed.
        • + *
        • If a directory and a WAR file exist (eg: {@code foo/} and {@code foo.war}) then the directory is assumed to be + * the unpacked WAR and only the WAR file is deployed (which may reuse the unpacked directory)
        • + *
        • If a directory and a matching XML file exist (eg: {@code foo/} and {@code foo.xml}) then the directory is assumed to be + * an unpacked WAR and only the XML file is deployed (which may use the directory in its configuration)
        • + *
        • If a WAR file and a matching XML file exist (eg: {@code foo.war} and {@code foo.xml}) then the WAR file is assumed to + * be configured by the XML file and only the XML file is deployed. + *
        + *

        For XML configured contexts, the following is available.

        + *
          + *
        • The XML Object ID Map will have a reference to the {@link Server} instance via the ID name {@code "Server"}
        • + *
        • The Default XML Properties are populated from a call to {@link XmlConfiguration#setJettyStandardIdsAndProperties(Object, Path)} (for things like {@code jetty.home} and {@code jetty.base})
        • + *
        • An extra XML Property named {@code "jetty.webapps"} is available, and points to the monitored path.
        • + *
        + *

        + * Context Deployment properties will be initialized with: + *

        + *
          + *
        • The properties set on the application via embedded calls modifying {@link DefaultApp#getAttributes()}
        • + *
        • The app specific properties file {@code webapps/.properties}
        • + *
        • The environment specific properties file {@code webapps/[-zzz].properties}
        • + *
        • The {@link Attributes} from the {@link Environment}
        • + *
        + * + *

        + * To configure Environment specific deployment {@link Attributes}, + * either set the appropriate {@link Deployable} attribute via {@link Attributes#setAttribute(String, Object)}, + * or use the convenience class {@link EnvironmentConfig}. + *

        + * + *
        {@code
        + * DefaultDeploymentAppProvider provider = new DefaultDeploymentAppProvider();
        + * EnvironmentConfig env10config = provider.configureEnvironment("ee10");
        + * env10config.setExtractWars(true);
        + * env10config.setParentLoaderPriority(false);
        + * }
        + */ +@ManagedObject("Provider for start-up deployment of webapps based on presence in directory") +public class DefaultProvider extends ContainerLifeCycle implements AppProvider, Scanner.ChangeSetListener +{ + private static final Logger LOG = LoggerFactory.getLogger(DefaultProvider.class); + + private final FilenameFilter filenameFilter; + private final List monitoredDirs = new CopyOnWriteArrayList<>(); + + private Map apps = new HashMap<>(); + private DeploymentManager deploymentManager; + private Comparator actionComparator = new DeployActionComparator(); + private DefaultContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); + private Path environmentsDir; + private int scanInterval = 10; + private Scanner scanner; + private boolean useRealPaths; + private boolean deferInitialScan = false; + + public DefaultProvider() + { + this(new MonitoredPathFilter()); + } + + public DefaultProvider(FilenameFilter filter) + { + filenameFilter = filter; + setScanInterval(0); + } + + private static String asStringList(Collection paths) + { + return paths.stream() + .sorted(PathCollators.byName(true)) + .map(Path::toString) + .collect(Collectors.joining(", ", "[", "]")); + } + + /** + * @param dir Directory to scan for deployable artifacts + */ + public void addMonitoredDirectory(Path dir) + { + if (isStarted()) + throw new IllegalStateException("Unable to add monitored directory while running"); + monitoredDirs.add(Objects.requireNonNull(dir)); + } + + public void addScannerListener(Scanner.Listener listener) + { + scanner.addListener(listener); + } + + /** + * Configure the Environment specific Deploy settings. + * + * @param name the name of the environment. + * @return the deployment configuration for the {@link Environment}. + */ + public EnvironmentConfig configureEnvironment(String name) + { + return new EnvironmentConfig(Environment.ensure(name)); + } + + public Comparator getActionComparator() + { + return actionComparator; + } + + public void setActionComparator(Comparator actionComparator) + { + this.actionComparator = actionComparator; + } + + public Collection getApps() + { + return apps.values(); + } + + public DefaultContextHandlerFactory getContextHandlerFactory() + { + return contextHandlerFactory; + } + + public void setContextHandlerFactory(DefaultContextHandlerFactory contextHandlerFactory) + { + this.contextHandlerFactory = contextHandlerFactory; + } + + /** + * Get the default {@link Environment} name for discovered web applications that + * do not declare the {@link Environment} that they belong to. + * + *

        + * Falls back to {@link Environment#getAll()} list, and returns + * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} + *

        + * + * @return the default environment name. + */ + public String getDefaultEnvironmentName() + { + return this.contextHandlerFactory.getDefaultEnvironmentName(); + } + + public void setDefaultEnvironmentName(String name) + { + this.contextHandlerFactory.setDefaultEnvironmentName(name); + } + + /** + * Get the deploymentManager. + * + * @return the deploymentManager + */ + public DeploymentManager getDeploymentManager() + { + return deploymentManager; + } + + @Override + public void setDeploymentManager(DeploymentManager deploymentManager) + { + this.deploymentManager = deploymentManager; + } + + public Path getEnvironmentsDirectory() + { + return environmentsDir; + } + + public void setEnvironmentsDirectory(Path dir) + { + if (isStarted()) + throw new IllegalStateException("Unable to add environments directory while running"); + environmentsDir = dir; + } + + public List getMonitoredDirectories() + { + return monitoredDirs; + } + + public void setMonitoredDirectories(Collection directories) + { + if (isStarted()) + throw new IllegalStateException("Unable to add monitored directories while running"); + + for (Path dir : directories) + { + monitoredDirs.add(Objects.requireNonNull(dir)); + } + } + + @ManagedAttribute("scanning interval to detect changes which need reloaded") + public int getScanInterval() + { + return scanInterval; + } + + public void setScanInterval(int scanInterval) + { + this.scanInterval = scanInterval; + } + + public Comparator getUnitComparator() + { + return actionComparator; + } + + public void setUnitComparator(Comparator comparator) + { + this.actionComparator = comparator; + } + + /** + * Test if initial scan should be deferred. + * + * @return true if initial scan is deferred, false to have initial scan occur on startup of ScanningAppProvider. + */ + public boolean isDeferInitialScan() + { + return deferInitialScan; + } + + /** + * Flag to control initial scan behavior. + * + *
          + *
        • {@code true} - to have initial scan deferred until the {@link Server} component + * has reached it's STARTED state.
          + * Note: any failures in a deployment will not fail the Server startup in this mode.
        • + *
        • {@code false} - (default value) to have initial scan occur as normal on + * ScanningAppProvider startup.
        • + *
        + * + * @param defer true to defer initial scan, false to have initial scan occur on startup of ScanningAppProvider. + */ + public void setDeferInitialScan(boolean defer) + { + deferInitialScan = defer; + } + + /** + * @return True if the real path of the scanned files should be used for deployment. + */ + public boolean isUseRealPaths() + { + return useRealPaths; + } + + /** + * @param useRealPaths True if the real path of the scanned files should be used for deployment. + */ + public void setUseRealPaths(boolean useRealPaths) + { + this.useRealPaths = useRealPaths; + } + + /** + * This is the listener event for Scanner to report changes. + * + * @param changeSet the changeset from the Scanner. + */ + @Override + public void pathsChanged(Map changeSet) + { + Objects.requireNonNull(changeSet); + if (LOG.isDebugEnabled()) + { + LOG.debug("pathsChanged: {}", + changeSet.entrySet() + .stream() + .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) + .collect(Collectors.joining(", ", "[", "]")) + ); + } + + Set changedBaseNames = new HashSet<>(); + Set availableEnvironmentNames = Environment.getAll().stream() + .map(Environment::getName).collect(Collectors.toUnmodifiableSet()); + Set changedEnvironments = new HashSet<>(); + + for (Map.Entry entry : changeSet.entrySet()) + { + Path path = entry.getKey(); + DefaultApp.State state = switch (entry.getValue()) + { + case ADDED -> + { + yield DefaultApp.State.ADDED; + } + case CHANGED -> + { + yield DefaultApp.State.CHANGED; + } + case REMOVED -> + { + yield DefaultApp.State.REMOVED; + } + }; + + // Using lower-case as defined by System Locale, as the files themselves from System FS. + String basename = FileID.getBasename(path).toLowerCase(); + + if (isMonitoredPath(path)) + { + // we have a normal path entry + changedBaseNames.add(basename); + DefaultApp app = apps.computeIfAbsent(basename, DefaultApp::new); + app.putPath(path, state); + } + else if (isEnvironmentConfigPath(path)) + { + String envname = null; + for (String name : availableEnvironmentNames) + { + if (basename.startsWith(name)) + envname = name; + } + if (StringUtil.isBlank(envname)) + { + LOG.warn("Unable to determine Environment for file: {}", path); + continue; + } + changedEnvironments.add(envname); + } + } + + // Now we know the DefaultApp instances that are changed by the incoming + // Scanner changes alone. + + List changedApps = changedBaseNames + .stream() + .map(name -> apps.get(name)) + .collect(Collectors.toList()); + + if (changedEnvironments.isEmpty()) + { + // We have a set of changes, with no environment configuration + // changes present. + List actions = buildActionList(changedApps); + performActions(actions); + } + else + { + // We have incoming environment configuration changes + // We need to add any missing DefaultApp that have changed + // due to incoming environment configuration changes. + + for (String changedEnvName : changedEnvironments) + { + // Add any missing apps to changedApps list + for (DefaultApp app : apps.values()) + { + if (changedBaseNames.contains(app.getName())) + continue; // skip app that's already in the change list. + + if (changedEnvName.equalsIgnoreCase(app.getEnvironmentName())) + { + if (app.getState() == DefaultApp.State.UNCHANGED) + app.setState(DefaultApp.State.CHANGED); + changedApps.add(app); + changedBaseNames.add(app.getName()); + } + } + + // Load any Environment properties files into Environment attributes. + try + { + Properties envProps = loadEnvironmentProperties(changedEnvName); + Environment environment = Environment.get(changedEnvName); + envProps.stringPropertyNames().forEach( + k -> environment.setAttribute(k, envProps.getProperty(k)) + ); + } + catch (IOException e) + { + if (LOG.isDebugEnabled()) + LOG.debug("Unable to load environment properties for environment [{}]", changedEnvName, e); + } + } + + List actions = buildActionList(changedApps); + performActions(actions); + } + } + + @ManagedOperation(value = "Scan the monitored directories", impact = "ACTION") + public void scan() + { + LOG.info("Performing scan of monitored directories: {}", + monitoredDirs.stream() + .map(Path::toUri) + .map(URI::toASCIIString) + .collect(Collectors.joining(", ", "[", "]")) + ); + scanner.nudge(); + } + + @Override + public String toString() + { + return String.format("%s@%x[dirs=%s]", this.getClass(), hashCode(), monitoredDirs); + } + + protected List buildActionList(List changedApps) + { + if (LOG.isDebugEnabled()) + LOG.debug("buildActionList: {}", changedApps); + + List actions = new ArrayList<>(); + for (DefaultApp app : changedApps) + { + if (LOG.isDebugEnabled()) + LOG.debug("changed app: {}", app); + + switch (app.getState()) + { + case ADDED -> + { + actions.add(new DeployAction(DeployAction.Type.ADD, app)); + } + case CHANGED -> + { + actions.add(new DeployAction(DeployAction.Type.REMOVE, app)); + actions.add(new DeployAction(DeployAction.Type.ADD, app)); + } + case REMOVED -> + { + actions.add(new DeployAction(DeployAction.Type.REMOVE, app)); + } + } + } + return sortActions(actions); + } + + @Override + protected void doStart() throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("{}.doStart()", this.getClass().getSimpleName()); + if (monitoredDirs.isEmpty()) + throw new IllegalStateException("No monitored dir specified"); + + LOG.info("Deployment monitor in {} at intervals {}s", monitoredDirs, getScanInterval()); + + Predicate validDir = (path) -> + { + if (!Files.exists(path)) + { + LOG.warn("Does not exist: {}", path); + return false; + } + + if (!Files.isDirectory(path)) + { + LOG.warn("Is not a directory: {}", path); + return false; + } + + return true; + }; + + List dirs = new ArrayList<>(); + for (Path dir : monitoredDirs) + { + if (validDir.test(dir)) + dirs.add(dir); + } + + if (environmentsDir != null) + { + if (validDir.test(environmentsDir)) + dirs.add(environmentsDir); + } + + scanner = new Scanner(null, useRealPaths); + scanner.setScanDirs(dirs); + scanner.setScanInterval(scanInterval); + scanner.setFilenameFilter(filenameFilter); + scanner.setReportDirs(true); + scanner.setScanDepth(1); + scanner.addListener(this); + scanner.setReportExistingFilesOnStartup(true); + scanner.setAutoStartScanning(!deferInitialScan); + addBean(scanner); + + if (isDeferInitialScan()) + { + // Setup listener to wait for Server in STARTED state, which + // triggers the first scan of the monitored directories + getDeploymentManager().getServer().addEventListener( + new LifeCycle.Listener() + { + @Override + public void lifeCycleStarted(LifeCycle event) + { + if (event instanceof Server) + { + if (LOG.isDebugEnabled()) + LOG.debug("Triggering Deferred Scan of {}", dirs); + scanner.startScanning(); + } + } + }); + } + + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + super.doStop(); + if (scanner != null) + { + removeBean(scanner); + scanner.removeListener(this); + scanner = null; + } + } + + protected boolean exists(String path) + { + return scanner.exists(path); + } + + protected List getAppsInEnvironment(String envName) + { + return apps.values().stream() + .filter((app) -> envName.equals(app.getEnvironmentName())) + .toList(); + } + + protected boolean isEnvironmentConfigPath(Path path) + { + if (environmentsDir == null) + return false; + + return isSameDir(environmentsDir, path.getParent()); + } + + protected boolean isMonitoredPath(Path path) + { + Path parentDir = path.getParent(); + for (Path monitoredDir : monitoredDirs) + { + if (isSameDir(monitoredDir, parentDir)) + return true; + } + return false; + } + + protected boolean isSameDir(Path dirA, Path dirB) + { + try + { + return Files.isSameFile(dirA, dirB); + } + catch (IOException e) + { + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring: Unable to use Files.isSameFile({}, {})", dirA, dirB, e); + return false; + } + } + + protected void performActions(List actions) + { + for (DeployAction step : actions) + { + try + { + switch (step.getType()) + { + case REMOVE -> + { + apps.remove(step.getName()); + deploymentManager.removeApp(step.getApp()); + } + case ADD -> + { + // Load .properties into app. + step.getApp().loadProperties(); + + // Ensure Environment name is set + String appEnvironment = step.getApp().getEnvironmentName(); + if (StringUtil.isBlank(appEnvironment)) + appEnvironment = getDefaultEnvironmentName(); + step.getApp().setEnvironmentName(appEnvironment); + + // Create a new Attributes for the App, which is the + // combination of Environment Attributes with App Attributes overlaying them. + Environment environment = Environment.get(appEnvironment); + Attributes deployAttributes = initAttributes(environment, step.getApp()); + + // Ensure that Environment configuration XMLs are listed in deployAttributes + List envXmlPaths = findEnvironmentXmlPaths(deployAttributes); + envXmlPaths.sort(PathCollators.byName(true)); + DefaultContextHandlerFactory.setEnvironmentXmlPaths(deployAttributes, envXmlPaths); + + // Create the Context Handler + ContextHandler contextHandler = getContextHandlerFactory().newContextHandler(this, step.getApp(), deployAttributes); + step.getApp().setContextHandler(contextHandler); + + // Introduce the App to the DeploymentManager + deploymentManager.addApp(step.getApp()); + } + } + } + catch (Throwable t) + { + LOG.warn("Failed to to perform action {} on {}", step.getType(), step.getApp(), t); + } + finally + { + step.getApp().resetStates(); + } + } + } + + protected List sortActions(List actions) + { + Comparator deployActionComparator = getActionComparator(); + if (deployActionComparator != null) + actions.sort(deployActionComparator); + return actions; + } + + private void copyAttributes(Attributes sourceAttributes, Attributes destAttributes) + { + sourceAttributes.getAttributeNameSet().forEach((key) -> + destAttributes.setAttribute(key, sourceAttributes.getAttribute(key))); + } + + private List findEnvironmentXmlPaths(Attributes deployAttributes) + { + List rawEnvXmlPaths = deployAttributes.getAttributeNameSet() + .stream() + .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) + .map(k -> Path.of((String)deployAttributes.getAttribute(k))) + .toList(); + + List ret = new ArrayList<>(); + for (Path rawPath : rawEnvXmlPaths) + { + if (Files.exists(rawPath)) + { + if (Files.isRegularFile(rawPath)) + { + // just add it, nothing else to do. + if (rawPath.isAbsolute()) + ret.add(rawPath); + else + ret.add(rawPath.toAbsolutePath()); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring non-file reference to environment xml: {}", rawPath); + } + } + else if (!rawPath.isAbsolute()) + { + // we have a relative defined path, try to resolve it from known locations + if (LOG.isDebugEnabled()) + LOG.debug("Resolving environment xml path relative reference: {}", rawPath); + boolean found = false; + for (Path monitoredDir : getMonitoredDirectories()) + { + Path resolved = monitoredDir.resolve(rawPath); + if (Files.isRegularFile(resolved)) + { + found = true; + // add resolved path + ret.add(resolved); + } + else + { + // try resolve from parent (this is backward compatible with 12.0.0) + resolved = monitoredDir.getParent().resolve(rawPath); + if (Files.isRegularFile(resolved)) + { + found = true; + // add resolved path + ret.add(resolved); + } + } + } + if (!found && LOG.isDebugEnabled()) + LOG.debug("Ignoring relative environment xml path that doesn't exist: {}", rawPath); + } + } + + return ret; + } + + private Attributes initAttributes(Environment environment, DefaultApp app) throws IOException + { + Attributes attributes = new Attributes.Mapped(); + + // Grab Environment attributes first + copyAttributes(environment, attributes); + + // Overlay the app attributes + copyAttributes(app.getAttributes(), attributes); + + // The now merged attributes + return attributes; + } + + /** + * Load all of the {@link Environment} specific {@code [-].properties} files + * found in the directory provided. + * + *

        + * All found properties files are first sorted by filename, then loaded one by one into + * a single {@link Properties} instance. + *

        + * + * @param env the environment name + */ + private Properties loadEnvironmentProperties(String env) throws IOException + { + Properties props = new Properties(); + + Path dir = getEnvironmentsDirectory(); + if (dir == null) + { + // nothing to load + return props; + } + + if (!Files.isDirectory(dir)) + { + LOG.warn("Not an environments directory: {}", dir); + return props; + } + + List envPropertyFiles; + + // Get all environment specific properties files for this environment, + // order them according to the lexical ordering of the filenames + try (Stream paths = Files.list(dir)) + { + envPropertyFiles = paths.filter(Files::isRegularFile) + .filter(p -> FileID.isExtension(p, "properties")) + .filter(p -> + { + String name = p.getFileName().toString(); + return name.startsWith(env); + }) + .sorted(PathCollators.byName(true)) + .toList(); + } + + if (LOG.isDebugEnabled()) + LOG.debug("Environment property files {}", envPropertyFiles); + + // Load each *.properties file + for (Path file : envPropertyFiles) + { + try (InputStream stream = Files.newInputStream(file)) + { + Properties tmp = new Properties(); + tmp.load(stream); + //put each property into our substitution pool + tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); + } + } + + return props; + } + + /** + * Builder of a deployment configuration for a specific {@link Environment}. + * + *

        + * Results in {@link Attributes} for {@link Environment} containing the + * deployment configuration (as {@link Deployable} keys) that is applied to all deployable + * apps belonging to that {@link Environment}. + *

        + */ + public static class EnvironmentConfig + { + // Using setters in this class to allow jetty-xml + // syntax to skip setting of an environment attribute if property is unset, + // allowing the in code values to be same defaults as they are in embedded-jetty. + + private final Environment _environment; + + private EnvironmentConfig(Environment environment) + { + this._environment = environment; + } + + /** + * Load a java properties file as a set of Attributes for this Environment. + * + * @param path the path of the properties file + * @throws IOException if unable to read the properties file + */ + public void loadProperties(Path path) throws IOException + { + Properties props = new Properties(); + try (InputStream inputStream = Files.newInputStream(path)) + { + props.load(inputStream); + props.forEach((key, value) -> _environment.setAttribute((String)key, value)); + } + } + + /** + * Convenience method for {@code loadProperties(Path.of(pathName));} + * + * @param pathName the name of the path to load. + * @throws IOException if unable to read the properties file + * @see #loadProperties(Path) + */ + public void loadPropertiesFromPathName(String pathName) throws IOException + { + loadProperties(Path.of(pathName)); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} attribute. + * + * @param configurations The configuration class names as a comma separated list + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String configurations) + { + setConfigurationClasses(StringUtil.isBlank(configurations) ? null : configurations.split(",")); + } + + /** + * This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property. + * + * @param configurations The configuration class names. + * @see Deployable#CONFIGURATION_CLASSES + */ + public void setConfigurationClasses(String[] configurations) + { + if (configurations == null) + _environment.removeAttribute(Deployable.CONFIGURATION_CLASSES); + else + _environment.setAttribute(Deployable.CONFIGURATION_CLASSES, configurations); + } + + /** + * This is equivalent to setting the {@link Deployable#CONTAINER_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning container jars + * @see Deployable#CONTAINER_SCAN_JARS + */ + public void setContainerScanJarPattern(String pattern) + { + _environment.setAttribute(Deployable.CONTAINER_SCAN_JARS, pattern); + } + + /** + * The name of the class that this environment uses to create {@link ContextHandler} + * instances (can be class that implements {@code java.util.function.Supplier} + * as well). + * + *

        + * This is the fallback class used, if the context class itself isn't defined by + * the web application being deployed. + *

        + * + * @param classname the classname for this environment's context deployable. + * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT + */ + public void setContextHandlerClass(String classname) + { + _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); + } + + /** + * Set the defaultsDescriptor. + * This is equivalent to setting the {@link Deployable#DEFAULTS_DESCRIPTOR} attribute. + * + * @param defaultsDescriptor the defaultsDescriptor to set + * @see Deployable#DEFAULTS_DESCRIPTOR + */ + public void setDefaultsDescriptor(String defaultsDescriptor) + { + _environment.setAttribute(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor); + } + + /** + * This is equivalent to setting the {@link Deployable#EXTRACT_WARS} attribute. + * + * @param extractWars the extractWars to set + * @see Deployable#EXTRACT_WARS + */ + public void setExtractWars(boolean extractWars) + { + _environment.setAttribute(Deployable.EXTRACT_WARS, extractWars); + } + + /** + * This is equivalent to setting the {@link Deployable#PARENT_LOADER_PRIORITY} attribute. + * + * @param parentLoaderPriority the parentLoaderPriority to set + * @see Deployable#PARENT_LOADER_PRIORITY + */ + public void setParentLoaderPriority(boolean parentLoaderPriority) + { + _environment.setAttribute(Deployable.PARENT_LOADER_PRIORITY, parentLoaderPriority); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_EXCLUSION_PATTERN} property. + * + * @param pattern The regex pattern to exclude ServletContainerInitializers from executing + * @see Deployable#SCI_EXCLUSION_PATTERN + */ + public void setServletContainerInitializerExclusionPattern(String pattern) + { + _environment.setAttribute(Deployable.SCI_EXCLUSION_PATTERN, pattern); + } + + /** + * This is equivalent to setting the {@link Deployable#SCI_ORDER} property. + * + * @param order The ordered list of ServletContainerInitializer classes to run + * @see Deployable#SCI_ORDER + */ + public void setServletContainerInitializerOrder(String order) + { + _environment.setAttribute(Deployable.SCI_ORDER, order); + } + + /** + * This is equivalent to setting the {@link Deployable#WEBINF_SCAN_JARS} property. + * + * @param pattern The regex pattern to use when bytecode scanning web-inf jars + * @see Deployable#WEBINF_SCAN_JARS + */ + public void setWebInfScanJarPattern(String pattern) + { + _environment.setAttribute(Deployable.WEBINF_SCAN_JARS, pattern); + } + } + + public static class MonitoredPathFilter implements FilenameFilter + { + @Override + public boolean accept(File dir, String name) + { + if (dir == null || !dir.canRead()) + return false; + + Path path = dir.toPath().resolve(name); + + // We don't monitor subdirectories. + if (Files.isDirectory(path)) + return false; + + // Synthetic files (like consoles, printers, serial ports, etc) are ignored. + if (!Files.isRegularFile(path)) + return false; + + try + { + // ignore traditional "hidden" path entries. + if (name.startsWith(".")) + return false; + // ignore path tagged as hidden by FS + if (Files.isHidden(path)) + return false; + } + catch (IOException ignore) + { + // ignore + } + + // The filetypes that are monitored, and we want updates for when they change. + return FileID.isExtension(name, "jar", "war", "xml", "properties"); + } + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployAction.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployAction.java new file mode 100644 index 000000000000..521054a82e72 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployAction.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +/** + *

        Represents a single step in one update as a result of a scanner event.

        + */ +public class DeployAction +{ + public enum Type + { + REMOVE, + ADD; + } + + private final Type type; + private final DefaultApp app; + + public DeployAction(Type type, DefaultApp app) + { + this.type = type; + this.app = app; + } + + public String getName() + { + return app.getName(); + } + + public DefaultApp getApp() + { + return app; + } + + public Type getType() + { + return type; + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployActionComparator.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployActionComparator.java new file mode 100644 index 000000000000..48f5235af55d --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DeployActionComparator.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.util.Comparator; + +/** + *

        The List of {@link DeployAction} sort.

        + * + *
          + *
        • {@link DeployAction#getType()} is sorted by all {@link DeployAction.Type#REMOVE} + * actions first, followed by all {@link DeployAction.Type#ADD} actions.
        • + *
        • {@link DeployAction.Type#REMOVE} type are in descending alphabetically order.
        • + *
        • {@link DeployAction.Type#ADD} type are in ascending alphabetically order.
        • + *
        > + */ +public class DeployActionComparator implements Comparator +{ + private final Comparator typeComparator; + private final Comparator basenameComparator; + + public DeployActionComparator() + { + typeComparator = Comparator.comparing(DeployAction::getType); + basenameComparator = Comparator.comparing(DeployAction::getName); + } + + @Override + public int compare(DeployAction o1, DeployAction o2) + { + int diff = typeComparator.compare(o1, o2); + if (diff != 0) + return diff; + return switch (o1.getType()) + { + case REMOVE -> + { + yield basenameComparator.compare(o2, o1); + } + case ADD -> + { + yield basenameComparator.compare(o1, o2); + } + }; + } +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java deleted file mode 100644 index 93a776553029..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ /dev/null @@ -1,422 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.io.FilenameFilter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; - -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; -import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.providers.internal.DeploymentUnits; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.Scanner; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.annotation.ManagedOperation; -import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceFactory; -import org.eclipse.jetty.util.resource.Resources; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ManagedObject("Abstract Provider for loading webapps") -public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider, DeploymentUnits.Listener -{ - private static final Logger LOG = LoggerFactory.getLogger(ScanningAppProvider.class); - - private DeploymentUnits _units = new DeploymentUnits(); - private DeploymentManager _deploymentManager; - private FilenameFilter _filenameFilter; - private Comparator _unitComparator = Comparator.comparing(Unit::getBaseName); - private final List _monitored = new CopyOnWriteArrayList<>(); - private int _scanInterval = 10; - private Scanner _scanner; - private boolean _useRealPaths; - private boolean _deferInitialScan = false; - - protected ScanningAppProvider() - { - this(null); - } - - protected ScanningAppProvider(FilenameFilter filter) - { - _filenameFilter = filter; - _units.setListener(this); - installBean(_units); - } - - /** - * @return True if the real path of the scanned files should be used for deployment. - */ - public boolean isUseRealPaths() - { - return _useRealPaths; - } - - /** - * @param useRealPaths True if the real path of the scanned files should be used for deployment. - */ - public void setUseRealPaths(boolean useRealPaths) - { - _useRealPaths = useRealPaths; - } - - protected List getUnitsForEnvironment(String envName) - { - return _units.getUnits().stream() - .filter((unit) -> envName.equals(unit.getEnvironmentName())) - .toList(); - } - - protected void setFilenameFilter(FilenameFilter filter) - { - if (isRunning()) - throw new IllegalStateException(); - _filenameFilter = filter; - } - - public Comparator getUnitComparator() - { - return _unitComparator; - } - - public void setUnitComparator(Comparator comparator) - { - this._unitComparator = comparator; - } - - /** - * @return The index of currently deployed applications. - */ - protected Collection getDeployedApps() - { - return _units.getUnits() - .stream() - .map(Unit::getApp) - .collect(Collectors.toUnmodifiableSet()); - } - - /** - * Called by the Scanner.DiscreteListener to create a new App object. - * Isolated in a method so that it is possible to override the default App - * object for specialized implementations of the AppProvider. - * - * @param path The file that the main point of deployment (eg: a context XML, a WAR file, a directory, etc) - * @return The App object for this particular context. - */ - protected App createApp(Path path) - { - App app = new App(_deploymentManager, this, path); - if (LOG.isDebugEnabled()) - LOG.debug("{} creating {}", this, app); - return app; - } - - @Override - protected void doStart() throws Exception - { - if (LOG.isDebugEnabled()) - LOG.debug("{}.doStart()", this.getClass().getSimpleName()); - if (_monitored.isEmpty()) - throw new IllegalStateException("No monitored dir specified"); - - LOG.info("Deployment monitor in {} at intervals {}s", _monitored, getScanInterval()); - List files = new ArrayList<>(); - for (Resource resource : _monitored) - { - if (Resources.missing(resource)) - { - LOG.warn("Does not exist: {}", resource); - continue; // skip - } - - // handle resource smartly - for (Resource r: resource) - { - Path path = r.getPath(); - if (path == null) - { - LOG.warn("Not based on FileSystem Path: {}", r); - continue; // skip - } - if (Files.isDirectory(path) || Files.isReadable(path)) - files.add(resource.getPath()); - else - LOG.warn("Unsupported Path (not a directory and/or not readable): {}", r); - } - } - - _scanner = new Scanner(null, _useRealPaths); - _scanner.setScanDirs(files); - _scanner.setScanInterval(_scanInterval); - _scanner.setFilenameFilter(_filenameFilter); - _scanner.setReportDirs(true); - _scanner.setScanDepth(1); //consider direct dir children of monitored dir - _scanner.addListener(_units); - _scanner.setReportExistingFilesOnStartup(true); - _scanner.setAutoStartScanning(!_deferInitialScan); - addBean(_scanner); - - if (isDeferInitialScan()) - { - // Setup listener to wait for Server in STARTED state, which - // triggers the first scan of the monitored directories - getDeploymentManager().getServer().addEventListener( - new LifeCycle.Listener() - { - @Override - public void lifeCycleStarted(LifeCycle event) - { - if (event instanceof Server) - { - if (LOG.isDebugEnabled()) - LOG.debug("Triggering Deferred Scan of {}", _monitored); - _scanner.startScanning(); - } - } - }); - } - - super.doStart(); - } - - @Override - protected void doStop() throws Exception - { - super.doStop(); - if (_scanner != null) - { - removeBean(_scanner); - _scanner.removeListener(_units); - _scanner = null; - } - } - - protected boolean exists(String path) - { - return _scanner.exists(path); - } - - /** - * Given a deployment unit, pick the main Path that is actually the point of deployment. - * - * @param unit a single unit of deployment. - * @return the specific path that represents the main point of deployment (eg: xml if it exists) - */ - protected abstract Path getMainDeploymentPath(Unit unit); - - @Override - public void unitsChanged(List units) - { - units.sort(getUnitComparator()); - - if (LOG.isDebugEnabled()) - LOG.debug("unitsChanged: {}", units); - - for (Unit unit : units) - { - if (LOG.isDebugEnabled()) - LOG.debug("unit changed: {}", unit); - - switch (unit.getState()) - { - case ADDED -> - { - Path mainDeploymentPath = getMainDeploymentPath(unit); - App app = this.createApp(mainDeploymentPath); - unit.setApp(app); - if (LOG.isDebugEnabled()) - LOG.debug("Unit ADDED: {}", unit); - - if (app != null) - _deploymentManager.addApp(app); - } - case CHANGED -> - { - App oldApp = unit.removeApp(); - if (oldApp != null) - _deploymentManager.removeApp(oldApp); - - Path mainDeploymentPath = getMainDeploymentPath(unit); - App app = this.createApp(mainDeploymentPath); - unit.setApp(app); - - if (LOG.isDebugEnabled()) - LOG.debug("Unit CHANGED: {}", unit); - - if (app != null) - _deploymentManager.addApp(app); - } - case REMOVED -> - { - App app = unit.removeApp(); - if (LOG.isDebugEnabled()) - LOG.debug("Unit REMOVED: {}", unit); - if (app != null) - _deploymentManager.removeApp(app); - } - } - - // we are done processing unit, reset its state - unit.resetStates(); - } - } - - /** - * Get the deploymentManager. - * - * @return the deploymentManager - */ - public DeploymentManager getDeploymentManager() - { - return _deploymentManager; - } - - public Resource getMonitoredDirResource() - { - if (_monitored.isEmpty()) - return null; - if (_monitored.size() > 1) - throw new IllegalStateException(); - return _monitored.get(0); - } - - public String getMonitoredDirName() - { - Resource resource = getMonitoredDirResource(); - return resource == null ? null : resource.toString(); - } - - @ManagedAttribute("scanning interval to detect changes which need reloaded") - public int getScanInterval() - { - return _scanInterval; - } - - @Override - public void setDeploymentManager(DeploymentManager deploymentManager) - { - _deploymentManager = deploymentManager; - } - - public void setMonitoredResources(List resources) - { - _monitored.clear(); - if (resources == null) - return; - resources.stream().filter(Objects::nonNull).forEach(_monitored::add); - } - - public List getMonitoredResources() - { - return Collections.unmodifiableList(_monitored); - } - - public void setMonitoredDirResource(Resource resource) - { - setMonitoredResources(Collections.singletonList(resource)); - } - - public void addScannerListener(Scanner.Listener listener) - { - _scanner.addListener(listener); - } - - /** - * @param dir Directory to scan for context descriptors or war files - */ - public void setMonitoredDirName(String dir) - { - setMonitoredDirectories(Collections.singletonList(dir)); - } - - public void setMonitoredDirectories(Collection directories) - { - try - { - List resources = new ArrayList<>(); - for (String dir : directories) - { - resources.add(ResourceFactory.of(this).newResource(dir)); - } - setMonitoredResources(resources); - } - catch (Exception e) - { - throw new IllegalArgumentException(e); - } - } - - /** - * Test if initial scan should be deferred. - * - * @return true if initial scan is deferred, false to have initial scan occur on startup of ScanningAppProvider. - */ - public boolean isDeferInitialScan() - { - return _deferInitialScan; - } - - /** - * Flag to control initial scan behavior. - * - *
          - *
        • {@code true} - to have initial scan deferred until the {@link Server} component - * has reached it's STARTED state.
          - * Note: any failures in a deploy will not fail the Server startup in this mode.
        • - *
        • {@code false} - (default value) to have initial scan occur as normal on - * ScanningAppProvider startup.
        • - *
        - * - * @param defer true to defer initial scan, false to have initial scan occur on startup of ScanningAppProvider. - */ - public void setDeferInitialScan(boolean defer) - { - _deferInitialScan = defer; - } - - public void setScanInterval(int scanInterval) - { - _scanInterval = scanInterval; - } - - @ManagedOperation(value = "Scan the monitored directories", impact = "ACTION") - public void scan() - { - LOG.info("Performing scan of monitored directories: {}", - getMonitoredResources().stream().map((r) -> r.getURI().toASCIIString()) - .collect(Collectors.joining(", ", "[", "]")) - ); - _scanner.nudge(); - } - - @Override - public String toString() - { - return String.format("%s@%x%s", this.getClass(), hashCode(), _monitored); - } -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java deleted file mode 100644 index fabd82d33523..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/Unit.java +++ /dev/null @@ -1,249 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.resource.PathCollators; - -/** - * A Unit of deployment, a basename and all the associated - * paths that belong to that basename, along with state. - */ -public class Unit -{ - public enum State - { - UNCHANGED, - ADDED, - CHANGED, - REMOVED - } - - private final String baseName; - private final Map paths = new HashMap<>(); - /** - * Indicates a synthetic Unit, used to track Environment specific configurations. - * Such as {@code .xml}, {@code .properties}, {@code -.xml}, {@code -.properties}, - * all under a single unit that will not actually be deployed. - */ - private String envConfig; - private App app; - private State state; - - public Unit(String basename) - { - this.baseName = basename; - this.state = calcState(); - } - - public String getBaseName() - { - return baseName; - } - - public State getState() - { - return state; - } - - /** - *

        - * Calculate the State of the overall Unit based on the States in the Paths. - *

        - *
        - *
        UNCHANGED
        - *
        All Path states are in UNCHANGED state
        - *
        ADDED
        - *
        All Path states are in ADDED state
        - *
        CHANGED
        - *
        At least one Path state is CHANGED, or there is a variety of states
        - *
        REMOVED
        - *
        All Path states are in REMOVED state, or there are no Paths being tracked
        - *
        - * - * @return the state of the Unit. - */ - private State calcState() - { - if (paths.isEmpty()) - return State.REMOVED; - - // Calculate state of unit from Path states. - Unit.State ret = null; - for (Unit.State pathState : paths.values()) - { - switch (pathState) - { - case UNCHANGED -> - { - if (ret == null) - ret = State.UNCHANGED; - else if (ret != State.UNCHANGED) - ret = State.CHANGED; - } - case ADDED -> - { - if (ret == null) - ret = State.ADDED; - else if (ret == State.UNCHANGED || ret == State.REMOVED) - ret = State.ADDED; - } - case CHANGED -> - { - ret = State.CHANGED; - } - case REMOVED -> - { - if (ret == null) - ret = State.REMOVED; - else if (ret != State.REMOVED) - ret = State.CHANGED; - } - } - } - return ret != null ? ret : State.UNCHANGED; - } - - public Map getPaths() - { - return paths; - } - - /** - * Return list of Paths being tracked that are live (not-removed). - * - * @return the list of live paths. - */ - public List getLivePaths() - { - return paths - .entrySet() - .stream() - .filter((e) -> e.getValue() != Unit.State.REMOVED) - .map(Map.Entry::getKey) - .sorted(PathCollators.byName(true)) - .toList(); - } - - public void putPath(Path path, State state) - { - this.paths.put(path, state); - setState(calcState()); - } - - public boolean isDeployable() - { - return StringUtil.isBlank(envConfig); - } - - public String getEnvironmentName() - { - if (app == null) - return null; - return app.getEnvironmentName(); - } - - public String getEnvironmentConfigName() - { - return envConfig; - } - - public void setEnvironmentConfigName(String environmentConfigName) - { - this.envConfig = environmentConfigName; - } - - public App getApp() - { - return app; - } - - public void setApp(App app) - { - this.app = app; - } - - public App removeApp() - { - App oldApp = this.app; - this.app = null; - return oldApp; - } - - public void resetStates() - { - // Drop paths that were removed. - List removedPaths = paths.entrySet() - .stream().filter(e -> e.getValue() == State.REMOVED) - .map(Map.Entry::getKey) - .toList(); - for (Path removedPath : removedPaths) - { - paths.remove(removedPath); - } - // Set all remaining path states to UNCHANGED - paths.replaceAll((p, v) -> State.UNCHANGED); - state = calcState(); - } - - @Override - public boolean equals(Object o) - { - if (o == null || getClass() != o.getClass()) - return false; - Unit unit = (Unit)o; - return Objects.equals(baseName, unit.baseName); - } - - @Override - public int hashCode() - { - return Objects.hashCode(baseName); - } - - public void setState(State state) - { - this.state = state; - } - - @Override - public String toString() - { - StringBuilder str = new StringBuilder(); - str.append("Unit[").append(baseName); - str.append("|").append(getState()); - if (envConfig != null) - str.append(", envConfig=").append(envConfig); - str.append(", paths="); - str.append(paths.entrySet().stream() - .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) - .collect(Collectors.joining(", ", "[", "]")) - ); - str.append(", app="); - if (app == null) - str.append(""); - else - str.append(app); - str.append("]"); - return str.toString(); - } -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java deleted file mode 100644 index aadcdea2a4b0..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnits.java +++ /dev/null @@ -1,178 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers.internal; - -import java.nio.file.Path; -import java.util.Collection; -import java.util.EventListener; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import org.eclipse.jetty.deploy.providers.Unit; -import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.Scanner; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.component.Environment; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The role of DeploymentUnits is to keep track of {@link Unit} instances, and - * process changes coming from {@link java.util.Scanner} into a set of units - * that will be processed via a listener. - */ -public class DeploymentUnits implements Scanner.ChangeSetListener -{ - private static final Logger LOG = LoggerFactory.getLogger(DeploymentUnits.class); - - /** - * Basename of unit, to the Unit instance. - */ - private Map units = new HashMap<>(); - - private Listener listener; - - public Listener getListener() - { - return listener; - } - - public void setListener(Listener listener) - { - this.listener = listener; - } - - public Unit getUnit(String basename) - { - return units.get(basename); - } - - public Collection getUnits() - { - return units.values(); - } - - @Override - public void pathsChanged(Map changeSet) - { - Objects.requireNonNull(changeSet); - if (LOG.isDebugEnabled()) - { - LOG.debug("processChanges: changeSet: {}", - changeSet.entrySet() - .stream() - .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) - .collect(Collectors.joining(", ", "[", "]")) - ); - } - - Set changedBaseNames = new HashSet<>(); - Set environmentNames = Environment.getAll().stream() - .map(Environment::getName).collect(Collectors.toUnmodifiableSet()); - - for (Map.Entry entry : changeSet.entrySet()) - { - Path path = entry.getKey(); - Unit.State state = toState(entry.getValue()); - - // Using lower-case as defined by System Locale, as the files themselves from System FS. - String basename = FileID.getBasename(path).toLowerCase(); - - String envname = getEnvironmentName(basename, environmentNames); - if (StringUtil.isNotBlank(envname)) - { - // The file starts with a known environment name. - // We only care if it is a Environment Configuration file, namely an XML or Properties file. - if (FileID.isExtension(path, "xml", "properties")) - { - // we have an environment configuration specific path entry. - changedBaseNames.add(envname); - Unit unit = units.computeIfAbsent(envname, Unit::new); - unit.setEnvironmentConfigName(envname); - unit.putPath(path, state); - } - else - { - LOG.warn("Encountered Path that uses reserved Environment name prefix " + - "(This is seen as a configuration for the environment) [{}]: {}", envname, path); - // This is something like ee10-app.war - // For now, we add it to the basename, but in the future, this will be rejected - // as it prevents the use of a configuration file like ee10-app.xml next to it (which - // will be seen as a XML to configure the ee10 environment, not an XML for deploying that war) - changedBaseNames.add(basename); - Unit unit = units.computeIfAbsent(basename, Unit::new); - unit.putPath(path, state); - } - } - else - { - // we have a normal path entry - changedBaseNames.add(basename); - Unit unit = units.computeIfAbsent(basename, Unit::new); - unit.putPath(path, state); - } - } - - Listener listener = getListener(); - if (listener != null) - { - List changedUnits = changedBaseNames.stream() - .map(name -> units.get(name)) - .collect(Collectors.toList()); - - listener.unitsChanged(changedUnits); - } - } - - protected String getEnvironmentName(String basename, Set environmentNames) - { - // Handle the case where the basename is part of an environment configuration - for (String envname : environmentNames) - { - if (basename.startsWith(envname)) - return envname; - } - - return null; - } - - private Unit.State toState(Scanner.Notification notification) - { - return switch (notification) - { - case ADDED -> - { - yield Unit.State.ADDED; - } - case CHANGED -> - { - yield Unit.State.CHANGED; - } - case REMOVED -> - { - yield Unit.State.REMOVED; - } - }; - } - - public interface Listener extends EventListener - { - void unitsChanged(List units); - } -} diff --git a/jetty-core/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy/lifecycle-bindings.txt b/jetty-core/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy/lifecycle-bindings.txt deleted file mode 100644 index ed00104daf35..000000000000 --- a/jetty-core/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy/lifecycle-bindings.txt +++ /dev/null @@ -1 +0,0 @@ -# Default Bindings \ No newline at end of file diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java index cb0c14e2f3b5..ef6355cf7922 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java @@ -45,7 +45,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { actualOrder.add(node); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index 21646d04e897..fc1578e674f6 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -42,8 +42,8 @@ public void testStateTransitionNewToDeployed() throws Exception depman.start(); // Trigger new App - mockProvider.createWebapp("foo-webapp-1.war"); - App app = depman.getApp("mock-foo-webapp-1.war"); + mockProvider.createWebapp("foo-webapp-1"); + App app = depman.getApp("foo-webapp-1"); // Request Deploy of App depman.requestAppGoal(app, "deployed"); @@ -78,7 +78,7 @@ public void testStateTransitionReceive() throws Exception // Perform no goal request. // Setup Expectations. - List expected = new ArrayList(); + List expected = new ArrayList<>(); pathtracker.assertExpected("Test StateTransition / New only", expected); } @@ -103,8 +103,8 @@ public void testStateTransitionDeployedToUndeployed() throws Exception depman.start(); // Trigger new App - App foo = mockProvider.createWebapp("foo-webapp-1.war"); - App app = depman.getApp(foo.getPath()); + App foo = mockProvider.createWebapp("foo-webapp-1"); + App app = depman.getApp(foo.getName()); // Request Deploy of App depman.requestAppGoal(app, "deployed"); @@ -114,12 +114,12 @@ public void testStateTransitionDeployedToUndeployed() throws Exception MBeanServerConnection mbsConnection = jmxConnection.getConnection(); ObjectName dmObjName = new ObjectName("org.eclipse.jetty.deploy:type=deploymentmanager,id=0"); - String[] params = new String[]{"mock-foo-webapp-1.war", "undeployed"}; + String[] params = new String[]{"foo-webapp-1", "undeployed"}; String[] signature = new String[]{"java.lang.String", "java.lang.String"}; mbsConnection.invoke(dmObjName, "requestAppGoal", params, signature); // Setup Expectations. - List expected = new ArrayList(); + List expected = new ArrayList<>(); // SHOULD NOT SEE THIS NODE VISITED - expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index f3e8b85d4a12..2c2dae447df1 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -19,20 +19,17 @@ import java.util.Set; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.LifeCycle; -import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -84,9 +81,9 @@ public void testReceiveApp() throws Exception // Test app get App app = apps.stream().findFirst().orElse(null); assertNotNull(app); - App actual = depman.getApp(app.getPath()); + App actual = depman.getApp(app.getName()); assertNotNull(actual, "Should have gotten app (by id)"); - assertThat(actual.getPath().toString(), endsWith("mock-foo-webapp-1.war")); + assertThat(actual.getName(), is("foo-webapp-1")); } finally { @@ -110,76 +107,6 @@ public void testBinding() assertEquals(1, deploybindings.size(), "'deploying' Bindings.size"); } - @Test - public void testDefaultEnvironment() - { - DeploymentManager depman = new DeploymentManager(); - assertThat(depman.getDefaultEnvironmentName(), Matchers.nullValue()); - - Environment.ensure("ee7"); - depman.addAppProvider(new MockAppProvider() - { - @Override - public String getEnvironmentName() - { - return "ee7"; - } - }); - assertThat(depman.getDefaultEnvironmentName(), is("ee7")); - - Environment.ensure("ee12"); - depman.addAppProvider(new MockAppProvider() - { - @Override - public String getEnvironmentName() - { - return "ee12"; - } - }); - assertThat(depman.getDefaultEnvironmentName(), is("ee12")); - - Environment.ensure("ee11"); - depman.addAppProvider(new MockAppProvider() - { - @Override - public String getEnvironmentName() - { - return "ee11"; - } - }); - assertThat(depman.getDefaultEnvironmentName(), is("ee12")); - - Environment.ensure("somethingElse"); - depman.addAppProvider(new MockAppProvider() - { - @Override - public String getEnvironmentName() - { - return "somethingElse"; - } - }); - assertThat(depman.getDefaultEnvironmentName(), is("ee12")); - - Environment.ensure("other"); - depman.addAppProvider(new MockAppProvider() - { - @Override - public String getEnvironmentName() - { - return "other"; - } - }); - - assertThat(depman.getAppProviders().stream().map(AppProvider::getEnvironmentName).sorted(Deployable.ENVIRONMENT_COMPARATOR).toList(), - contains( - "other", - "somethingElse", - "ee7", - "ee11", - "ee12" - )); - } - @Test public void testXmlConfigured(WorkDir workDir) throws Exception { @@ -188,9 +115,9 @@ public void testXmlConfigured(WorkDir workDir) throws Exception try { jetty = new XmlConfiguredJetty(testdir); - jetty.addConfiguration("jetty.xml"); - jetty.addConfiguration("jetty-http.xml"); - jetty.addConfiguration("jetty-core-deploy-custom.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Should not throw an Exception jetty.load(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java new file mode 100644 index 000000000000..3f1fa51dbe91 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import org.eclipse.jetty.server.handler.ContextHandler; + +public class MockApp implements App +{ + private final String name; + private ContextHandler contextHandler; + + public MockApp(String name) + { + this.name = name; + } + + @Override + public String getName() + { + return name; + } + + public void setContextHandler(ContextHandler contextHandler) + { + this.contextHandler = contextHandler; + } + + @Override + public ContextHandler getContextHandler() + { + return contextHandler; + } + + @Override + public String toString() + { + return String.format("%s@%x[%s]", this.getClass().getSimpleName(), hashCode(), getName()); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java index 57af532999fc..051b35a8eece 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java @@ -26,7 +26,6 @@ public class MockAppProvider extends AbstractLifeCycle implements AppProvider private DeploymentManager deployMan; private Path webappsDir; - @Override public String getEnvironmentName() { return Environment.ensure("mock").getName(); @@ -46,41 +45,41 @@ public void doStart() public App createWebapp(String name) { - App app = new App(deployMan, this, Path.of("./mock-" + name)); + String basename = FileID.getBasename(name); + MockApp app = new MockApp(basename); + app.setContextHandler(createContextHandler(app)); this.deployMan.addApp(app); return app; } - @Override public ContextHandler createContextHandler(App app) { ContextHandler contextHandler = new ContextHandler(); - String name = app.getPath().toString(); - name = name.substring(name.lastIndexOf("-") + 1); - Path war = webappsDir.resolve(name); + String name = app.getName(); + Path war = webappsDir.resolve(name + ".war"); - String path = war.toString(); + String contextPath = war.toString(); if (FileID.isWebArchive(war)) { // Context Path is the same as the archive. - path = path.substring(0, path.length() - 4); + contextPath = FileID.getBasename(war); } - // special case of archive (or dir) named "root" is / context - if (path.equalsIgnoreCase("root") || path.equalsIgnoreCase("root/")) - path = "/"; + // special case of archive named "root" is / context-path + if (contextPath.equalsIgnoreCase("root")) + contextPath = "/"; // Ensure "/" is Prepended to all context paths. - if (path.charAt(0) != '/') - path = "/" + path; + if (contextPath.charAt(0) != '/') + contextPath = "/" + contextPath; // Ensure "/" is Not Trailing in context paths. - if (path.endsWith("/") && path.length() > 0) - path = path.substring(0, path.length() - 1); + if (contextPath.endsWith("/")) + contextPath = contextPath.substring(0, contextPath.length() - 1); - contextHandler.setContextPath(path); + contextHandler.setContextPath(contextPath); return contextHandler; } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java deleted file mode 100644 index 6a30dc6d1c71..000000000000 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderTest.java +++ /dev/null @@ -1,154 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; - -@ExtendWith(WorkDirExtension.class) -public class ContextProviderTest extends AbstractCleanEnvironmentTest -{ - public WorkDir workDir; - - /** - * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. - *

        - * Test with only a single XML. - */ - @Test - public void testMainDeploymentPathOnlyXml() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - - Unit unit = new Unit("bar"); - unit.putPath(xml, Unit.State.UNCHANGED); - - ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(unit); - - assertThat("main path", main, equalTo(xml)); - } - - /** - * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. - *

        - * Test with an XML and WAR. - */ - @Test - public void testMainDeploymentPathXmlAndWar() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - Unit unit = new Unit("bar"); - unit.putPath(xml, Unit.State.UNCHANGED); - unit.putPath(war, Unit.State.UNCHANGED); - - ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(unit); - - assertThat("main path", main, equalTo(xml)); - } - - /** - * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. - *

        - * Test with a Directory and WAR. - */ - @Test - public void testMainDeploymentPathDirAndWar() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path appDir = dir.resolve("bar"); - Files.createDirectory(appDir); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - Unit unit = new Unit("bar"); - unit.putPath(appDir, Unit.State.UNCHANGED); - unit.putPath(war, Unit.State.UNCHANGED); - - ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(unit); - - assertThat("main path", main, equalTo(war)); - } - - /** - * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. - *

        - * Test with a Directory and XML. - */ - @Test - public void testMainDeploymentPathDirAndXml() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path appDir = dir.resolve("bar"); - Files.createDirectory(appDir); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - - Unit unit = new Unit("bar"); - unit.putPath(appDir, Unit.State.UNCHANGED); - unit.putPath(xml, Unit.State.UNCHANGED); - - ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(unit); - - assertThat("main path", main, equalTo(xml)); - } - - /** - * Ensuring heuristics defined in javadoc for {@link ContextProvider} is followed. - *

        - * Test with a Directory and XML and WAR - */ - @Test - public void testMainDeploymentPathDirAndXmlAndWar() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path appDir = dir.resolve("bar"); - Files.createDirectory(appDir); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - Unit unit = new Unit("bar"); - unit.putPath(appDir, Unit.State.UNCHANGED); - unit.putPath(xml, Unit.State.UNCHANGED); - unit.putPath(war, Unit.State.UNCHANGED); - - ContextProvider provider = new ContextProvider(); - Path main = provider.getMainDeploymentPath(unit); - - assertThat("main path", main, equalTo(xml)); - } -} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java new file mode 100644 index 000000000000..6eebbf81854a --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java @@ -0,0 +1,257 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class DefaultAppTest +{ + public WorkDir workDir; + + /** + * Ensuring main path heuristics is followed. + *

        + * Test with only a single XML. + */ + @Test + public void testMainPathOnlyXml() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + DefaultApp app = new DefaultApp("bar"); + app.putPath(xml, DefaultApp.State.UNCHANGED); + + Path main = app.getMainPath(); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring main path heuristics is followed. + *

        + * Test with an XML and WAR. + */ + @Test + public void testMainPathXmlAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DefaultApp app = new DefaultApp("bar"); + app.putPath(xml, DefaultApp.State.UNCHANGED); + app.putPath(war, DefaultApp.State.UNCHANGED); + + Path main = app.getMainPath(); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring main path heuristics is followed. + *

        + * Test with a Directory and WAR. + */ + @Test + public void testMainPathDirAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DefaultApp app = new DefaultApp("bar"); + app.putPath(appDir, DefaultApp.State.UNCHANGED); + app.putPath(war, DefaultApp.State.UNCHANGED); + + Path main = app.getMainPath(); + + assertThat("main path", main, equalTo(war)); + } + + /** + * Ensuring main path heuristics is followed. + *

        + * Test with a Directory and XML. + */ + @Test + public void testMainPathDirAndXml() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + DefaultApp app = new DefaultApp("bar"); + app.putPath(appDir, DefaultApp.State.UNCHANGED); + app.putPath(xml, DefaultApp.State.UNCHANGED); + + Path main = app.getMainPath(); + + assertThat("main path", main, equalTo(xml)); + } + + /** + * Ensuring main path heuristics is followed. + *

        + * Test with a Directory and XML and WAR + */ + @Test + public void testMainPathDirAndXmlAndWar() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path appDir = dir.resolve("bar"); + Files.createDirectory(appDir); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + DefaultApp app = new DefaultApp("bar"); + app.putPath(appDir, DefaultApp.State.UNCHANGED); + app.putPath(xml, DefaultApp.State.UNCHANGED); + app.putPath(war, DefaultApp.State.UNCHANGED); + + Path main = app.getMainPath(); + + assertThat("main path", main, equalTo(xml)); + } + + @Test + public void testStateUnchanged() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.UNCHANGED); + app.putPath(Path.of("test-b"), DefaultApp.State.UNCHANGED); + + assertThat(app.getState(), is(DefaultApp.State.UNCHANGED)); + } + + @Test + public void testStateInitialEmpty() + { + DefaultApp app = new DefaultApp("test"); + // intentionally empty of Paths + + assertThat(app.getState(), is(DefaultApp.State.REMOVED)); + } + + @Test + public void testStatePutThenRemoveAll() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.ADDED); + assertThat(app.getState(), is(DefaultApp.State.ADDED)); + + // Now it gets flagged as removed. (eg: by a Scanner change) + app.putPath(Path.of("test-a"), DefaultApp.State.REMOVED); + // Then it gets processed, which results in a state reset. + app.resetStates(); + + // The resulting Unit should have no paths, and be flagged as removed. + assertThat(app.getPaths().size(), is(0)); + assertThat(app.getState(), is(DefaultApp.State.REMOVED)); + } + + @Test + public void testStateAddedOnly() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.ADDED); + app.putPath(Path.of("test-b"), DefaultApp.State.ADDED); + + assertThat(app.getState(), is(DefaultApp.State.ADDED)); + } + + @Test + public void testStateAddedRemoved() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.REMOVED); // existing file removed in this scan + app.putPath(Path.of("test-b"), DefaultApp.State.ADDED); // new file introduced in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } + + @Test + public void testStateAddedChanged() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.CHANGED); // existing file changed in this scan + app.putPath(Path.of("test-b"), DefaultApp.State.ADDED); // new file introduced in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } + + @Test + public void testStateUnchangedAdded() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), DefaultApp.State.ADDED); // new file introduced in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } + + @Test + public void testStateUnchangedChanged() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), DefaultApp.State.CHANGED); // existing file changed in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } + + @Test + public void testStateUnchangedRemoved() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), DefaultApp.State.REMOVED); // existing file removed in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } + + @Test + public void testStateUnchangedRemovedAdded() + { + DefaultApp app = new DefaultApp("test"); + app.putPath(Path.of("test-a"), DefaultApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), DefaultApp.State.REMOVED); // existing file changed in this scan event + app.putPath(Path.of("test-c"), DefaultApp.State.ADDED); // new file introduced in this scan event + + assertThat(app.getState(), is(DefaultApp.State.CHANGED)); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java similarity index 92% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java index 7b957380255e..0a892fb6b7df 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java @@ -48,7 +48,7 @@ * Tests {@link ContextProvider} behaviors when in Deferred Startup mode */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderDeferredStartupTest extends AbstractCleanEnvironmentTest +public class DefaultProviderDeferredStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; @@ -66,11 +66,11 @@ public void testDelayedDeploy() throws Exception // Set jetty up on the real base jetty = new XmlConfiguredJetty(realBase); - jetty.addConfiguration("jetty.xml"); - jetty.addConfiguration("jetty-http.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); - jetty.addConfiguration("jetty-core-deploy-custom.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Put a context into the base jetty.copyWebapp("bar-core-context.xml", "bar.xml"); @@ -99,7 +99,7 @@ public void lifeCycleStarted(LifeCycle event) { eventQueue.add("Server started"); } - if (event instanceof ScanningAppProvider) + if (event instanceof DefaultProvider) { eventQueue.add("ScanningAppProvider started"); } @@ -112,13 +112,13 @@ public void lifeCycleStarted(LifeCycle event) server.addEventListener(eventCaptureListener); - ScanningAppProvider scanningAppProvider = null; + DefaultProvider scanningAppProvider = null; DeploymentManager deploymentManager = server.getBean(DeploymentManager.class); for (AppProvider appProvider : deploymentManager.getAppProviders()) { - if (appProvider instanceof ScanningAppProvider) + if (appProvider instanceof DefaultProvider) { - scanningAppProvider = (ScanningAppProvider)appProvider; + scanningAppProvider = (DefaultProvider)appProvider; } } assertNotNull(scanningAppProvider, "Should have found ScanningAppProvider"); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java similarity index 92% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java index c54ec97d769e..958b6b8f096d 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java @@ -45,13 +45,13 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; /** - * Similar in scope to {@link ContextProviderStartupTest}, except is concerned with the modification of existing - * deployed contexts due to incoming changes identified by the {@link ContextProvider}. + * Similar in scope to {@link DefaultProviderStartupTest}, except is concerned with the modification of existing + * deployed contexts due to incoming changes identified by the {@link DefaultProvider}. */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderRuntimeUpdatesTest extends AbstractCleanEnvironmentTest +public class DefaultProviderRuntimeUpdatesTest extends AbstractCleanEnvironmentTest { - private static final Logger LOG = LoggerFactory.getLogger(ContextProviderRuntimeUpdatesTest.class); + private static final Logger LOG = LoggerFactory.getLogger(DefaultProviderRuntimeUpdatesTest.class); private static XmlConfiguredJetty jetty; private final AtomicInteger _scans = new AtomicInteger(); @@ -81,11 +81,11 @@ public void createJettyBase(Path testdir) throws Exception public void startJetty() throws Exception { - jetty.addConfiguration("jetty.xml"); - jetty.addConfiguration("jetty-http.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); - jetty.addConfiguration("jetty-core-deploy-custom.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Should not throw an Exception jetty.load(); @@ -97,7 +97,7 @@ public void startJetty() throws Exception DeploymentManager dm = jetty.getServer().getBean(DeploymentManager.class); for (AppProvider provider : dm.getAppProviders()) { - if (provider instanceof ScanningAppProvider scanningAppProvider) + if (provider instanceof DefaultProvider scanningAppProvider) { _providerCount++; scanningAppProvider.addScannerListener(new Scanner.ScanCycleListener() @@ -170,6 +170,9 @@ public void testWebAppsWithAddedEnvConfig(WorkDir workDir) throws Exception Path testdir = workDir.getEmptyPathDir(); createJettyBase(testdir); + Path environments = jetty.getJettyBasePath().resolve("environments"); + FS.ensureDirExists(environments); + Environment.ensure("core").setAttribute("testname", "Initial"); // Setup initial webapp, with XML @@ -187,7 +190,7 @@ public void testWebAppsWithAddedEnvConfig(WorkDir workDir) throws Exception assertThat("displayname", contextHandler.getDisplayName(), is("Simple Initial")); // Add environment configuration - Path coreProp = jetty.getJettyBasePath().resolve("webapps/core.properties"); + Path coreProp = environments.resolve("core.properties"); Files.writeString(coreProp, "testname=New EnvConfig"); waitForDirectoryScan(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java similarity index 64% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java index d864ff87fa55..cd60a063fec3 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/ContextProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java @@ -39,13 +39,12 @@ import static org.hamcrest.Matchers.instanceOf; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Tests {@link ContextProvider} as it starts up for the first time. + * Tests {@link DefaultProvider} as it starts up for the first time. */ @ExtendWith(WorkDirExtension.class) -public class ContextProviderStartupTest extends AbstractCleanEnvironmentTest +public class DefaultProviderStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; @@ -62,11 +61,11 @@ public void setupEnvironment() throws Exception Files.writeString(resourceBase.resolve("text.txt"), "This is the resourceBase text"); - jetty.addConfiguration("jetty.xml"); - jetty.addConfiguration("jetty-http.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); - jetty.addConfiguration("jetty-core-deploy-custom.xml"); + jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Setup initial context jetty.copyWebapp("bar-core-context.xml", "bar.xml"); @@ -100,10 +99,13 @@ public void testStartupContext() throws Exception public void testStartupWithRelativeEnvironmentContext() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context.xml", StandardOpenOption.CREATE_NEW); - Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), Deployable.ENVIRONMENT_XML + ".other = etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); - assertTrue(Files.exists(propsFile)); - assertTrue(Files.exists(props2File)); + + Path environments = jettyBase.resolve("environments"); + FS.ensureDirExists(environments); + + Files.writeString(environments.resolve("core.properties"), Deployable.ENVIRONMENT_XML + "=etc/core-context.xml", StandardOpenOption.CREATE_NEW); + Files.writeString(environments.resolve("core-other.properties"), Deployable.ENVIRONMENT_XML + ".other=etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); + Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING); Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING); @@ -125,14 +127,17 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception public void testStartupWithAbsoluteEnvironmentContext() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), - String.format("%s = %s%n", Deployable.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), + + Path environments = jettyBase.resolve("environments"); + FS.ensureDirExists(environments); + + Files.writeString(environments.resolve("core.properties"), + String.format("%s=%s%n", Deployable.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), StandardOpenOption.CREATE_NEW); - assertTrue(Files.exists(propsFile)); - Path props2File = Files.writeString(jettyBase.resolve("webapps/core-other.properties"), - String.format("%s = %s%n", (Deployable.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), + Files.writeString(environments.resolve("core-other.properties"), + String.format("%s=%s%n", (Deployable.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), StandardOpenOption.CREATE_NEW); - assertTrue(Files.exists(props2File)); + jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -151,15 +156,17 @@ public void testStartupWithAbsoluteEnvironmentContext() throws Exception public void testNonEnvironmentPropertyFileNotApplied() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsFile = Files.writeString(jettyBase.resolve("webapps/non-env.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW); - Path propsEE8File = Files.writeString(jettyBase.resolve("webapps/ee8.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW); - Path propsEE9File = Files.writeString(jettyBase.resolve("webapps/ee9.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW); - Path propsEE10File = Files.writeString(jettyBase.resolve("webapps/ee10.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW); - Path propsNonCoreFile = Files.writeString(jettyBase.resolve("webapps/not-core.properties"), Deployable.ENVIRONMENT_XML + " = some/file/that/should/be/ignored.txt", StandardOpenOption.CREATE_NEW); - assertTrue(Files.exists(propsFile)); - assertTrue(Files.exists(propsEE8File)); - assertTrue(Files.exists(propsEE9File)); - assertTrue(Files.exists(propsEE10File)); + + // Create some property files in ${jetty.base}/environments/ directory. + Path environments = jettyBase.resolve("environments"); + FS.ensureDirExists(environments); + + Files.writeString(environments.resolve("non-env.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee8.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee9.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee10.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("not-core.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -176,26 +183,26 @@ public void testNonEnvironmentPropertyFileNotApplied() throws Exception public void testPropertyOverriding() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Path propsCoreAFile = Files.writeString(jettyBase.resolve("webapps/core-a.properties"), Deployable.ENVIRONMENT_XML + " = etc/a.xml", StandardOpenOption.CREATE_NEW); - Path propsCoreBFile = Files.writeString(jettyBase.resolve("webapps/core-b.properties"), Deployable.ENVIRONMENT_XML + " = etc/b.xml", StandardOpenOption.CREATE_NEW); - Path propsCoreCFile = Files.writeString(jettyBase.resolve("webapps/core-c.properties"), Deployable.ENVIRONMENT_XML + " = etc/c.xml", StandardOpenOption.CREATE_NEW); - Path propsCoreDFile = Files.writeString(jettyBase.resolve("webapps/core-d.properties"), Deployable.ENVIRONMENT_XML + " = etc/d.xml", StandardOpenOption.CREATE_NEW); - assertTrue(Files.exists(propsCoreAFile)); - assertTrue(Files.exists(propsCoreBFile)); - assertTrue(Files.exists(propsCoreCFile)); - assertTrue(Files.exists(propsCoreDFile)); - Path aPath = jettyBase.resolve("etc/a.xml"); + + Path environments = jettyBase.resolve("environments"); + FS.ensureDirExists(environments); + + Files.writeString(environments.resolve("core-a.properties"), Deployable.ENVIRONMENT_XML + "=etc/a.xml"); + Files.writeString(environments.resolve("core-b.properties"), Deployable.ENVIRONMENT_XML + "=etc/b.xml"); + Files.writeString(environments.resolve("core-c.properties"), Deployable.ENVIRONMENT_XML + "=etc/c.xml"); + Files.writeString(environments.resolve("core-d.properties"), Deployable.ENVIRONMENT_XML + "=etc/d.xml"); + + Path etc = jettyBase.resolve("etc"); + FS.ensureDirExists(etc); + + Path aPath = etc.resolve("a.xml"); writeXmlDisplayName(aPath, "A WebApp"); - Path bPath = jettyBase.resolve("etc/b.xml"); + Path bPath = etc.resolve("b.xml"); writeXmlDisplayName(bPath, "B WebApp"); - Path cPath = jettyBase.resolve("etc/c.xml"); + Path cPath = etc.resolve("c.xml"); writeXmlDisplayName(cPath, "C WebApp"); - Path dPath = jettyBase.resolve("etc/d.xml"); + Path dPath = etc.resolve("d.xml"); writeXmlDisplayName(dPath, "D WebApp"); - assertTrue(Files.exists(propsCoreAFile)); - assertTrue(Files.exists(propsCoreBFile)); - assertTrue(Files.exists(propsCoreCFile)); - assertTrue(Files.exists(propsCoreDFile)); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -208,17 +215,28 @@ public void testPropertyOverriding() throws Exception /** * Test that properties defined in an environment-specific properties file * are used for substitution. - * - * @throws Exception */ @Test public void testPropertySubstitution() throws Exception { Path jettyBase = jetty.getJettyBasePath(); - Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context-sub.xml\ntest.displayName=DisplayName Set By Property", StandardOpenOption.CREATE_NEW); - Files.copy(MavenPaths.findTestResourceFile("etc/core-context-sub.xml"), jettyBase.resolve("etc/core-context-sub.xml"), StandardCopyOption.REPLACE_EXISTING); + + Path environments = jettyBase.resolve("environments"); + FS.ensureDirExists(environments); + + Files.writeString(environments.resolve("core.properties"), + """ + jetty.deploy.environmentXml=etc/core-context-sub.xml + test.displayName=DisplayName Set By Property + """); + + Files.copy(MavenPaths.findTestResourceFile("etc/core-context-sub.xml"), + jettyBase.resolve("etc/core-context-sub.xml"), + StandardCopyOption.REPLACE_EXISTING); + jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); + ContextHandler context = jetty.getContextHandler("/bar"); assertNotNull(context); assertEquals("DisplayName Set By Property", context.getDisplayName()); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java new file mode 100644 index 000000000000..a5bd9ffc03c9 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java @@ -0,0 +1,371 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.Scanner; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +@ExtendWith(WorkDirExtension.class) +public class DefaultProviderTest extends AbstractCleanEnvironmentTest +{ + public WorkDir workDir; + + public static class AssertActionListDefaultProvider extends DefaultProvider + { + Consumer> assertActionList; + + @Override + protected void performActions(List actions) + { + assertActionList.accept(actions); + + // Perform post performActions cleanup that normally happens + for (DeployAction action : actions) + { + action.getApp().resetStates(); + } + } + } + + @Test + public void testActionListNewXmlOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); + defaultProvider.addMonitoredDirectory(dir); + + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), Matchers.contains(xml)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlThenRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("foo.xml"); + Files.writeString(xml, "XML for foo", UTF_8); + + AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); + defaultProvider.addMonitoredDirectory(dir); + + // Initial deployment. + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("foo")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), contains(xml)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.ADDED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + + // Removed only deployment file. + Files.deleteIfExists(xml); + changeSet.clear(); + changeSet.put(xml, Scanner.Notification.REMOVED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("changedUnits.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("foo")); + assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.REMOVED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), contains(xml)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.REMOVED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(nullValue())); + }; + + defaultProvider.pathsChanged(changeSet); + } + + @Test + public void testActionListNewXmlAndWarOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); + defaultProvider.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.ADDED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlAndWarWithXmlUpdate() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); + defaultProvider.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.ADDED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + + // Change/Touch war + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.UNCHANGED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.CHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.UNCHANGED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.CHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlAndWarWithXmlRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); + defaultProvider.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.ADDED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.ADDED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + + // Change/Touch war and xml + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + changeSet.put(xml, Scanner.Notification.CHANGED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.CHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.CHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); + }; + + defaultProvider.pathsChanged(changeSet); + + // Delete XML (now only war exists) + Files.deleteIfExists(xml); + changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.REMOVED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.REMOVED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.UNCHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(war)); + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.CHANGED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(DefaultApp.State.REMOVED)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.UNCHANGED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(war)); + }; + + defaultProvider.pathsChanged(changeSet); + + // Delete WAR + Files.deleteIfExists(war); + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.REMOVED); + + defaultProvider.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.getName(), is("bar")); + assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); + assertThat("action.app.state", action.getApp().getState(), is(DefaultApp.State.REMOVED)); + assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(war)); + assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(DefaultApp.State.REMOVED)); + assertThat("action.app.mainPath", action.getApp().getMainPath(), is(nullValue())); + }; + + defaultProvider.pathsChanged(changeSet); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DeployActionComparatorTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DeployActionComparatorTest.java new file mode 100644 index 000000000000..e859e5a1c2a1 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DeployActionComparatorTest.java @@ -0,0 +1,196 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class DeployActionComparatorTest +{ + @Test + public void testAddOnly() + { + DefaultApp appFoo = new DefaultApp("foo"); + appFoo.putPath(Path.of("foo.xml"), DefaultApp.State.ADDED); + DefaultApp appBar = new DefaultApp("bar"); + appBar.putPath(Path.of("bar.xml"), DefaultApp.State.ADDED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.ADD, new DefaultApp("bar"))); + actions.add(new DeployAction(DeployAction.Type.ADD, new DefaultApp("foo"))); + + actions.sort(new DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expected in ascending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("bar")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("foo")); + } + + @Test + public void testRemoveOnly() + { + DefaultApp appFoo = new DefaultApp("foo"); + appFoo.putPath(Path.of("foo.xml"), DefaultApp.State.REMOVED); + + DefaultApp appBar = new DefaultApp("bar"); + appBar.putPath(Path.of("bar.xml"), DefaultApp.State.REMOVED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.REMOVE, appFoo)); + actions.add(new DeployAction(DeployAction.Type.REMOVE, appBar)); + + actions.sort(new DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expected in descending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("foo")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("bar")); + } + + @Test + public void testRemoveTwoAndAddTwo() + { + DefaultApp appFoo = new DefaultApp("foo"); + appFoo.putPath(Path.of("foo.xml"), DefaultApp.State.REMOVED); + + DefaultApp appBar = new DefaultApp("bar"); + appBar.putPath(Path.of("bar.xml"), DefaultApp.State.REMOVED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.REMOVE, appFoo)); + actions.add(new DeployAction(DeployAction.Type.ADD, appFoo)); + actions.add(new DeployAction(DeployAction.Type.ADD, appBar)); + actions.add(new DeployAction(DeployAction.Type.REMOVE, appBar)); + + // Perform sort + actions.sort(new DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expecting REMOVE first + + // REMOVE is in descending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("foo")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("bar")); + + // expecting ADD next + + // ADD is in ascending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("bar")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("foo")); + } + + @Test + public void testRemoveFourAndAddTwo() + { + DefaultApp appA = new DefaultApp("app-a"); + appA.putPath(Path.of("app-a.xml"), DefaultApp.State.REMOVED); + + DefaultApp appB = new DefaultApp("app-b"); + appB.putPath(Path.of("app-b.xml"), DefaultApp.State.REMOVED); + + DefaultApp appC = new DefaultApp("app-c"); + appC.putPath(Path.of("app-c.xml"), DefaultApp.State.REMOVED); + + DefaultApp appD = new DefaultApp("app-d"); + appD.putPath(Path.of("app-d.xml"), DefaultApp.State.REMOVED); + + List actions = new ArrayList<>(); + // app A is going through hot-reload + actions.add(new DeployAction(DeployAction.Type.REMOVE, appA)); + actions.add(new DeployAction(DeployAction.Type.ADD, appA)); + // app B is being removed + actions.add(new DeployAction(DeployAction.Type.REMOVE, appB)); + // app C is being removed + actions.add(new DeployAction(DeployAction.Type.REMOVE, appC)); + // app D is going through hot-reload + actions.add(new DeployAction(DeployAction.Type.ADD, appD)); + actions.add(new DeployAction(DeployAction.Type.REMOVE, appD)); + + assertThat(actions.size(), is(6)); + + // Perform sort + actions.sort(new DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expecting REMOVE first + + // REMOVE is in descending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("app-d")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("app-c")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("app-b")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.REMOVE)); + assertThat(action.getApp().getName(), is("app-a")); + + // expecting ADD next + + // ADD is in ascending basename order + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("app-a")); + + action = iterator.next(); + assertThat(action.getType(), is(DeployAction.Type.ADD)); + assertThat(action.getApp().getName(), is("app-d")); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java deleted file mode 100644 index 587c19ed9367..000000000000 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/UnitTest.java +++ /dev/null @@ -1,131 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers; - -import java.nio.file.Path; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -public class UnitTest -{ - @Test - public void testUnitStateUnchanged() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); - unit.putPath(Path.of("test-b"), Unit.State.UNCHANGED); - - assertThat(unit.getState(), is(Unit.State.UNCHANGED)); - } - - @Test - public void testUnitStateInitialEmpty() - { - Unit unit = new Unit("test"); - // intentionally empty of Paths - - assertThat(unit.getState(), is(Unit.State.REMOVED)); - } - - @Test - public void testUnitStatePutThenRemoveAll() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.ADDED); - assertThat(unit.getState(), is(Unit.State.ADDED)); - - // Now it gets flagged as removed. (eg: by a Scanner change) - unit.putPath(Path.of("test-a"), Unit.State.REMOVED); - // Then it gets processed, which results in a state reset. - unit.resetStates(); - - // The resulting Unit should have no paths, and be flagged as removed. - assertThat(unit.getPaths().size(), is(0)); - assertThat(unit.getState(), is(Unit.State.REMOVED)); - } - - @Test - public void testUnitStateAddedOnly() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.ADDED); - unit.putPath(Path.of("test-b"), Unit.State.ADDED); - - assertThat(unit.getState(), is(Unit.State.ADDED)); - } - - @Test - public void testUnitStateAddedRemoved() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.REMOVED); // existing file removed in this scan - unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } - - @Test - public void testUnitStateAddedChanged() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.CHANGED); // existing file changed in this scan - unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } - - @Test - public void testUnitStateUnchangedAdded() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan - unit.putPath(Path.of("test-b"), Unit.State.ADDED); // new file introduced in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } - - @Test - public void testUnitStateUnchangedChanged() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan - unit.putPath(Path.of("test-b"), Unit.State.CHANGED); // existing file changed in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } - - @Test - public void testUnitStateUnchangedRemoved() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan - unit.putPath(Path.of("test-b"), Unit.State.REMOVED); // existing file removed in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } - - @Test - public void testUnitStateUnchangedRemovedAdded() - { - Unit unit = new Unit("test"); - unit.putPath(Path.of("test-a"), Unit.State.UNCHANGED); // existed in previous scan - unit.putPath(Path.of("test-b"), Unit.State.REMOVED); // existing file changed in this scan event - unit.putPath(Path.of("test-c"), Unit.State.ADDED); // new file introduced in this scan event - - assertThat(unit.getState(), is(Unit.State.CHANGED)); - } -} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java deleted file mode 100644 index f7792ab139f1..000000000000 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/internal/DeploymentUnitsTest.java +++ /dev/null @@ -1,255 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.providers.internal; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.jetty.deploy.providers.Unit; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -import org.eclipse.jetty.util.Scanner; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.is; - -@ExtendWith(WorkDirExtension.class) -public class DeploymentUnitsTest -{ - public WorkDir workDir; - - @Test - public void testNewXmlOnly() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - - DeploymentUnits units = new DeploymentUnits(); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), contains(xml)); - }); - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - } - - @Test - public void testXmlThenRemoved() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("foo.xml"); - Files.writeString(xml, "XML for foo", UTF_8); - - DeploymentUnits units = new DeploymentUnits(); - - // Initial deployment. - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("foo")); - assertThat("changedUnit[foo].state", unit.getState(), is(Unit.State.ADDED)); - assertThat("changedUnit[foo].paths", unit.getPaths().keySet(), contains(xml)); - assertThat("changedUnit[foo].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - - // Removed deployment. - Files.deleteIfExists(xml); - changeSet.clear(); - changeSet.put(xml, Scanner.Notification.REMOVED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("foo")); - assertThat("changedUnit[foo].state", unit.getState(), is(Unit.State.REMOVED)); - assertThat("changedUnit[foo].paths", unit.getPaths().keySet(), contains(xml)); - assertThat("changedUnit[foo].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.REMOVED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - } - - @Test - public void testNewXmlAndWarOnly() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - DeploymentUnits units = new DeploymentUnits(); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - } - - @Test - public void testXmlAndWarWithXmlUpdate() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - DeploymentUnits units = new DeploymentUnits(); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - - // Change/Touch war - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.CHANGED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.UNCHANGED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.CHANGED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - } - - @Test - public void testXmlAndWarWithXmlRemoved() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - DeploymentUnits units = new DeploymentUnits(); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.ADDED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.ADDED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - - // Change/Touch war and xml - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.CHANGED); - changeSet.put(xml, Scanner.Notification.CHANGED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.CHANGED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.CHANGED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - - // Delete XML - Files.deleteIfExists(xml); - changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.REMOVED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.CHANGED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("changedUnit[bar].paths[xml].state", unit.getPaths().get(xml), is(Unit.State.REMOVED)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.UNCHANGED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - - // Delete WAR - Files.deleteIfExists(war); - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.REMOVED); - units.setListener(changedUnits -> - { - assertThat("changedUnits.size", changedUnits.size(), is(1)); - Unit unit = changedUnits.get(0); - assertThat("changedUnit[0].basename", unit.getBaseName(), is("bar")); - assertThat("changedUnit[bar].state", unit.getState(), is(Unit.State.REMOVED)); - assertThat("changedUnit[bar].paths", unit.getPaths().keySet(), containsInAnyOrder(war)); - assertThat("changedUnit[bar].paths[war].state", unit.getPaths().get(war), is(Unit.State.REMOVED)); - }); - units.pathsChanged(changeSet); - units.getUnits().forEach(Unit::resetStates); - } -} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java index 9822961c6657..601eb57d63a1 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java @@ -17,7 +17,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; -import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLConnection; @@ -141,11 +140,6 @@ public void addConfiguration(Path xmlConfigFile) addConfiguration(ResourceFactory.root().newResource(xmlConfigFile)); } - public void addConfiguration(String testConfigName) throws MalformedURLException - { - addConfiguration(MavenPaths.findTestResourceFile(testConfigName)); - } - public void addConfiguration(Resource xmlConfig) { _xmlConfigurations.add(xmlConfig); diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml index 748ab6ecbda8..d07554671715 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -1,21 +1,18 @@ - + + + core + core - - - core - - - - - - - - + + + + + diff --git a/jetty-core/jetty-deploy/src/test/resources/webapps/bar-core-context.properties b/jetty-core/jetty-deploy/src/test/resources/webapps/bar-core-context.properties index 2ad513333744..ab4fbf31c1bf 100644 --- a/jetty-core/jetty-deploy/src/test/resources/webapps/bar-core-context.properties +++ b/jetty-core/jetty-deploy/src/test/resources/webapps/bar-core-context.properties @@ -1,2 +1,2 @@ -environment: core -jetty.deploy.contextHandlerClass: org.eclipse.jetty.deploy.BarContextHandler \ No newline at end of file +environment=core +jetty.deploy.contextHandlerClass=org.eclipse.jetty.deploy.BarContextHandler \ No newline at end of file diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml similarity index 77% rename from jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml rename to jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml index 7adf72376086..1c0886e12019 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml @@ -3,11 +3,11 @@ - + core - + diff --git a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod similarity index 69% rename from jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod rename to jetty-core/jetty-server/src/main/config/modules/core-deploy.mod index bf893a6f3cf8..6e02d6341f04 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/core-deploy.mod +++ b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod @@ -7,14 +7,9 @@ core [depend] deploy -[lib] - -[files] -webapps/ - [xml] etc/jetty-core-deploy.xml [ini-template] ## Default ContextHandler class for "core" environment deployments -# contextHandlerClass=org.eclipse.jetty.server.handler.ResourceHandler$ResourceContext +# contextHandlerClass=org.eclipse.jetty.server.handler.CoreWebAppContext diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java new file mode 100644 index 000000000000..e7380004715c --- /dev/null +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java @@ -0,0 +1,214 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.server.handler; + +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.Resources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A classloader isolated Core WebApp. + * + *

        + * The Base Resource represents the metadata base that defines this {@code CoreWebAppContext}. + *

        + *

        + * The metadata base can be a directory on disk, or a non-traditional {@code war} file with the following contents. + *

        + *
          + *
        • {@code /lib/*.jar} - the jar files for the classloader of this webapp
        • + *
        • {@code /classes/} - the raw class files for this webapp
        • + *
        • {@code /static/} - the static content to serve for this webapp
        • + *
        + *

        + * Note: if using the non-traditional {@code war} as your metadata base, the + * existence of {@code /lib/*.jar} files means the archive will be + * unpacked into the temp directory defined by this core webapp. + *

        + */ +public class CoreWebAppContext extends ContextHandler +{ + private static final Logger LOG = LoggerFactory.getLogger(CoreWebAppContext.class); + private static final String ORIGINAL_BASE_RESOURCE = "org.eclipse.jetty.webapp.originalBaseResource"; + private List _extraClasspath; + private boolean _builtClassLoader = false; + + public CoreWebAppContext() + { + this("/"); + } + + public CoreWebAppContext(String contextPath) + { + super(); + setContextPath(contextPath); + } + + /** + * @return List of Resources that each will represent a new classpath entry + */ + @ManagedAttribute(value = "extra classpath for context classloader", readonly = true) + public List getExtraClasspath() + { + return _extraClasspath == null ? Collections.emptyList() : _extraClasspath; + } + + /** + * Set the Extra ClassPath via delimited String. + *

        + * This is a convenience method for {@link #setExtraClasspath(List)} + *

        + * + * @param extraClasspath Comma or semicolon separated path of filenames or URLs + * pointing to directories or jar files. Directories should end + * with '/'. + * @see #setExtraClasspath(List) + */ + public void setExtraClasspath(String extraClasspath) + { + setExtraClasspath(getResourceFactory().split(extraClasspath)); + } + + public void setExtraClasspath(List extraClasspath) + { + _extraClasspath = extraClasspath; + } + + public ResourceFactory getResourceFactory() + { + return ResourceFactory.of(this); + } + + protected Resource unpack(Resource dir) throws IOException + { + Path tempDir = getTempDirectory().toPath(); + dir.copyTo(tempDir); + return ResourceFactory.of(this).newResource(tempDir); + } + + protected ClassLoader newClassLoader(Resource base) throws IOException + { + List urls = new ArrayList<>(); + + if (Resources.isDirectory(base)) + { + Resource libDir = base.resolve("lib"); + if (Resources.isDirectory(libDir)) + { + for (Resource entry : libDir.list()) + { + URI uri = entry.getURI(); + if (FileID.isJavaArchive(uri)) + urls.add(uri.toURL()); + } + } + + Resource classesDir = base.resolve("classes"); + if (Resources.isDirectory(classesDir)) + { + urls.add(classesDir.getURI().toURL()); + } + } + + List extraEntries = getExtraClasspath(); + if (extraEntries != null && !extraEntries.isEmpty()) + { + for (Resource entry : extraEntries) + { + urls.add(entry.getURI().toURL()); + } + } + + if (LOG.isDebugEnabled()) + LOG.debug("Core classloader for {}", urls); + + if (urls.isEmpty()) + return Environment.CORE.getClassLoader(); + + return new URLClassLoader(urls.toArray(URL[]::new), Environment.CORE.getClassLoader()); + } + + protected void initWebApp() throws IOException + { + if (getBaseResource() == null) + { + // Nothing to do. + return; + } + + // needs to be a directory (either raw, or archive mounted) + if (!Resources.isDirectory(getBaseResource())) + { + LOG.warn("Invalid Metadata Base Resource (not a directory): {}", getBaseResource()); + return; + } + + Resource dir = getBaseResource(); + // attempt to figure out if the resource is compressed or not + if (!dir.getURI().getScheme().equals("file")) + { + setAttribute(ORIGINAL_BASE_RESOURCE, dir.getURI()); + dir = unpack(dir); + setBaseResource(dir); + } + + Resource staticDir = getBaseResource().resolve("static"); + if (Resources.isDirectory(staticDir)) + { + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setBaseResource(staticDir); + setHandler(resourceHandler); + } + } + + @Override + protected void doStart() throws Exception + { + initWebApp(); + + if (getClassLoader() == null) + { + _builtClassLoader = true; + // Build Classloader (since once wasn't created) + setClassLoader(newClassLoader(getBaseResource())); + } + + super.doStart(); + } + + @Override + protected void doStop() throws Exception + { + if (_builtClassLoader) + { + setClassLoader(null); + } + + super.doStop(); + } +} diff --git a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 9c45a486319d..7494f94621a0 100644 --- a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -435,11 +435,27 @@ public void initializeDefaults(Object object) * @return String to resolved and normalized path, or null if dir or destPath is empty. */ public static String resolvePath(String dir, String destPath) + { + Path resolved = resolvedPath(dir, destPath); + if (resolved == null) + return null; + return resolved.toString(); + } + + /** + * Utility method to resolve a provided path against a directory. + * + * @param dir the directory (should be a directory reference, does not have to exist) + * @param destPath the destination path (can be relative or absolute, syntax depends on OS + FileSystem in use, + * and does not need to exist) + * @return String to resolved and normalized path, or null if dir or destPath is empty. + */ + public static Path resolvedPath(String dir, String destPath) { if (StringUtil.isEmpty(dir) || StringUtil.isEmpty(destPath)) return null; - return Paths.get(dir).resolve(destPath).normalize().toString(); + return Paths.get(dir).resolve(destPath).normalize(); } private static class JettyXmlConfiguration implements ConfigurationProcessor diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 19c0107e843a..26854ef2df23 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -3,7 +3,7 @@ - + ee10 diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index 117abad9dd0c..b80283ce7a34 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -3,7 +3,7 @@ - + ee11 diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index f1b3b32fa3cd..03a047702c7d 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -3,7 +3,7 @@ - + ee8 diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 0de7b4693173..3ab52a5c8977 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -3,7 +3,7 @@ - + ee9 From fbedd51b595a6e4420b3b6b53ca0f5ddf64c5dd0 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 11:01:33 -0600 Subject: [PATCH 039/104] Fixing compilation on OSGI modules --- .../java/org/eclipse/jetty/deploy/App.java | 125 ++---------------- .../jetty/osgi/AbstractContextProvider.java | 5 - .../jetty/osgi/BundleContextProvider.java | 28 ++-- .../jetty/osgi/BundleWebAppProvider.java | 9 +- .../java/org/eclipse/jetty/osgi/OSGiApp.java | 50 ++++--- .../org/eclipse/jetty/osgi/OSGiDeployer.java | 28 ++-- .../eclipse/jetty/osgi/OSGiUndeployer.java | 18 ++- .../jetty/ee10/osgi/boot/EE10Activator.java | 40 +++--- .../jetty/ee10/test/DeploymentErrorTest.java | 23 ++-- .../jetty/ee11/osgi/boot/EE11Activator.java | 40 +++--- .../jetty/ee11/test/DeploymentErrorTest.java | 25 ++-- .../jetty/ee9/osgi/boot/EE9Activator.java | 40 +++--- .../jetty/ee9/test/DeploymentErrorTest.java | 25 ++-- 13 files changed, 197 insertions(+), 259 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java index 7a7db0c75ff5..3b39e77c679a 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java @@ -20,126 +20,17 @@ */ public interface App { + /** + * The application name. + * + * @return the application name. + */ String getName(); /** * Get the active ContextHandler for this App. - * The App is not responsible for creating the ContextHandler, the AppProvider is. + * + * @return the ContextHandler for the App. null means the ContextHandler hasn't been created yet. */ - // TODO: make not default - default ContextHandler getContextHandler() - { - return null; - } - -// private final DeploymentManager _manager; -// private final AppProvider _provider; -// private final Path _path; -// private final Map _properties = new HashMap<>(); -// private ContextHandler _context; -// -// /** -// * Create an App with specified Origin ID and archivePath -// *

        -// * Any properties file that exists with the same {@link FileID#getBasename(Path)} as the -// * filename passed will be used to initialize the properties returned by {@link #getProperties()}. -// * -// * @param manager the deployment manager -// * @param provider the app provider -// * @param path the path to the application directory, war file or XML descriptor -// * @see App#getContextPath() -// */ -// public App(DeploymentManager manager, AppProvider provider, Path path) -// { -// _manager = manager; -// _provider = provider; -// _path = path; -// -// if (path != null) -// { -// try -// { -// String basename = FileID.getBasename(path); -// Path properties = path.getParent().resolve(basename + ".properties"); -// if (Files.exists(properties)) -// { -// try (InputStream stream = Files.newInputStream(properties)) -// { -// Properties p = new Properties(); -// p.load(stream); -// p.stringPropertyNames().forEach(k -> _properties.put(k, p.getProperty(k))); -// } -// } -// } -// catch (Exception e) -// { -// throw new RuntimeException(e); -// } -// } -// } -// -// /** -// * @return The deployment manager -// */ -// public DeploymentManager getDeploymentManager() -// { -// return _manager; -// } -// -// /** -// * @return The AppProvider -// */ -// public AppProvider getAppProvider() -// { -// return _provider; -// } -// -// protected ContextHandler newContextHandler() -// { -// return getDeploymentManager().getContextHandlerFactory().newContextHandler(this); -// } -// -// /** -// * Get ContextHandler for the App. -// * -// * Create it if needed. -// * -// * @return the {@link ContextHandler} to use for the App when fully started. -// * (Portions of which might be ignored when App is not yet -// * {@link AppLifeCycle#DEPLOYED} or {@link AppLifeCycle#STARTED}) -// * @throws Exception if unable to get the context handler -// */ -// public ContextHandler getContextHandler() throws Exception -// { -// if (_context == null) -// _context = newContextHandler(); -// return _context; -// } -// -// /** -// * The context path {@link App} relating to how it is installed on the -// * jetty server side. -// * -// * @return the contextPath for the App -// */ -// public String getContextPath() -// { -// return _context == null ? null : _context.getContextPath(); -// } -// -// /** -// * The origin of this {@link App} as specified by the {@link AppProvider} -// * -// * @return String representing the origin of this app. -// */ -// public Path getPath() -// { -// return _path; -// } -// -// @Override -// public String toString() -// { -// return "App@%x[%s,%s,%s]".formatted(hashCode(), getEnvironmentName(), _context, _path); -// } + ContextHandler getContextHandler(); } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 1e1e2ad7148f..96dd6fc1698c 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -63,7 +63,6 @@ public Attributes getAttributes() return _attributes; } - @Override public ContextHandler createContextHandler(App app) throws Exception { if (app == null) @@ -71,7 +70,6 @@ public ContextHandler createContextHandler(App app) throws Exception //Create a ContextHandler suitable to deploy in OSGi ContextHandler h = _contextFactory.createContextHandler(this, app); - return h; } @@ -81,11 +79,8 @@ public void setDeploymentManager(DeploymentManager deploymentManager) _deploymentManager = deploymentManager; } - @Override public String getEnvironmentName() { - // TODO: when AppProvider.getEnvironmentName is eventually removed, leave this method here for - // these OSGI based AppProviders to use. return _environment; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index 1f76665fcd35..7bc69b407c5f 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map; -import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.StringUtil; @@ -45,9 +44,9 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu { private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); - private Map _appMap = new HashMap<>(); + private Map _appMap = new HashMap<>(); - private Map> _bundleMap = new HashMap<>(); + private Map> _bundleMap = new HashMap<>(); private ServiceRegistration _serviceRegForBundles; @@ -77,7 +76,7 @@ public Object addingBundle(Bundle bundle, BundleEvent event) { try { - String serverName = (String)bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); + String serverName = bundle.getHeaders().get(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME); if ((StringUtil.isBlank(serverName) && _serverName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) || (!StringUtil.isBlank(serverName) && (serverName.equals(_serverName)))) { @@ -121,7 +120,7 @@ protected void doStart() throws Exception _tracker.open(); //register as an osgi service for deploying contexts defined in a bundle, advertising the name of the jetty Server instance we are related to - Dictionary properties = new Hashtable(); + Dictionary properties = new Hashtable<>(); properties.put(OSGiServerConstants.MANAGED_JETTY_SERVER_NAME, serverName); _serviceRegForBundles = FrameworkUtil.getBundle(this.getClass()).getBundleContext().registerService(BundleProvider.class.getName(), this, properties); super.doStart(); @@ -181,26 +180,21 @@ public boolean bundleAdded(Bundle bundle) throws Exception String[] tmp = contextFiles.split("[,;]"); for (String contextFile : tmp) - { - OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle); + { + OSGiApp app = new OSGiApp(bundle); URI contextFilePath = Util.resolvePathAsLocalizedURI(contextFile, app.getBundle(), jettyHomePath); //set up the single context file for this deployment app.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toString()); - + app.setContextHandler(createContextHandler(app)); _appMap.put(app.getPath(), app); - List apps = _bundleMap.get(bundle); - if (apps == null) - { - apps = new ArrayList(); - _bundleMap.put(bundle, apps); - } + List apps = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); apps.add(app); getDeploymentManager().addApp(app); added = true; } - return added; //true if even 1 context from this bundle was added + return added; // true if even 1 context from this bundle was added } /** @@ -212,11 +206,11 @@ public boolean bundleAdded(Bundle bundle) throws Exception @Override public boolean bundleRemoved(Bundle bundle) throws Exception { - List apps = _bundleMap.remove(bundle); + List apps = _bundleMap.remove(bundle); boolean removed = false; if (apps != null) { - for (App app : apps) + for (OSGiApp app : apps) { if (_appMap.remove(app.getPath()) != null) { diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index 843e427551fb..d70bd829aec4 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -185,8 +185,9 @@ public boolean bundleAdded(Bundle bundle) throws Exception //TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all //exceptions inside the impl of addApp. Need to send the Event and also register as a service //only if the deployment succeeded - OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle); + OSGiApp app = new OSGiApp(bundle); app.setPathToResourceBase(staticResourcesLocation); + app.setContextHandler(createContextHandler(app)); _bundleMap.put(bundle, app); getDeploymentManager().addApp(app); return true; @@ -196,7 +197,8 @@ public boolean bundleAdded(Bundle bundle) throws Exception if (bundle.getEntry("/WEB-INF/web.xml") != null) { String base = "."; - OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle); + OSGiApp app = new OSGiApp(bundle); + app.setContextHandler(createContextHandler(app)); app.setPathToResourceBase(base); _bundleMap.put(bundle, app); getDeploymentManager().addApp(app); @@ -208,8 +210,9 @@ public boolean bundleAdded(Bundle bundle) throws Exception { //Could be a static webapp with no web.xml String base = "."; - OSGiApp app = new OSGiApp(getDeploymentManager(), this, bundle); + OSGiApp app = new OSGiApp(bundle); app.setPathToResourceBase(base); + app.setContextHandler(createContextHandler(app)); _bundleMap.put(bundle, app); getDeploymentManager().addApp(app); return true; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java index 5f2fa1fad017..2e8b5d6a8e4d 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java @@ -21,10 +21,9 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.Objects; +import java.util.Properties; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; -import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandler; @@ -42,11 +41,14 @@ * * Base class representing info about a WebAppContext/ContextHandler to be deployed into jetty. */ -public class OSGiApp extends App +public class OSGiApp implements App { private static final Logger LOG = LoggerFactory.getLogger(OSGiApp.class); + private final String _bundleName; + private final Path _bundlePath; protected Bundle _bundle; + protected Properties _properties; protected ServiceRegistration _registration; protected ContextHandler _contextHandler; protected String _pathToResourceBase; @@ -114,21 +116,16 @@ private static String getContextPath(Bundle bundle) return contextPath; } - - /** - * @param manager the DeploymentManager to which to deploy - * @param provider the provider that discovered the context/webapp - * @param bundle the bundle associated with the context/webapp - */ - public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle) + + public OSGiApp(Bundle bundle) throws Exception { - super(manager, provider, getBundlePath(bundle)); - + _bundleName = bundle.getSymbolicName(); + _bundlePath = getBundlePath(bundle); _bundle = Objects.requireNonNull(bundle); _bundleResource = getBundleAsResource(bundle); - - //copy selected bundle headers into the properties + + // copy selected bundle headers into the properties Dictionary headers = bundle.getHeaders(); Enumeration keys = headers.keys(); while (keys.hasMoreElements()) @@ -136,7 +133,9 @@ public OSGiApp(DeploymentManager manager, AppProvider provider, Bundle bundle) String key = keys.nextElement(); String val = headers.get(key); if (Deployable.ENVIRONMENT.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_ENVIRONMENT.equalsIgnoreCase(key)) + { getProperties().put(Deployable.ENVIRONMENT, val); + } else if (Deployable.DEFAULTS_DESCRIPTOR.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH.equalsIgnoreCase(key)) { getProperties().put(Deployable.DEFAULTS_DESCRIPTOR, val); @@ -155,17 +154,31 @@ else if (OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH.equalsIgnoreCase(key)) setContextPath(getContextPath(bundle)); } + @Override + public String getName() + { + return _bundleName; + } + + public Path getPath() + { + return _bundlePath; + } + + public Properties getProperties() + { + return _properties; + } + public Resource getBundleResource() { return _bundleResource; } @Override - public ContextHandler getContextHandler() throws Exception + public ContextHandler getContextHandler() { - if (_contextHandler == null) - _contextHandler = getAppProvider().createContextHandler(this); - return _contextHandler; + return _contextHandler; } public void setContextHandler(ContextHandler contextHandler) @@ -183,7 +196,6 @@ public void setPathToResourceBase(String path) _pathToResourceBase = path; } - @Override public String getContextPath() { return _contextPath; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java index 032cf76f6249..83569c8c1dd3 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java @@ -14,10 +14,12 @@ package org.eclipse.jetty.osgi; import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardDeployer; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; /** * OSGiDeployer @@ -27,8 +29,7 @@ */ public class OSGiDeployer extends StandardDeployer { - - private Server _server; + private final Server _server; public OSGiDeployer(Server server) { @@ -36,39 +37,44 @@ public OSGiDeployer(Server server) } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { - //TODO how to NOT send this event if its not a webapp: + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + return; + + //TODO how to NOT send this event if its not a webapp: //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler) if (!(app instanceof OSGiApp)) { - doProcessBinding(node, app); + doProcessBinding(deploymentManager, node, app); } else { - EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath()); + String contextPath = contextHandler.getContextPath(); + EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, ((OSGiApp)app).getBundle(), contextPath); try { - doProcessBinding(node, app); + doProcessBinding(deploymentManager, node, app); ((OSGiApp)app).registerAsOSGiService(); - EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath()); + EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, ((OSGiApp)app).getBundle(), contextPath); } catch (Exception e) { - EventSender.getInstance().send(EventSender.FAILED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath()); + EventSender.getInstance().send(EventSender.FAILED_EVENT, ((OSGiApp)app).getBundle(), contextPath); throw e; } } } - protected void doProcessBinding(Node node, App app) throws Exception + protected void doProcessBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { ClassLoader old = Thread.currentThread().getContextClassLoader(); ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); Thread.currentThread().setContextClassLoader(cl); try { - super.processBinding(node, app); + super.processBinding(deploymentManager, node, app); } finally { diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java index 9fa1a22b5cb6..dd08f3c7fc78 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java @@ -14,10 +14,12 @@ package org.eclipse.jetty.osgi; import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardUndeployer; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; /** * OSGiUndeployer @@ -27,7 +29,7 @@ */ public class OSGiUndeployer extends StandardUndeployer { - private Server _server; + private final Server _server; public OSGiUndeployer(Server server) { @@ -35,21 +37,27 @@ public OSGiUndeployer(Server server) } @Override - public void processBinding(Node node, App app) throws Exception + public void processBinding(DeploymentManager deploymentManager, Node node, App app) throws Exception { - EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath()); + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + return; + + String contextPath = contextHandler.getContextPath(); + + EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, ((OSGiApp)app).getBundle(), contextPath); ClassLoader old = Thread.currentThread().getContextClassLoader(); ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); Thread.currentThread().setContextClassLoader(cl); try { - super.processBinding(node, app); + super.processBinding(deploymentManager, node, app); } finally { Thread.currentThread().setContextClassLoader(old); } - EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((OSGiApp)app).getBundle(), app.getContextPath()); + EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, ((OSGiApp)app).getBundle(), contextPath); ((OSGiApp)app).deregisterAsOSGiService(); } } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index 232c43599a9a..ee94d116efe7 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -150,10 +150,16 @@ public Object addingService(ServiceReference sr) { for (AppProvider provider : deployer.get().getAppProviders()) { - if (BundleContextProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - contextProvider = BundleContextProvider.class.cast(provider); - if (BundleWebAppProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - webAppProvider = BundleWebAppProvider.class.cast(provider); + if (provider instanceof BundleContextProvider bundleContextProvider) + { + if (bundleContextProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + contextProvider = bundleContextProvider; + } + if (provider instanceof BundleWebAppProvider bundleWebAppProvider) + { + if (bundleWebAppProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + webAppProvider = bundleWebAppProvider; + } } if (contextProvider == null) { @@ -263,7 +269,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -272,7 +278,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -284,7 +290,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -299,9 +305,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -358,7 +364,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -367,7 +373,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -411,7 +417,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -452,7 +458,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -474,9 +480,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; @@ -552,7 +558,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } //web.xml - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); @@ -561,7 +567,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } // webdefault-ee10.xml - tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index b7daf2bb4952..6b7922397adb 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.ContextProvider; +import org.eclipse.jetty.deploy.providers.DefaultProvider; import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -53,7 +53,6 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.ResourceFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -82,8 +81,6 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception connector.setPort(0); server.addConnector(connector); - ResourceFactory resourceFactory = ResourceFactory.of(server); - // Empty contexts collections ContextHandlerCollection contexts = new ContextHandlerCollection(); @@ -106,11 +103,10 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - ContextProvider appProvider = new ContextProvider(); + DefaultProvider appProvider = new DefaultProvider(); appProvider.configureEnvironment("ee10"); - appProvider.setScanInterval(1); - appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); + appProvider.addMonitoredDirectory(docroots); deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); @@ -313,7 +309,10 @@ private App findApp(String contextPath, List apps) { for (App app : apps) { - if (contextPath.equals(app.getContextPath())) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + continue; // skip + if (contextPath.equals(contextHandler.getContextPath())) return app; } return null; @@ -379,9 +378,13 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, App app) { - if (app.getContextPath().equalsIgnoreCase(expectedContextPath)) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + return; + + if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) { diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 0982628430f4..6d1bfbabcb62 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -150,10 +150,16 @@ public Object addingService(ServiceReference sr) { for (AppProvider provider : deployer.get().getAppProviders()) { - if (BundleContextProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - contextProvider = BundleContextProvider.class.cast(provider); - if (BundleWebAppProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - webAppProvider = BundleWebAppProvider.class.cast(provider); + if (provider instanceof BundleContextProvider bundleContextProvider) + { + if (bundleContextProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + contextProvider = bundleContextProvider; + } + if (provider instanceof BundleWebAppProvider bundleWebAppProvider) + { + if (bundleWebAppProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + webAppProvider = bundleWebAppProvider; + } } if (contextProvider == null) { @@ -263,7 +269,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -272,7 +278,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -284,7 +290,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -299,9 +305,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -358,7 +364,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -367,7 +373,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -411,7 +417,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -452,7 +458,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -474,9 +480,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; @@ -552,7 +558,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } //web.xml - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); @@ -561,7 +567,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } // webdefault-ee11.xml - tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index 60ab0fa816f9..b39e0e324982 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.ContextProvider; +import org.eclipse.jetty.deploy.providers.DefaultProvider; import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -53,7 +53,6 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.ResourceFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -82,8 +81,6 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception connector.setPort(0); server.addConnector(connector); - ResourceFactory resourceFactory = ResourceFactory.of(server); - // Empty contexts collections ContextHandlerCollection contexts = new ContextHandlerCollection(); @@ -106,11 +103,10 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - ContextProvider appProvider = new ContextProvider(); - Environment.ensure("ee11"); - + DefaultProvider appProvider = new DefaultProvider(); + appProvider.configureEnvironment("ee11"); appProvider.setScanInterval(1); - appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); + appProvider.addMonitoredDirectory(docroots); deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); @@ -313,7 +309,10 @@ private App findApp(String contextPath, List apps) { for (App app : apps) { - if (contextPath.equals(app.getContextPath())) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + continue; // skip + if (contextPath.equals(contextHandler.getContextPath())) return app; } return null; @@ -379,9 +378,13 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, App app) { - if (app.getContextPath().equalsIgnoreCase(expectedContextPath)) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + return; + + if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) { diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index cb28ef215bb8..e01003256e2e 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -149,10 +149,16 @@ public Object addingService(ServiceReference sr) { for (AppProvider provider : deployer.get().getAppProviders()) { - if (BundleContextProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - contextProvider = BundleContextProvider.class.cast(provider); - if (BundleWebAppProvider.class.isInstance(provider) && ENVIRONMENT.equalsIgnoreCase(provider.getEnvironmentName())) - webAppProvider = BundleWebAppProvider.class.cast(provider); + if (provider instanceof BundleContextProvider bundleContextProvider) + { + if (bundleContextProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + contextProvider = bundleContextProvider; + } + if (provider instanceof BundleWebAppProvider bundleWebAppProvider) + { + if (bundleWebAppProvider.getEnvironmentName().equalsIgnoreCase(ENVIRONMENT)) + webAppProvider = bundleWebAppProvider; + } } if (contextProvider == null) { @@ -261,7 +267,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -270,7 +276,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -282,7 +288,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -297,9 +303,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -356,7 +362,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)app.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -365,7 +371,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -408,7 +414,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -449,7 +455,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -471,9 +477,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", osgiApp.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)osgiApp.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; @@ -549,7 +555,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } //web.xml - String tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); @@ -558,7 +564,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App } // webdefault-ee9.xml - tmp = osgiApp.getProperties().get(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index 34fb6841dfe9..ada52d250fab 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.ContextProvider; +import org.eclipse.jetty.deploy.providers.DefaultProvider; import org.eclipse.jetty.ee9.webapp.AbstractConfiguration; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -50,7 +50,6 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; -import org.eclipse.jetty.util.resource.ResourceFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -80,8 +79,6 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro connector.setPort(0); server.addConnector(connector); - ResourceFactory resourceFactory = ResourceFactory.of(server); - // Empty contexts collections contexts = new ContextHandlerCollection(); @@ -101,9 +98,9 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - ContextProvider appProvider = new ContextProvider(); - Environment.ensure("ee9"); - appProvider.setMonitoredDirResource(resourceFactory.newResource(docroots)); + DefaultProvider appProvider = new DefaultProvider(); + appProvider.configureEnvironment("ee9"); + appProvider.addMonitoredDirectory(docroots); appProvider.setScanInterval(1); deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); @@ -330,7 +327,11 @@ private App findApp(String contextPath, List apps) { for (App app : apps) { - if (contextPath.equals(app.getContextPath())) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + continue; // skip + + if (contextPath.equals(contextHandler.getContextPath())) return app; } return null; @@ -396,9 +397,13 @@ public String[] getBindingTargets() } @Override - public void processBinding(Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, App app) { - if (app.getContextPath().equalsIgnoreCase(expectedContextPath)) + ContextHandler contextHandler = app.getContextHandler(); + if (contextHandler == null) + return; + + if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) { From 4bb0850b547fe9e0eb878a19c5e05e9c5d136514 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 13:01:52 -0600 Subject: [PATCH 040/104] Fixing deployer XML id --- .../deploy/providers/DefaultProvider.java | 10 +++- .../src/main/config/etc/jetty-core-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 56 ++++++++++--------- .../src/main/config/etc/jetty-ee11-deploy.xml | 56 ++++++++++--------- .../src/main/config/etc/jetty-ee8-deploy.xml | 56 ++++++++++--------- .../src/main/config/etc/jetty-ee9-deploy.xml | 56 ++++++++++--------- 6 files changed, 125 insertions(+), 111 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index f6ff93c8fd01..f13997c49f83 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -151,6 +151,8 @@ private static String asStringList(Collection paths) */ public void addMonitoredDirectory(Path dir) { + if (LOG.isDebugEnabled()) + LOG.debug("Adding monitored directory: {}", dir); if (isStarted()) throw new IllegalStateException("Unable to add monitored directory while running"); monitoredDirs.add(Objects.requireNonNull(dir)); @@ -241,6 +243,8 @@ public Path getEnvironmentsDirectory() public void setEnvironmentsDirectory(Path dir) { + if (LOG.isDebugEnabled()) + LOG.debug("Setting Environments directory: {}", dir); if (isStarted()) throw new IllegalStateException("Unable to add environments directory while running"); environmentsDir = dir; @@ -256,9 +260,11 @@ public void setMonitoredDirectories(Collection directories) if (isStarted()) throw new IllegalStateException("Unable to add monitored directories while running"); + monitoredDirs.clear(); + for (Path dir : directories) { - monitoredDirs.add(Objects.requireNonNull(dir)); + addMonitoredDirectory(dir); } } @@ -509,7 +515,7 @@ protected List buildActionList(List changedApps) protected void doStart() throws Exception { if (LOG.isDebugEnabled()) - LOG.debug("{}.doStart()", this.getClass().getSimpleName()); + LOG.debug("{} doStart()", this); if (monitoredDirs.isEmpty()) throw new IllegalStateException("No monitored dir specified"); diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml index 1c0886e12019..692929f174d3 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml @@ -3,7 +3,7 @@ - + core diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 26854ef2df23..13121724c051 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -3,31 +3,33 @@ - - - ee10 - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee10.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - + + + + ee10 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee10.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + + diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index b80283ce7a34..a4675dc352b4 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -3,31 +3,33 @@ - - - ee11 - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee11.xml - - - - - - - - - .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - + + + + ee11 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee11.xml + + + + + + + + + .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + + diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index 03a047702c7d..b66ea9e09c45 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -3,31 +3,33 @@ - - - ee8 - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee8.xml - - - - - - - - - .*/jetty-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\.jar|.*jsp.jstl-[^/]*\.jar - - - - - - + + + + ee8 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee8.xml + + + + + + + + + .*/jetty-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-[^/]*\.jar|.*jsp.jstl-[^/]*\.jar + + + + + + + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 3ab52a5c8977..19d87fccf0a5 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -3,31 +3,33 @@ - - - ee9 - - - - - - jetty.deploy.defaultsDescriptorPath - jetty.deploy.defaultsDescriptor - - /etc/webdefault-ee9.xml - - - - - - - - - .*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ - - - - - - + + + + ee9 + + + + + + jetty.deploy.defaultsDescriptorPath + jetty.deploy.defaultsDescriptor + + /etc/webdefault-ee9.xml + + + + + + + + + .*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + + + + + + + From 1e2965999dcf9ebf59e6918239ad51736a2fbaaa Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 15:03:06 -0600 Subject: [PATCH 041/104] Fixing null OSGiApp.properties --- .../src/main/java/org/eclipse/jetty/osgi/OSGiApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java index 2e8b5d6a8e4d..f38a593b3a4e 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java @@ -47,8 +47,8 @@ public class OSGiApp implements App private final String _bundleName; private final Path _bundlePath; + private final Properties _properties = new Properties(); protected Bundle _bundle; - protected Properties _properties; protected ServiceRegistration _registration; protected ContextHandler _contextHandler; protected String _pathToResourceBase; From 2acf58e01d9c34b192a8b550d3b6040da0633785 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 16:09:18 -0600 Subject: [PATCH 042/104] More work on test stability --- .../DefaultContextHandlerFactory.java | 2 +- .../src/test/resources/RFC2616Base.xml | 16 +- .../src/test/resources/deploy.xml | 16 +- .../src/test/resources/RFC2616Base.xml | 171 ++++++++++-------- .../src/test/resources/deploy.xml | 16 +- .../src/test/resources/RFC2616Base.xml | 16 +- .../src/test/resources/deploy.xml | 16 +- 7 files changed, 137 insertions(+), 116 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java index c2cd79752539..79d3f43a200b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -170,7 +170,7 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app // via a different means. This is especially important for a deployable App // that is only a .war file (no XML). The eventual WebInfConfiguration // will use this attribute. - app.getAttributes().setAttribute(Deployable.WAR, mainPath.toString()); + deployAttributes.setAttribute(Deployable.WAR, mainPath.toString()); // Initialize any deployable if (context instanceof Deployable deployable) diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 379bd1560e3f..15ea52077b1f 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -96,13 +96,15 @@ - - - - src/test/resources/ - webapp-contexts/RFC2616/ - - + + + + + src/test/resources/ + webapp-contexts/RFC2616/ + + + 1 diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index efb34d961c13..a4a3c530eb65 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -14,13 +14,15 @@ - - - - target - webapps - - + + + + + target + webapps + + + 1 diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 62951771d39f..367b8fdefe20 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -8,92 +8,103 @@ - - - https - - 32768 - 8192 - 8192 - true - true - 1024 - - - - RFC2616 - - + + https + + + + 32768 + 8192 + 8192 + true + true + 1024 - - - - - - - - - - - - - /tests - - - VirtualHost - - - /virtualhost - - virtual - - - - - /tests - /default - - default - - - - - /echo - - echo - - - - - - - ee11 + + - + + + + + - - - - - - - - - - - src/test/resources/ - webapp-contexts/RFC2616/ - + + + + /tests + + + VirtualHost + + + /virtualhost + + + + + virtual + + + + + /tests + /default + + + + default + + + + + /echo + + + + echo + + + + + + + + + ee11 + + + + + + + + + + + + + + + + src/test/resources/ + webapp-contexts/RFC2616/ + + + 1 @@ -102,7 +113,7 @@ true - .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ + .*/jakarta.servlet-api-[^/]*\.jar$|.*wasp-.*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ @@ -112,9 +123,9 @@ - - - + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 23b1f84ef03e..8e2004ccd658 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -14,13 +14,15 @@ - - - - target - webapps - - + + + + + target + webapps + + + 1 diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 51235bd714a7..f80293244b8b 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -80,13 +80,15 @@ - - - - src/test/resources/ - webapp-contexts/RFC2616/ - - + + + + + src/test/resources/ + webapp-contexts/RFC2616/ + + + 1 diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index aea87fa1f578..34c63b1f54aa 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -14,13 +14,15 @@ - - - - target - webapps - - + + + + + target + webapps + + + 1 From 7b5cd4ed90e61c4e183aacf51b97945459fa303f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 23 Jan 2025 17:19:00 -0600 Subject: [PATCH 043/104] Fix directory deployment issue --- .../deploy/providers/DefaultProvider.java | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index f13997c49f83..87b3f5acef1c 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -129,21 +130,12 @@ public class DefaultProvider extends ContainerLifeCycle implements AppProvider, public DefaultProvider() { - this(new MonitoredPathFilter()); + filenameFilter = new MonitoredPathFilter(monitoredDirs); } public DefaultProvider(FilenameFilter filter) { filenameFilter = filter; - setScanInterval(0); - } - - private static String asStringList(Collection paths) - { - return paths.stream() - .sorted(PathCollators.byName(true)) - .map(Path::toString) - .collect(Collectors.joining(", ", "[", "]")); } /** @@ -1023,6 +1015,13 @@ public void setWebInfScanJarPattern(String pattern) public static class MonitoredPathFilter implements FilenameFilter { + private final List monitoredDirs; + + public MonitoredPathFilter(List monitoredDirs) + { + this.monitoredDirs = monitoredDirs; + } + @Override public boolean accept(File dir, String name) { @@ -1031,14 +1030,6 @@ public boolean accept(File dir, String name) Path path = dir.toPath().resolve(name); - // We don't monitor subdirectories. - if (Files.isDirectory(path)) - return false; - - // Synthetic files (like consoles, printers, serial ports, etc) are ignored. - if (!Files.isRegularFile(path)) - return false; - try { // ignore traditional "hidden" path entries. @@ -1053,8 +1044,28 @@ public boolean accept(File dir, String name) // ignore } - // The filetypes that are monitored, and we want updates for when they change. - return FileID.isExtension(name, "jar", "war", "xml", "properties"); + if (Files.isRegularFile(path) && FileID.isExtension(name, "jar", "war", "xml", "properties")) + return true; + + // From this point down, we looking for things that are possible directory deployments. + if (!Files.isDirectory(path)) + return false; + + // Don't deploy monitored paths + if (monitoredDirs.contains(path)) + return false; + + String lowerName = name.toLowerCase(Locale.ENGLISH); + + // is it a nominated config directory + if (lowerName.endsWith(".d")) + return false; + + // ignore source control directories + if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) + return false; + + return true; } } } From 318ab9e0cf057af2bac973298897bf7e02a998ec Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 24 Jan 2025 14:04:19 -0600 Subject: [PATCH 044/104] Fixing "core" deployments --- jetty-core/jetty-deploy/pom.xml | 31 ++++ .../DefaultContextHandlerFactory.java | 46 ++++-- .../deploy/providers/DefaultProvider.java | 34 ++++- .../DefaultProviderCoreWebappTest.java | 138 +++++++++++++++++ .../DefaultProviderRuntimeUpdatesTest.java | 2 +- .../resources/jetty-core-deploy-custom.xml | 2 +- .../org/eclipse/jetty/server/Deployable.java | 2 + .../server/handler/CoreWebAppContext.java | 141 +++++++++++++++--- .../jetty-test-core-example-webapp/pom.xml | 49 ++++++ .../src/main/assembly/core-webapp.xml | 33 ++++ .../main/java/org/example/ExampleHandler.java | 72 +++++++++ .../resources/org/example/example.properties | 1 + jetty-core/jetty-tests/pom.xml | 1 + .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee11-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee8-deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 2 +- .../tests/distribution/DistributionTests.java | 4 +- 24 files changed, 523 insertions(+), 53 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/pom.xml create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/assembly/core-webapp.xml create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleHandler.java create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/resources/org/example/example.properties diff --git a/jetty-core/jetty-deploy/pom.xml b/jetty-core/jetty-deploy/pom.xml index cb7c1a5fd96b..c9544ba40e75 100644 --- a/jetty-core/jetty-deploy/pom.xml +++ b/jetty-core/jetty-deploy/pom.xml @@ -44,6 +44,15 @@ jetty-slf4j-impl test + + + org.eclipse.jetty + jetty-test-core-example-webapp + ${project.version} + core-webapp + zip + test + org.eclipse.jetty.toolchain jetty-test-helper @@ -53,6 +62,28 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-core-webapps + + copy-dependencies + + generate-resources + + core-webapp + zip + ${project.build.directory}/core-webapps/ + false + true + true + true + + + + maven-surefire-plugin diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java index 79d3f43a200b..631026748dd8 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -113,6 +113,9 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app if (!Files.exists(mainPath)) throw new IllegalStateException("App path does not exist " + mainPath); + deployAttributes.setAttribute(Deployable.MAIN_PATH, mainPath); + deployAttributes.setAttribute(Deployable.OTHER_PATHS, app.getPaths().keySet()); + String envName = app.getEnvironmentName(); if (StringUtil.isBlank(envName)) { @@ -172,10 +175,6 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app // will use this attribute. deployAttributes.setAttribute(Deployable.WAR, mainPath.toString()); - // Initialize any deployable - if (context instanceof Deployable deployable) - deployable.initializeDefaults(deployAttributes); - return getContextHandler(context); } finally @@ -227,7 +226,7 @@ public void initializeDefaults(Object context) }); // Run configure against appropriate classloader. - ClassLoader xmlClassLoader = environment.getClassLoader(); + ClassLoader xmlClassLoader = getClassLoader(context, environment); ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(xmlClassLoader); @@ -246,6 +245,21 @@ public void initializeDefaults(Object context) } } + private ClassLoader getClassLoader(Object context, Environment environment) + { + if (context != null) + { + if (context instanceof ContextHandler contextHandler) + { + ClassLoader classLoader = contextHandler.getClassLoader(); + if (classLoader != null) + return classLoader; + } + } + + return environment.getClassLoader(); + } + protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) { if (LOG.isDebugEnabled()) @@ -253,10 +267,13 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path assert contextHandler != null; - if (contextHandler.getBaseResource() == null && Files.isDirectory(path)) + if (contextHandler.getBaseResource() == null) { - ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); - contextHandler.setBaseResource(resourceFactory.newResource(path)); + if (Files.isDirectory(path)) + { + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); + contextHandler.setBaseResource(resourceFactory.newResource(path)); + } } // pass through properties as attributes directly @@ -397,6 +414,9 @@ private ContextHandler getContextHandler(Object context) */ private Object newContextInstance(DefaultProvider provider, Environment environment, App app, Attributes attributes, Path path) throws Exception { + if (LOG.isDebugEnabled()) + LOG.debug("newContextInstance({}, {}, {}, {})", provider, environment, app, path); + Object context = newInstance((String)attributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS)); if (context != null) { @@ -406,18 +426,24 @@ private Object newContextInstance(DefaultProvider provider, Environment environm initializeContextPath(contextHandler, path); initializeContextHandler(contextHandler, path, attributes); - return context; + + // Initialize any deployable + if (context instanceof Deployable deployable) + deployable.initializeDefaults(attributes); } if (FileID.isXml(path)) { - context = applyXml(provider, null, path, environment, attributes); + context = applyXml(provider, context, path, environment, attributes); ContextHandler contextHandler = getContextHandler(context); if (contextHandler == null) throw new IllegalStateException("Unknown context type of " + context); return context; } + if (context != null) + return context; + // fallback to default from environment. context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index 87b3f5acef1c..ab03b2653759 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -371,6 +371,12 @@ public void pathsChanged(Map changeSet) // Using lower-case as defined by System Locale, as the files themselves from System FS. String basename = FileID.getBasename(path).toLowerCase(); + // Strip the ".d" extension on directory basenames + if (Files.isDirectory(path) && FileID.isExtension(path, "d")) + { + basename = basename.substring(0, basename.length() - 2); + } + if (isMonitoredPath(path)) { // we have a normal path entry @@ -929,18 +935,34 @@ public void setContainerScanJarPattern(String pattern) /** * The name of the class that this environment uses to create {@link ContextHandler} - * instances (can be class that implements {@code java.util.function.Supplier} - * as well). + * instances (supports a class that implements {@code java.util.function.Supplier} as well). * *

        - * This is the fallback class used, if the context class itself isn't defined by - * the web application being deployed. + * This is the class used to create a ContextHandler for the environment before + * any XML files are loaded to configure the context. *

        * * @param classname the classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT + * @see Deployable#CONTEXT_HANDLER_CLASS */ public void setContextHandlerClass(String classname) + { + _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); + } + + /** + * The name of the default class that this environment uses to create {@link ContextHandler} + * instances (supports a class that implements {@code java.util.function.Supplier} as well). + * + *

        + * This is the fallback class used, if the context class itself isn't defined by + * the web application being deployed. (such as from an XML definition) + *

        + * + * @param classname the default classname for this environment's context deployable. + * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT + */ + public void setDefaultContextHandlerClass(String classname) { _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); } @@ -1059,7 +1081,7 @@ public boolean accept(File dir, String name) // is it a nominated config directory if (lowerName.endsWith(".d")) - return false; + return true; // ignore source control directories if ("cvs".equals(lowerName) || "cvsroot".equals(lowerName)) diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java new file mode 100644 index 000000000000..3dbe79aa69ba --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java @@ -0,0 +1,138 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy.providers; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.CoreWebAppContext; +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(WorkDirExtension.class) +public class DefaultProviderCoreWebappTest extends AbstractCleanEnvironmentTest +{ + public WorkDir workDir; + private Server server; + + public void startServer(DefaultProvider provider) throws Exception + { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + + DeploymentManager deploymentManager = new DeploymentManager(); + deploymentManager.setContexts(contexts); + deploymentManager.addAppProvider(provider); + server.addBean(deploymentManager); + + server.start(); + } + + @AfterEach + public void stopServer() + { + LifeCycle.stop(server); + } + + @Test + public void testExampleCoreDir() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path webapps = baseDir.resolve("webapps"); + FS.ensureDirExists(webapps); + + Path demoDir = webapps.resolve("demo.d"); + FS.ensureDirExists(demoDir); + + Path srcZip = MavenPaths.targetDir().resolve("core-webapps/jetty-test-core-example-webapp.zip"); + Assertions.assertTrue(Files.exists(srcZip), "Src Zip should exist: " + srcZip); + unpack(srcZip, demoDir); + + // ensure that demo jar isn't in our test/server classpath. + // it should only exist in the jar file on disk. + assertThrows(ClassNotFoundException.class, () -> Class.forName("org.example.ExampleHandler")); + + Path demoXml = webapps.resolve("demo.xml"); + String demoXmlStr = """ + + + + /demo + + + + + """; + Files.writeString(demoXml, demoXmlStr); + + DefaultProvider defaultProvider = new DefaultProvider(); + defaultProvider.addMonitoredDirectory(webapps); + DefaultProvider.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); + coreConfig.setContextHandlerClass(CoreWebAppContext.class.getName()); + + startServer(defaultProvider); + + URI destURI = server.getURI().resolve("/demo/"); + HttpURLConnection http = (HttpURLConnection)destURI.toURL().openConnection(); + assertThat(http.getResponseCode(), is(200)); + String responseBody = IO.toString(http.getInputStream()); + assertThat(responseBody, containsString(Server.getVersion())); + assertThat(responseBody, containsString(destURI.getPath())); + assertThat(responseBody, containsString("it all looks so easy.")); + } + + private void unpack(Path srcPath, Path destPath) throws IOException + { + Map env = new HashMap<>(); + + URI jarUri = URIUtil.uriJarPrefix(srcPath.toUri(), "!/"); + try (FileSystem zipfs = FileSystems.newFileSystem(jarUri, env)) + { + Path root = zipfs.getPath("/"); + IO.copyDir(root, destPath); + } + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java index 958b6b8f096d..1fb284e4e0f5 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java @@ -133,7 +133,7 @@ public void waitForDirectoryScan() * This addresses issue https://github.com/jetty/jetty.project/issues/12543 */ @Test - public void testSelectiveDeploy(WorkDir workDir) throws Exception + public void testRedeployIfAnyTrackedPathChanges(WorkDir workDir) throws Exception { Path testdir = workDir.getEmptyPathDir(); createJettyBase(testdir); diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml index d07554671715..dc6dd93334a4 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -7,7 +7,7 @@ core - + diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index 7cd65f5621b2..7a3d25858e2f 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -68,6 +68,8 @@ public interface Deployable String SCI_ORDER = "jetty.deploy.servletContainerInitializerOrder"; String WAR = "jetty.deploy.war"; String WEBINF_SCAN_JARS = "jetty.deploy.webInfScanJarPattern"; + String MAIN_PATH = "jetty.deploy.paths.main"; + String OTHER_PATHS = "jetty.deploy.paths.other"; void initializeDefaults(Attributes attributes); } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java index e7380004715c..8cf039b2cd99 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java @@ -17,14 +17,22 @@ import java.net.URI; import java.net.URL; import java.net.URLClassLoader; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; +import org.eclipse.jetty.io.RuntimeIOException; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.resource.PathCollators; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.resource.Resources; @@ -51,10 +59,11 @@ * unpacked into the temp directory defined by this core webapp. *

        */ -public class CoreWebAppContext extends ContextHandler +public class CoreWebAppContext extends ContextHandler implements Deployable { private static final Logger LOG = LoggerFactory.getLogger(CoreWebAppContext.class); private static final String ORIGINAL_BASE_RESOURCE = "org.eclipse.jetty.webapp.originalBaseResource"; + private boolean _initialized = false; private List _extraClasspath; private boolean _builtClassLoader = false; @@ -78,6 +87,49 @@ public List getExtraClasspath() return _extraClasspath == null ? Collections.emptyList() : _extraClasspath; } + @Override + public void initializeDefaults(Attributes attributes) + { + try + { + // This CoreWebAppContext is arriving via a Deployer + for (String keyName : attributes.getAttributeNameSet()) + { + Object value = attributes.getAttribute(keyName); + + switch (keyName) + { + case Deployable.OTHER_PATHS -> + { + // The Base Resource (before init) is a nominated directory ("/.d/") + + //noinspection unchecked + java.util.Collection deployablePaths = (java.util.Collection)value; + Optional optionalDir = deployablePaths.stream() + .sorted(PathCollators.byName(true)) + .filter(Files::isDirectory) + .filter(p -> FileID.isExtension(p.getFileName().toString(), "d")) + .findFirst(); + + if (optionalDir.isPresent()) + { + ResourceFactory resourceFactory = ResourceFactory.of(this); + Resource resourceDir = resourceFactory.newResource(optionalDir.get()); + setBaseResource(resourceDir); + } + } + } + } + + // Init the webapp, unpack if necessary, create the classloader, etc. + initWebApp(); + } + catch (IOException e) + { + throw new RuntimeIOException("Unable to init " + this.getClass().getSimpleName(), e); + } + } + /** * Set the Extra ClassPath via delimited String. *

        @@ -155,48 +207,88 @@ protected ClassLoader newClassLoader(Resource base) throws IOException protected void initWebApp() throws IOException { - if (getBaseResource() == null) + if (_initialized) { - // Nothing to do. + if (LOG.isDebugEnabled()) + LOG.debug("Already initialized, not initializing again"); return; } - // needs to be a directory (either raw, or archive mounted) - if (!Resources.isDirectory(getBaseResource())) + if (getBaseResource() == null) { - LOG.warn("Invalid Metadata Base Resource (not a directory): {}", getBaseResource()); + // Nothing to do. return; } - Resource dir = getBaseResource(); - // attempt to figure out if the resource is compressed or not - if (!dir.getURI().getScheme().equals("file")) + Resource base = getBaseResource(); + + if (!Resources.isDirectory(base)) { - setAttribute(ORIGINAL_BASE_RESOURCE, dir.getURI()); - dir = unpack(dir); - setBaseResource(dir); + // see if we can unpack this reference. + if (FileID.isExtension(base.getURI(), "zip", "jar", "war")) + { + // We have an archive that needs to be unpacked + setAttribute(ORIGINAL_BASE_RESOURCE, base.getURI()); + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) + { + URI archiveURI = URIUtil.toJarFileUri(base.getURI()); + Resource mountedArchive = resourceFactory.newResource(archiveURI); + base = unpack(mountedArchive); + setBaseResource(base); + } + } + else + { + throw new IllegalArgumentException("Unrecognized non-directory base resource type: " + base); + } } - Resource staticDir = getBaseResource().resolve("static"); + _initialized = true; + + Resource staticDir = base.resolve("static"); if (Resources.isDirectory(staticDir)) { - ResourceHandler resourceHandler = new ResourceHandler(); - resourceHandler.setBaseResource(staticDir); - setHandler(resourceHandler); + if (!isResourceHandlerAlreadyPresent(staticDir)) + { + ResourceHandler resourceHandler = new ResourceHandler(); + resourceHandler.setBaseResource(staticDir); + setHandler(resourceHandler); + } } - } - - @Override - protected void doStart() throws Exception - { - initWebApp(); + // Don't override the user provided ClassLoader if (getClassLoader() == null) { _builtClassLoader = true; - // Build Classloader (since once wasn't created) setClassLoader(newClassLoader(getBaseResource())); } + } + + private boolean isResourceHandlerAlreadyPresent(Resource staticDir) + { + boolean alreadyExists = false; + for (Handler handler : getHandlers()) + { + if (handler instanceof ResourceHandler resourceHandler) + { + Resource baseResource = resourceHandler.getBaseResource(); + if (baseResource != null) + { + URI baseResourceURI = baseResource.getURI(); + if (baseResourceURI.equals(staticDir.getURI())) + { + alreadyExists = true; + } + } + } + } + return alreadyExists; + } + + @Override + protected void doStart() throws Exception + { + initWebApp(); super.doStart(); } @@ -207,8 +299,11 @@ protected void doStop() throws Exception if (_builtClassLoader) { setClassLoader(null); + _builtClassLoader = false; } + _initialized = false; + super.doStop(); } } diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/pom.xml b/jetty-core/jetty-tests/jetty-test-core-example-webapp/pom.xml new file mode 100644 index 000000000000..752683bfd7da --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + org.eclipse.jetty + jetty-tests + 12.1.0-SNAPSHOT + + + jetty-test-core-example-webapp + jar + Core :: Tests :: Core WebApp Example + + + true + + + + + org.eclipse.jetty + jetty-server + provided + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + create-core-webapp + + single + + package + + + src/main/assembly/core-webapp.xml + + + + + + + + + diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/assembly/core-webapp.xml b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/assembly/core-webapp.xml new file mode 100644 index 000000000000..5638196a3bfa --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/assembly/core-webapp.xml @@ -0,0 +1,33 @@ + + core-webapp + + zip + + false + + + ${basedir}/${project.build.outputDirectory} + /classes + + **/*.* + + + WEB-INF/lib/** + WEB-INF/jetty-web.xml + + + + ${basedir}/src/main/static + /static + + **/*.* + + + + + + /lib + false + + + diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleHandler.java b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleHandler.java new file mode 100644 index 000000000000..f0de9a866645 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleHandler.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Callback; + +/** + * An example Handler that is used for testing "core" webapp deployment. + */ +public class ExampleHandler extends Handler.Abstract +{ + private final String message; + + public ExampleHandler() + { + Properties props = new Properties(); + URL url = this.getClass().getResource("example.properties"); + if (url != null) + { + try (InputStream in = url.openStream()) + { + props.load(in); + this.message = props.getProperty("message"); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + else + { + this.message = "Unable to find example.properties"; + } + } + + @Override + public boolean handle(Request request, Response response, Callback callback) + { + Server server = request.getConnectionMetaData().getConnector().getServer(); + String body = """ + Server.info=%s + request.uri=%s + message=%s + """.formatted( + server.getServerInfo(), + request.getHttpURI().toURI().toASCIIString(), + message); + Content.Sink.write(response, true, body, callback); + return true; + } +} diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/resources/org/example/example.properties b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/resources/org/example/example.properties new file mode 100644 index 000000000000..ce4740329d79 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/resources/org/example/example.properties @@ -0,0 +1 @@ +message=On the other side of the screen, it all looks so easy. \ No newline at end of file diff --git a/jetty-core/jetty-tests/pom.xml b/jetty-core/jetty-tests/pom.xml index e58b2a3105a4..370f4d2b5e41 100644 --- a/jetty-core/jetty-tests/pom.xml +++ b/jetty-core/jetty-tests/pom.xml @@ -15,6 +15,7 @@ jetty-test-client-transports jetty-test-jmx jetty-test-http2-client-transport-provided-webapp + jetty-test-core-example-webapp diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 15ea52077b1f..e5d3040f5a35 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -110,7 +110,7 @@ ee10 true - org.eclipse.jetty.ee10.webapp.WebAppContext + org.eclipse.jetty.ee10.webapp.WebAppContext .*/jakarta.servlet-api-[^/]*\.jar$|.*jakarta.servlet.jsp.jstl-.*\.jar$ diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index a4a3c530eb65..a54eddd9c001 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -27,7 +27,7 @@ ee10 - org.eclipse.jetty.ee10.webapp.WebAppContext + org.eclipse.jetty.ee10.webapp.WebAppContext true diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 13121724c051..607d0151d03d 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -7,7 +7,7 @@ ee10 - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 367b8fdefe20..4e3216db9a79 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -109,7 +109,7 @@ ee11 - org.eclipse.jetty.ee11.webapp.WebAppContext + org.eclipse.jetty.ee11.webapp.WebAppContext true diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 8e2004ccd658..8a5fbdf44277 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -27,7 +27,7 @@ ee11 - org.eclipse.jetty.ee11.webapp.WebAppContext + org.eclipse.jetty.ee11.webapp.WebAppContext true diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index a4675dc352b4..e10bced8c15b 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -7,7 +7,7 @@ ee11 - + diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index b66ea9e09c45..e0b6e3b4541e 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -7,7 +7,7 @@ ee8 - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index f80293244b8b..781a06e7883e 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -93,7 +93,7 @@ ee9 - org.eclipse.jetty.ee9.webapp.WebAppContext + org.eclipse.jetty.ee9.webapp.WebAppContext true diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 34c63b1f54aa..8b66790c0a45 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -27,7 +27,7 @@ ee9 - org.eclipse.jetty.ee9.webapp.WebAppContext + org.eclipse.jetty.ee9.webapp.WebAppContext true diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 19d87fccf0a5..9b032b5fde6e 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -7,7 +7,7 @@ ee9 - + diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index 47bbdee7f25b..fbbbee49a460 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -1966,8 +1966,8 @@ public void testHTTP2ClientInCoreWebAppProvidedByServer() throws Exception Files.copy(webAppJar, Files.createDirectories(webAppDirLib).resolve("webapp.jar")); Files.writeString(webapps.resolve(name + ".xml"), """ - - + + /test From df5274ee865f96e1fae2abf6842ad6236af1fe0a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 27 Jan 2025 13:51:50 -0600 Subject: [PATCH 045/104] Correct the Deployable.initializeDefaults behavior in DefaultContextHandlerFactory --- .../DefaultContextHandlerFactory.java | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java index 631026748dd8..26c57f6abaa4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -169,12 +169,6 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app context = applyXml(provider, context, mainPath, environment, deployAttributes); } - // Set a backup value for the path to the war in case it hasn't already been set - // via a different means. This is especially important for a deployable App - // that is only a .war file (no XML). The eventual WebInfConfiguration - // will use this attribute. - deployAttributes.setAttribute(Deployable.WAR, mainPath.toString()); - return getContextHandler(context); } finally @@ -426,18 +420,24 @@ private Object newContextInstance(DefaultProvider provider, Environment environm initializeContextPath(contextHandler, path); initializeContextHandler(contextHandler, path, attributes); - - // Initialize any deployable - if (context instanceof Deployable deployable) - deployable.initializeDefaults(attributes); } + // Allow context created from CONTEXT_HANDLER_CLASS to be initialized + // before the XML executes, and possibly references content that only + // the context will know about (such as from a classloader) + initializeDeployable(context, attributes); + if (FileID.isXml(path)) { + // track if context is created from XML or an existing one is just being configured by XML + boolean createdContext = (context == null); context = applyXml(provider, context, path, environment, attributes); ContextHandler contextHandler = getContextHandler(context); if (contextHandler == null) throw new IllegalStateException("Unknown context type of " + context); + + if (createdContext) + initializeDeployable(context, attributes); return context; } @@ -446,7 +446,6 @@ private Object newContextInstance(DefaultProvider provider, Environment environm // fallback to default from environment. context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); - if (context != null) { ContextHandler contextHandler = getContextHandler(context); @@ -455,12 +454,34 @@ private Object newContextInstance(DefaultProvider provider, Environment environm initializeContextPath(contextHandler, path); initializeContextHandler(contextHandler, path, attributes); + initializeDeployable(context, attributes); return context; } return null; } + private void initializeDeployable(Object context, Attributes attributes) + { + // Ensure that WAR fallback String (that WebInfConfiguration needs) is + // only created once. + if (attributes.getAttribute(Deployable.WAR) == null) + { + Path mainPath = (Path)attributes.getAttribute(Deployable.MAIN_PATH); + if (FileID.isWebArchive(mainPath)) + { + // Set a backup value for the path to the war in case it hasn't already been set + // via a different means. This is especially important for a deployable App + // that is only a .war file (no XML). The eventual WebInfConfiguration + // will use this attribute. + attributes.setAttribute(Deployable.WAR, mainPath.toString()); + } + } + + if (context instanceof Deployable deployable) + deployable.initializeDefaults(attributes); + } + private Object newInstance(String className) throws Exception { if (StringUtil.isBlank(className)) From 2c65222fc3b67618914e1521f673e28a0fd4094a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 28 Jan 2025 06:48:04 -0600 Subject: [PATCH 046/104] Remove dead code --- .../java/org/eclipse/jetty/deploy/AppProvider.java | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java index 4a8447d17baf..e2af513223be 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.deploy; -import java.io.IOException; - import org.eclipse.jetty.util.component.LifeCycle; /** @@ -30,13 +28,4 @@ public interface AppProvider extends LifeCycle */ void setDeploymentManager(DeploymentManager deploymentManager); - /** - * Create a ContextHandler for an App - * - * @param app The App - * @return A ContextHandler - * @throws IOException if unable to create context - * @throws Exception if unable to create context - */ - // ContextHandler createContextHandler(App app) throws Exception; } From e045742ba2af096d2765c8572ccf6a9d892de150 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 28 Jan 2025 06:48:24 -0600 Subject: [PATCH 047/104] Remove references to old Unit class --- .../org/eclipse/jetty/deploy/providers/DefaultApp.java | 4 ++-- .../jetty/deploy/providers/DefaultProvider.java | 10 ---------- .../eclipse/jetty/deploy/providers/DefaultAppTest.java | 2 +- .../jetty/deploy/providers/DefaultProviderTest.java | 2 +- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java index cd513809862d..256652386264 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java @@ -274,7 +274,7 @@ public String toString() /** *

        - * Calculate the State of the overall Unit based on the States in the Paths. + * Calculate the State of the overall State based on the States in the Paths. *

        *
        *
        UNCHANGED
        @@ -287,7 +287,7 @@ public String toString() *
        All Path states are in REMOVED state, or there are no Paths being tracked
        *
        * - * @return the state of the Unit. + * @return the state of the App. */ private DefaultApp.State calcState() { diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index ab03b2653759..5fe305857661 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -271,16 +271,6 @@ public void setScanInterval(int scanInterval) this.scanInterval = scanInterval; } - public Comparator getUnitComparator() - { - return actionComparator; - } - - public void setUnitComparator(Comparator comparator) - { - this.actionComparator = comparator; - } - /** * Test if initial scan should be deferred. * diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java index 6eebbf81854a..50480534cb8d 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultAppTest.java @@ -179,7 +179,7 @@ public void testStatePutThenRemoveAll() // Then it gets processed, which results in a state reset. app.resetStates(); - // The resulting Unit should have no paths, and be flagged as removed. + // The resulting app should have no paths, and be flagged as removed. assertThat(app.getPaths().size(), is(0)); assertThat(app.getState(), is(DefaultApp.State.REMOVED)); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java index a5bd9ffc03c9..5efad9c1eb29 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderTest.java @@ -127,7 +127,7 @@ public void testActionListXmlThenRemoved() throws IOException defaultProvider.assertActionList = (actions) -> { - assertThat("changedUnits.size", actions.size(), is(1)); + assertThat("actions.size", actions.size(), is(1)); Iterator iterator = actions.iterator(); DeployAction action; From ef92632217c12cddc9a67e0db3afd6383a387693 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 28 Jan 2025 07:51:19 -0600 Subject: [PATCH 048/104] Delete AppProvider interface, use component beans instead. --- .../src/main/config/etc/jetty-deploy.xml | 7 +- .../config/etc/jetty-deployment-manager.xml | 2 +- .../org/eclipse/jetty/deploy/AppProvider.java | 31 ------- .../jetty/deploy/DeploymentManager.java | 90 ------------------- .../deploy/jmx/DeploymentManagerMBean.java | 6 -- .../DefaultContextHandlerFactory.java | 7 +- .../deploy/providers/DefaultProvider.java | 8 +- .../DeploymentManagerLifeCycleRouteTest.java | 9 +- .../jetty/deploy/DeploymentManagerTest.java | 3 +- .../eclipse/jetty/deploy/MockAppProvider.java | 3 +- .../DefaultProviderCoreWebappTest.java | 4 +- .../DefaultProviderDeferredStartupTest.java | 23 ++--- .../DefaultProviderRuntimeUpdatesTest.java | 26 +++--- .../jetty/osgi/AbstractContextProvider.java | 4 +- .../jetty/ee10/osgi/boot/EE10Activator.java | 11 ++- .../jetty/ee10/test/DeploymentErrorTest.java | 3 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 11 ++- .../jetty/ee11/test/DeploymentErrorTest.java | 3 +- .../jetty/ee9/osgi/boot/EE9Activator.java | 11 ++- .../jetty/ee9/test/DeploymentErrorTest.java | 3 +- 20 files changed, 75 insertions(+), 190 deletions(-) delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index e7040ab9692b..f4ee5b8d49d9 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -9,6 +9,9 @@ + + + @@ -34,8 +37,8 @@ - - + + diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml index 6d7a6b7dd238..5bb7dc16084c 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml @@ -10,7 +10,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java deleted file mode 100644 index e2af513223be..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java +++ /dev/null @@ -1,31 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy; - -import org.eclipse.jetty.util.component.LifeCycle; - -/** - * Object responsible for providing {@link App}s to the {@link DeploymentManager} - */ -public interface AppProvider extends LifeCycle -{ - /** - * Set the Deployment Manager - * - * @param deploymentManager the deployment manager - * @throws IllegalStateException if the provider {@link #isRunning()}. - */ - void setDeploymentManager(DeploymentManager deploymentManager); - -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index ba2074bb07a4..27314c2cf3af 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -49,7 +49,6 @@ * deployment manager roles graph *
          *
        1. Tracking Apps and their LifeCycle Location
        2. - *
        3. Managing AppProviders and the Apps that they provide.
        4. *
        5. Executing AppLifeCycle on App based on current and desired LifeCycle Location.
        6. *
        *

        @@ -93,7 +92,6 @@ void setLifeCycleNode(Node node) private final AutoLock _lock = new AutoLock(); private Throwable _onStartupErrors; - private final List _providers = new ArrayList<>(); private final AppLifeCycle _lifecycle = new AppLifeCycle(); private final Queue _apps = new ConcurrentLinkedQueue(); private ContextHandlerCollection _contexts; @@ -103,8 +101,6 @@ void setLifeCycleNode(Node node) /** * Receive an app for processing. * - * Most commonly used by the various {@link AppProvider} implementations. - * * @param app the app */ public void addApp(App app) @@ -123,40 +119,6 @@ public void addApp(App app) } } - /** - * Set the AppProviders. - * The providers passed are added via {@link #addBean(Object)} so that - * their lifecycles may be managed as a {@link ContainerLifeCycle}. - * - * @param providers the app provider list - */ - public void setAppProviders(Collection providers) - { - if (isRunning()) - throw new IllegalStateException(); - - _providers.clear(); - removeBeans(); - for (AppProvider provider : providers) - { - if (_providers.add(provider)) - addBean(provider, true); - } - } - - public Collection getAppProviders() - { - return Collections.unmodifiableList(_providers); - } - - public void addAppProvider(AppProvider provider) - { - if (isRunning()) - throw new IllegalStateException(); - _providers.add(provider); - addBean(provider, true); - } - public void setLifeCycleBindings(Collection bindings) { if (isRunning()) @@ -211,12 +173,6 @@ protected void doStart() throws Exception addLifeCycleBinding(new StandardUndeployer()); } - // Start all of the AppProviders - for (AppProvider provider : _providers) - { - startAppProvider(provider); - } - try (AutoLock l = _lock.lock()) { ExceptionUtil.ifExceptionThrow(_onStartupErrors); @@ -225,24 +181,6 @@ protected void doStart() throws Exception super.doStart(); } - @Override - protected void doStop() throws Exception - { - // Stop all of the AppProviders - for (AppProvider provider : _providers) - { - try - { - provider.stop(); - } - catch (Exception e) - { - LOG.warn("Unable to start AppProvider", e); - } - } - super.doStop(); - } - private AppEntry findAppEntry(String appId) { if (appId == null) @@ -350,21 +288,6 @@ public void removeApp(App app) } } - public void removeAppProvider(AppProvider provider) - { - if (_providers.remove(provider)) - removeBean(provider); - - try - { - provider.stop(); - } - catch (Exception e) - { - LOG.warn("Unable to stop Provider", e); - } - } - /** * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. @@ -483,19 +406,6 @@ public void setDefaultLifeCycleGoal(String defaultLifeCycleState) this._defaultLifeCycleGoal = defaultLifeCycleState; } - private void startAppProvider(AppProvider provider) - { - try - { - provider.setDeploymentManager(this); - provider.start(); - } - catch (Exception e) - { - LOG.warn("Unable to start AppProvider", e); - } - } - public void undeployAll() { LOG.debug("Undeploy All"); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java index a05178fe6af9..baabdf2e03c3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java @@ -92,12 +92,6 @@ public Collection getContexts() throws Exception return apps; } - @ManagedAttribute("Registered AppProviders") - public List getAppProviders() - { - return _manager.getAppProviders().stream().map(String::valueOf).collect(Collectors.toList()); - } - public void requestAppGoal(String appId, String nodeName) { _manager.requestAppGoal(appId, nodeName); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java index 26c57f6abaa4..b7da07f6acb8 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -215,8 +215,11 @@ public void initializeDefaults(Object context) !k.startsWith("jetty.webapps")) .forEach(k -> { - String v = Objects.toString(attributes.getAttribute(k)); - xmlc.getProperties().put(k, v); + Object v = attributes.getAttribute(k); + if (v == null) + xmlc.getProperties().remove(k); + else + xmlc.getProperties().put(k, Objects.toString(v)); }); // Run configure against appropriate classloader. diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index 5fe305857661..38f01480cdff 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -36,7 +36,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; @@ -111,7 +110,7 @@ * } */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") -public class DefaultProvider extends ContainerLifeCycle implements AppProvider, Scanner.ChangeSetListener +public class DefaultProvider extends ContainerLifeCycle implements Scanner.ChangeSetListener { private static final Logger LOG = LoggerFactory.getLogger(DefaultProvider.class); @@ -222,7 +221,6 @@ public DeploymentManager getDeploymentManager() return deploymentManager; } - @Override public void setDeploymentManager(DeploymentManager deploymentManager) { this.deploymentManager = deploymentManager; @@ -504,6 +502,10 @@ protected void doStart() throws Exception { if (LOG.isDebugEnabled()) LOG.debug("{} doStart()", this); + + if (getDeploymentManager() == null) + throw new IllegalStateException("No DeploymentManager defined"); + if (monitoredDirs.isEmpty()) throw new IllegalStateException("No monitored dir specified"); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index fc1578e674f6..f4a4032e7c34 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -33,10 +33,11 @@ public void testStateTransitionNewToDeployed() throws Exception depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); + mockProvider.setDeploymentManager(depman); depman.addLifeCycleBinding(pathtracker); - depman.addAppProvider(mockProvider); depman.setContexts(new ContextHandlerCollection()); + depman.addBean(mockProvider); // Start DepMan depman.start(); @@ -65,9 +66,10 @@ public void testStateTransitionReceive() throws Exception depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); + mockProvider.setDeploymentManager(depman); depman.addLifeCycleBinding(pathtracker); - depman.addAppProvider(mockProvider); + depman.addBean(mockProvider); // Start DepMan depman.start(); @@ -90,13 +92,14 @@ public void testStateTransitionDeployedToUndeployed() throws Exception depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); + mockProvider.setDeploymentManager(depman); // Setup JMX MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); depman.addBean(mbContainer); depman.addLifeCycleBinding(pathtracker); - depman.addAppProvider(mockProvider); + depman.addBean(mockProvider); depman.setContexts(new ContextHandlerCollection()); // Start DepMan diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 2c2dae447df1..48cc1d85bb24 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -61,9 +61,10 @@ public void testReceiveApp() throws Exception depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); + mockProvider.setDeploymentManager(depman); depman.addLifeCycleBinding(pathtracker); - depman.addAppProvider(mockProvider); + depman.addBean(mockProvider); // Start DepMan depman.start(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java index 051b35a8eece..eb5fad253dca 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java @@ -21,7 +21,7 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Environment; -public class MockAppProvider extends AbstractLifeCycle implements AppProvider +public class MockAppProvider extends AbstractLifeCycle { private DeploymentManager deployMan; private Path webappsDir; @@ -31,7 +31,6 @@ public String getEnvironmentName() return Environment.ensure("mock").getName(); } - @Override public void setDeploymentManager(DeploymentManager deploymentManager) { this.deployMan = deploymentManager; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java index 3dbe79aa69ba..ee98a8ad3ea9 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java @@ -64,7 +64,9 @@ public void startServer(DefaultProvider provider) throws Exception DeploymentManager deploymentManager = new DeploymentManager(); deploymentManager.setContexts(contexts); - deploymentManager.addAppProvider(provider); + + deploymentManager.addBean(provider); + provider.setDeploymentManager(deploymentManager); server.addBean(deploymentManager); server.start(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java index 0a892fb6b7df..65592a282aab 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderDeferredStartupTest.java @@ -22,7 +22,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.Server; @@ -45,7 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Tests {@link ContextProvider} behaviors when in Deferred Startup mode + * Tests {@link DefaultProvider} behaviors when in Deferred Startup mode */ @ExtendWith(WorkDirExtension.class) public class DefaultProviderDeferredStartupTest extends AbstractCleanEnvironmentTest @@ -112,27 +111,19 @@ public void lifeCycleStarted(LifeCycle event) server.addEventListener(eventCaptureListener); - DefaultProvider scanningAppProvider = null; DeploymentManager deploymentManager = server.getBean(DeploymentManager.class); - for (AppProvider appProvider : deploymentManager.getAppProviders()) - { - if (appProvider instanceof DefaultProvider) - { - scanningAppProvider = (DefaultProvider)appProvider; - } - } - assertNotNull(scanningAppProvider, "Should have found ScanningAppProvider"); - assertTrue(scanningAppProvider.isDeferInitialScan(), "The DeferInitialScan configuration should be true"); + DefaultProvider defaultProvider = deploymentManager.getBean(DefaultProvider.class); + assertNotNull(defaultProvider, "Should have found DefaultProvider"); + assertTrue(defaultProvider.isDeferInitialScan(), "The DeferInitialScan configuration should be true"); - scanningAppProvider.addEventListener(eventCaptureListener); - scanningAppProvider.addEventListener(new Container.InheritedListener() + defaultProvider.addEventListener(eventCaptureListener); + defaultProvider.addEventListener(new Container.InheritedListener() { @Override public void beanAdded(Container parent, Object child) { - if (child instanceof Scanner) + if (child instanceof Scanner scanner) { - Scanner scanner = (Scanner)child; scanner.addEventListener(eventCaptureListener); scanner.addListener(new Scanner.ScanCycleListener() { diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java index 1fb284e4e0f5..7c960af99b0f 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderRuntimeUpdatesTest.java @@ -17,11 +17,11 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; +import java.util.Collection; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; @@ -95,22 +95,20 @@ public void startJetty() throws Exception // monitor tick DeploymentManager dm = jetty.getServer().getBean(DeploymentManager.class); - for (AppProvider provider : dm.getAppProviders()) + Collection defaultProviders = dm.getBeans(DefaultProvider.class); + for (DefaultProvider provider : defaultProviders) { - if (provider instanceof DefaultProvider scanningAppProvider) + _providerCount++; + provider.addScannerListener(new Scanner.ScanCycleListener() { - _providerCount++; - scanningAppProvider.addScannerListener(new Scanner.ScanCycleListener() + @Override + public void scanEnded(int cycle) { - @Override - public void scanEnded(int cycle) - { - if (LOG.isDebugEnabled()) - LOG.debug("Scan ended: {}", cycle); - _scans.incrementAndGet(); - } - }); - } + if (LOG.isDebugEnabled()) + LOG.debug("Scan ended: {}", cycle); + _scans.incrementAndGet(); + } + }); } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 96dd6fc1698c..6b966aea9537 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -16,7 +16,6 @@ import java.util.Objects; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -36,7 +35,7 @@ * Jetty that have been discovered via OSGI either as bundles or services. *

        */ -public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider +public abstract class AbstractContextProvider extends AbstractLifeCycle { private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); @@ -73,7 +72,6 @@ public ContextHandler createContextHandler(App app) throws Exception return h; } - @Override public void setDeploymentManager(DeploymentManager deploymentManager) { _deploymentManager = deploymentManager; diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index ee94d116efe7..9716e9395378 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -26,7 +26,6 @@ import java.util.Optional; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -148,7 +147,10 @@ public Object addingService(ServiceReference sr) if (deployer.isPresent()) { - for (AppProvider provider : deployer.get().getAppProviders()) + DeploymentManager deploymentManager = deployer.get(); + Collection osgiProviders = deploymentManager.getBeans(AbstractContextProvider.class); + + for (AbstractContextProvider provider : osgiProviders) { if (provider instanceof BundleContextProvider bundleContextProvider) { @@ -161,16 +163,17 @@ public Object addingService(ServiceReference sr) webAppProvider = bundleWebAppProvider; } } + if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE10ContextFactory(_myBundle)); - deployer.get().addAppProvider(contextProvider); + contextProvider.setDeploymentManager(deploymentManager); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE10WebAppFactory(_myBundle)); - deployer.get().addAppProvider(webAppProvider); + contextProvider.setDeploymentManager(deploymentManager); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 6b7922397adb..3b35da43939d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -107,7 +107,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception appProvider.configureEnvironment("ee10"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - deploymentManager.addAppProvider(appProvider); + appProvider.setDeploymentManager(deploymentManager); + deploymentManager.addBean(appProvider); server.addBean(deploymentManager); diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 6d1bfbabcb62..7741b3343bd8 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -26,7 +26,6 @@ import java.util.Optional; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -148,7 +147,10 @@ public Object addingService(ServiceReference sr) if (deployer.isPresent()) { - for (AppProvider provider : deployer.get().getAppProviders()) + DeploymentManager deploymentManager = deployer.get(); + Collection osgiProviders = deploymentManager.getBeans(AbstractContextProvider.class); + + for (AbstractContextProvider provider : osgiProviders) { if (provider instanceof BundleContextProvider bundleContextProvider) { @@ -161,16 +163,17 @@ public Object addingService(ServiceReference sr) webAppProvider = bundleWebAppProvider; } } + if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE11ContextFactory(_myBundle)); - deployer.get().addAppProvider(contextProvider); + contextProvider.setDeploymentManager(deploymentManager); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE11WebAppFactory(_myBundle)); - deployer.get().addAppProvider(webAppProvider); + webAppProvider.setDeploymentManager(deploymentManager); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index b39e0e324982..374bdb8072f3 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -107,7 +107,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception appProvider.configureEnvironment("ee11"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - deploymentManager.addAppProvider(appProvider); + appProvider.setDeploymentManager(deploymentManager); + deploymentManager.addBean(appProvider); server.addBean(deploymentManager); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index e01003256e2e..3cf44cb4298b 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -26,7 +26,6 @@ import java.util.Optional; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -147,7 +146,10 @@ public Object addingService(ServiceReference sr) if (deployer.isPresent()) { - for (AppProvider provider : deployer.get().getAppProviders()) + DeploymentManager deploymentManager = deployer.get(); + Collection osgiProviders = deploymentManager.getBeans(AbstractContextProvider.class); + + for (AbstractContextProvider provider : osgiProviders) { if (provider instanceof BundleContextProvider bundleContextProvider) { @@ -160,16 +162,17 @@ public Object addingService(ServiceReference sr) webAppProvider = bundleWebAppProvider; } } + if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE9ContextFactory(_myBundle)); - deployer.get().addAppProvider(contextProvider); + contextProvider.setDeploymentManager(deploymentManager); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE9WebAppFactory(_myBundle)); - deployer.get().addAppProvider(webAppProvider); + webAppProvider.setDeploymentManager(deploymentManager); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index ada52d250fab..e6c1efab755d 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -102,7 +102,8 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro appProvider.configureEnvironment("ee9"); appProvider.addMonitoredDirectory(docroots); appProvider.setScanInterval(1); - deploymentManager.addAppProvider(appProvider); + appProvider.setDeploymentManager(deploymentManager); + deploymentManager.addBean(appProvider); server.addBean(deploymentManager); // Server handlers From d8a1e240b75cff921cca7a2d656a044228aa1805 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 28 Jan 2025 14:26:24 -0600 Subject: [PATCH 049/104] Reintroduce AppProvider & ContextHandlerFactory --- .../src/main/config/etc/jetty-deploy.xml | 5 +- .../java/org/eclipse/jetty/deploy/App.java | 13 +- .../org/eclipse/jetty/deploy/AppProvider.java | 49 ++++++ .../jetty/deploy/ContextHandlerFactory.java | 25 +++ .../jetty/deploy/DeploymentManager.java | 43 ++--- .../jetty/deploy/providers/DefaultApp.java | 18 +- .../DefaultContextHandlerFactory.java | 115 ++++--------- .../deploy/providers/DefaultProvider.java | 158 +++++++++--------- .../DeploymentManagerLifeCycleRouteTest.java | 32 ++-- .../jetty/deploy/DeploymentManagerTest.java | 8 +- .../org/eclipse/jetty/deploy/MockApp.java | 7 + .../eclipse/jetty/deploy/MockAppProvider.java | 15 +- .../DefaultProviderCoreWebappTest.java | 10 +- .../providers/DefaultProviderStartupTest.java | 27 ++- .../resources/jetty-core-deploy-custom.xml | 2 +- .../test/resources/jetty-logging.properties | 2 +- .../jetty/osgi/AbstractContextProvider.java | 22 +-- .../jetty/osgi/BundleContextProvider.java | 4 +- .../jetty/osgi/BundleWebAppProvider.java | 8 +- .../java/org/eclipse/jetty/osgi/OSGiApp.java | 5 +- .../src/main/config/etc/jetty-core-deploy.xml | 2 +- .../src/main/config/modules/core-deploy.mod | 2 +- .../org/eclipse/jetty/server/Deployable.java | 101 ++++++++++- ...ppContext.java => CoreContextHandler.java} | 13 +- .../jetty/ee10/osgi/boot/EE10Activator.java | 20 +-- .../jetty/ee10/test/DeploymentErrorTest.java | 3 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 20 +-- .../jetty/ee11/test/DeploymentErrorTest.java | 3 +- .../jetty/ee9/osgi/boot/EE9Activator.java | 20 +-- .../jetty/ee9/test/DeploymentErrorTest.java | 3 +- 30 files changed, 449 insertions(+), 306 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerFactory.java rename jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/{CoreWebAppContext.java => CoreContextHandler.java} (96%) diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index f4ee5b8d49d9..8a85aa04bb20 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -9,9 +9,6 @@ - - - @@ -38,7 +35,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java index 3b39e77c679a..56664d410d26 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.deploy; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.Environment; /** * An abstract App, the component that moves through the DeploymentManager. @@ -30,7 +31,17 @@ public interface App /** * Get the active ContextHandler for this App. * - * @return the ContextHandler for the App. null means the ContextHandler hasn't been created yet. + * @return the ContextHandler for this App. */ ContextHandler getContextHandler(); + + /** + * The Environment this App belongs to. + * + * @return the Environment for this App, null if App has no Environment. + */ + default Environment getEnvironment() + { + return null; + } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java new file mode 100644 index 000000000000..622d0026bdd5 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java @@ -0,0 +1,49 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import org.eclipse.jetty.server.Server; + +/** + * A component that is responsible for driving the add/remove of {@link App} + * to the DeploymentManager via the provided {@link Manager} interface. + */ +public interface AppProvider +{ + /** + * Deployment Manager specific methods that are available to the AppProvider. + */ + interface Manager + { + Server getServer(); + + default void addApp(App app) + { + addApp(app, AppLifeCycle.STARTED); + } + + void addApp(App app, String goalName); + + void requestAppGoal(App app, String goalName); + + void removeApp(App app); + } + + /** + * The Manager to use for manipulating Apps for this provider. + * + * @param manager the manager interface. + */ + void setManager(Manager manager); +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerFactory.java new file mode 100644 index 000000000000..6155cee2e7c4 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerFactory.java @@ -0,0 +1,25 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.Attributes; + +public interface ContextHandlerFactory +{ + // TODO: document Attributes that are needed vs optional and provided. + + ContextHandler newContextHandler(Server server, App app, Attributes deployAttributes) throws Exception; +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 27314c2cf3af..f803435b5bbf 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -55,7 +55,7 @@ * deployment manager graph */ @ManagedObject("Deployment Manager") -public class DeploymentManager extends ContainerLifeCycle +public class DeploymentManager extends ContainerLifeCycle implements AppProvider.Manager { private static final Logger LOG = LoggerFactory.getLogger(DeploymentManager.class); @@ -96,29 +96,40 @@ void setLifeCycleNode(Node node) private final Queue _apps = new ConcurrentLinkedQueue(); private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; - private String _defaultLifeCycleGoal = AppLifeCycle.STARTED; /** - * Receive an app for processing. + * Receive an app for processing, and * * @param app the app */ - public void addApp(App app) + @Override + public void addApp(App app, String nodeName) { if (LOG.isDebugEnabled()) - LOG.debug("addApp: {}", app); + LOG.debug("addApp: {} -> {}", app, nodeName); AppEntry entry = new AppEntry(); entry.app = app; - entry.setLifeCycleNode(_lifecycle.getNodeByName("undeployed")); + entry.setLifeCycleNode(_lifecycle.getNodeByName(AppLifeCycle.UNDEPLOYED)); _apps.add(entry); - if (isRunning() && _defaultLifeCycleGoal != null) + if (isRunning()) { // Immediately attempt to go to default lifecycle state - this.requestAppGoal(entry, _defaultLifeCycleGoal); + this.requestAppGoal(entry, nodeName); } } + public void addAppProvider(AppProvider provider) + { + provider.setManager(this); + addBean(provider); + } + + public Collection getAppProviders() + { + return getBeans(AppProvider.class); + } + public void setLifeCycleBindings(Collection bindings) { if (isRunning()) @@ -248,11 +259,6 @@ public ContextHandlerCollection getContexts() return _contexts; } - public String getDefaultLifeCycleGoal() - { - return _defaultLifeCycleGoal; - } - public AppLifeCycle getLifeCycle() { return _lifecycle; @@ -357,10 +363,10 @@ private void requestAppGoal(AppEntry appentry, String nodeName) { _lifecycle.runBindings(failed, appentry.app, this); } - catch (Throwable ignore) + catch (Throwable cause) { // The runBindings failed for 'failed' node - LOG.trace("IGNORED", ignore); + LOG.trace("IGNORED", cause); } if (isStarting()) @@ -401,17 +407,12 @@ public void setContexts(ContextHandlerCollection contexts) this._contexts = contexts; } - public void setDefaultLifeCycleGoal(String defaultLifeCycleState) - { - this._defaultLifeCycleGoal = defaultLifeCycleState; - } - public void undeployAll() { LOG.debug("Undeploy All"); for (AppEntry appentry : _apps) { - requestAppGoal(appentry, "undeployed"); + requestAppGoal(appentry, AppLifeCycle.UNDEPLOYED); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java index 256652386264..6c24e520a757 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultApp.java @@ -26,10 +26,10 @@ import java.util.stream.Collectors; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.resource.PathCollators; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,14 +95,24 @@ public void setContextHandler(ContextHandler contextHandler) this.contextHandler = contextHandler; } + @Override + public Environment getEnvironment() + { + return (Environment)getAttributes().getAttribute(DefaultContextHandlerFactory.ENVIRONMENT); + } + public String getEnvironmentName() { - return (String)getAttributes().getAttribute(Deployable.ENVIRONMENT); + Environment env = getEnvironment(); + if (env == null) + return ""; + else + return env.getName(); } - public void setEnvironmentName(String name) + public void setEnvironment(Environment env) { - getAttributes().setAttribute(Deployable.ENVIRONMENT, name); + getAttributes().setAttribute(DefaultContextHandlerFactory.ENVIRONMENT, env); } /** diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java index b7da07f6acb8..58b455ad49a6 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultContextHandlerFactory.java @@ -24,7 +24,9 @@ import java.util.stream.Collectors; import org.eclipse.jetty.deploy.App; +import org.eclipse.jetty.deploy.ContextHandlerFactory; import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; @@ -36,12 +38,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DefaultContextHandlerFactory +public class DefaultContextHandlerFactory implements ContextHandlerFactory { private static final Logger LOG = LoggerFactory.getLogger(DefaultContextHandlerFactory.class); - private static final String ENV_XML_PATHS = "jetty.deploy.defaultApp.envXmls"; - - private String _defaultEnvironmentName; + public static final String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; + public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; + public static final String ENVIRONMENT = "jetty.deploy.environment"; + public static final String ENV_XML_PATHS = "jetty.deploy.defaultApp.envXMLs"; + public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; private static Map asProperties(Attributes attributes) { @@ -68,40 +72,13 @@ public static void setEnvironmentXmlPaths(Attributes attributes, List path attributes.setAttribute(ENV_XML_PATHS, paths); } - /** - * Get the default {@link Environment} name for discovered web applications that - * do not declare the {@link Environment} that they belong to. - * - *

        - * Falls back to {@link Environment#getAll()} list, and returns - * the first name returned after sorting with {@link Deployable#ENVIRONMENT_COMPARATOR} - *

        - * - * @return the default environment name. - */ - public String getDefaultEnvironmentName() - { - if (_defaultEnvironmentName == null) - { - return Environment.getAll().stream() - .map(Environment::getName) - .max(Deployable.ENVIRONMENT_COMPARATOR) - .orElse(null); - } - return _defaultEnvironmentName; - } - - public void setDefaultEnvironmentName(String name) - { - this._defaultEnvironmentName = name; - } - - public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app, Attributes deployAttributes) throws Exception + @Override + public ContextHandler newContextHandler(Server server, App app, Attributes deployAttributes) throws Exception { - Path mainPath = app.getMainPath(); + Path mainPath = (Path)deployAttributes.getAttribute(Deployable.MAIN_PATH); if (mainPath == null) { - LOG.warn("Unable to create ContextHandler for app with no main path: {}", app); + LOG.warn("Unable to create ContextHandler for app with no main path defined: {}", app); return null; } @@ -113,23 +90,10 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app if (!Files.exists(mainPath)) throw new IllegalStateException("App path does not exist " + mainPath); - deployAttributes.setAttribute(Deployable.MAIN_PATH, mainPath); - deployAttributes.setAttribute(Deployable.OTHER_PATHS, app.getPaths().keySet()); - - String envName = app.getEnvironmentName(); - if (StringUtil.isBlank(envName)) - { - envName = getDefaultEnvironmentName(); - app.setEnvironmentName(envName); - } - - // Verify that referenced Environment even exists. - Environment environment = Environment.get(envName); - + Environment environment = app.getEnvironment(); if (environment == null) { - LOG.warn("Environment [{}] does not exist (referenced in app [{}]). The available environments are: {}", - app.getEnvironmentName(), + LOG.warn("Environment not declared for app [{}]. The available environments are: {}", app, Environment.getAll().stream() .map(Environment::getName) @@ -149,12 +113,12 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app /* * The process now is to figure out the context object to use. * This can come from a number of places. - * 1. If an XML deployable, this is the entry. + * 1. If an XML deployable, this is the entry. * 2. If another deployable (like a web archive, or directory), then check attributes. * a. use the app attributes to figure out the context handler class. * b. use the environment attributes default context handler class. */ - Object context = newContextInstance(provider, environment, app, deployAttributes, mainPath); + Object context = newContextInstance(server, environment, app, deployAttributes, mainPath); if (context == null) throw new IllegalStateException("unable to create ContextHandler for " + app); @@ -162,11 +126,11 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app LOG.debug("Context {} created from app {}", context.getClass().getName(), app); // Apply environment properties and XML to context - if (applyEnvironmentXml(provider, app, context, environment, deployAttributes)) + if (applyEnvironmentXml(server, context, environment, deployAttributes)) { // If an XML deployable, apply full XML over environment XML changes if (FileID.isXml(mainPath)) - context = applyXml(provider, context, mainPath, environment, deployAttributes); + context = applyXml(server, context, mainPath, environment, deployAttributes); } return getContextHandler(context); @@ -177,14 +141,14 @@ public ContextHandler newContextHandler(DefaultProvider provider, DefaultApp app } } - protected Object applyXml(DefaultProvider provider, Object context, Path xml, Environment environment, Attributes attributes) throws Exception + protected Object applyXml(Server server, Object context, Path xml, Environment environment, Attributes attributes) throws Exception { if (!FileID.isXml(xml)) return null; try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { - XmlConfiguration xmlc = new XmlConfiguration(resourceFactory.newResource(xml), null, asProperties(attributes)) + XmlConfiguration xmlConfiguration = new XmlConfiguration(resourceFactory.newResource(xml), null, asProperties(attributes)) { @Override public void initializeDefaults(Object context) @@ -204,8 +168,8 @@ public void initializeDefaults(Object context) } }; - xmlc.getIdMap().put("Environment", environment.getName()); - xmlc.setJettyStandardIdsAndProperties(provider.getDeploymentManager().getServer(), xml); + xmlConfiguration.getIdMap().put("Environment", environment.getName()); + xmlConfiguration.setJettyStandardIdsAndProperties(server, xml); // Put all Environment attributes into XmlConfiguration as properties that can be used. attributes.getAttributeNameSet() @@ -217,9 +181,9 @@ public void initializeDefaults(Object context) { Object v = attributes.getAttribute(k); if (v == null) - xmlc.getProperties().remove(k); + xmlConfiguration.getProperties().remove(k); else - xmlc.getProperties().put(k, Objects.toString(v)); + xmlConfiguration.getProperties().put(k, Objects.toString(v)); }); // Run configure against appropriate classloader. @@ -231,9 +195,9 @@ public void initializeDefaults(Object context) { // Create or configure the context if (context == null) - return xmlc.configure(); + return xmlConfiguration.configure(); - return xmlc.configure(context); + return xmlConfiguration.configure(context); } finally { @@ -332,15 +296,14 @@ else if (StringUtil.asciiStartsWithIgnoreCase(contextPath, "root-")) /** * Apply optional environment specific XML to context. * - * @param provider the DefaultProvider responsible for this context creation - * @param app the default app + * @param server the Server instance for referencing in XML * @param context the context to apply environment specific behavior to * @param environment the environment to use * @param attributes the attributes used to deploy the app * @return true if environment specific XML was applied. * @throws Exception if unable to apply environment configuration. */ - private boolean applyEnvironmentXml(DefaultProvider provider, DefaultApp app, Object context, Environment environment, Attributes attributes) throws Exception + private boolean applyEnvironmentXml(Server server, Object context, Environment environment, Attributes attributes) throws Exception { // Collect the optional environment context xml files. // Order them according to the name of their property key names. @@ -350,18 +313,14 @@ private boolean applyEnvironmentXml(DefaultProvider provider, DefaultApp app, Ob // nothing to do here return false; - boolean xmlApplied = false; - // apply each context environment xml file for (Path envXmlPath : sortedEnvXmlPaths) { if (LOG.isDebugEnabled()) LOG.debug("Applying environment specific context file {}", envXmlPath); - context = applyXml(provider, context, envXmlPath, environment, attributes); - xmlApplied = true; + context = applyXml(server, context, envXmlPath, environment, attributes); } - - return xmlApplied; + return true; } /** @@ -397,9 +356,9 @@ private ContextHandler getContextHandler(Object context) * The search order is: *

        *
          - *
        1. If app attribute {@link Deployable#CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
        2. + *
        3. If app attribute {@link #CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
        4. *
        5. If App deployable path is XML, apply XML {@code }
        6. - *
        7. Fallback to environment attribute {@link Deployable#CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
        8. + *
        9. Fallback to environment attribute {@link #CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
        10. *
        * * @param environment the environment context applies to @@ -409,12 +368,12 @@ private ContextHandler getContextHandler(Object context) * @return the Context Object. * @throws Exception if unable to create Object instance. */ - private Object newContextInstance(DefaultProvider provider, Environment environment, App app, Attributes attributes, Path path) throws Exception + private Object newContextInstance(Server server, Environment environment, App app, Attributes attributes, Path path) throws Exception { if (LOG.isDebugEnabled()) - LOG.debug("newContextInstance({}, {}, {}, {})", provider, environment, app, path); + LOG.debug("newContextInstance({}, {}, {}, {})", server, environment, app, path); - Object context = newInstance((String)attributes.getAttribute(Deployable.CONTEXT_HANDLER_CLASS)); + Object context = newInstance((String)attributes.getAttribute(CONTEXT_HANDLER_CLASS)); if (context != null) { ContextHandler contextHandler = getContextHandler(context); @@ -434,7 +393,7 @@ private Object newContextInstance(DefaultProvider provider, Environment environm { // track if context is created from XML or an existing one is just being configured by XML boolean createdContext = (context == null); - context = applyXml(provider, context, path, environment, attributes); + context = applyXml(server, context, path, environment, attributes); ContextHandler contextHandler = getContextHandler(context); if (contextHandler == null) throw new IllegalStateException("Unknown context type of " + context); @@ -448,7 +407,7 @@ private Object newContextInstance(DefaultProvider provider, Environment environm return context; // fallback to default from environment. - context = newInstance((String)environment.getAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT)); + context = newInstance((String)environment.getAttribute(CONTEXT_HANDLER_CLASS_DEFAULT)); if (context != null) { ContextHandler contextHandler = getContextHandler(context); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java index 38f01480cdff..4dc7e75e9f90 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/DefaultProvider.java @@ -36,7 +36,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.AppProvider; +import org.eclipse.jetty.deploy.ContextHandlerFactory; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -110,7 +112,7 @@ * } */ @ManagedObject("Provider for start-up deployment of webapps based on presence in directory") -public class DefaultProvider extends ContainerLifeCycle implements Scanner.ChangeSetListener +public class DefaultProvider extends ContainerLifeCycle implements AppProvider, Scanner.ChangeSetListener { private static final Logger LOG = LoggerFactory.getLogger(DefaultProvider.class); @@ -118,14 +120,16 @@ public class DefaultProvider extends ContainerLifeCycle implements Scanner.Chang private final List monitoredDirs = new CopyOnWriteArrayList<>(); private Map apps = new HashMap<>(); - private DeploymentManager deploymentManager; + private AppProvider.Manager manager; private Comparator actionComparator = new DeployActionComparator(); - private DefaultContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); + private ContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); + private Map environmentAttributesMap = new HashMap<>(); private Path environmentsDir; private int scanInterval = 10; private Scanner scanner; private boolean useRealPaths; private boolean deferInitialScan = false; + private String defaultEnvironmentName; public DefaultProvider() { @@ -137,6 +141,12 @@ public DefaultProvider(FilenameFilter filter) filenameFilter = filter; } + @Override + public void setManager(Manager manager) + { + this.manager = manager; + } + /** * @param dir Directory to scan for deployable artifacts */ @@ -180,12 +190,12 @@ public Collection getApps() return apps.values(); } - public DefaultContextHandlerFactory getContextHandlerFactory() + public ContextHandlerFactory getContextHandlerFactory() { return contextHandlerFactory; } - public void setContextHandlerFactory(DefaultContextHandlerFactory contextHandlerFactory) + public void setContextHandlerFactory(ContextHandlerFactory contextHandlerFactory) { this.contextHandlerFactory = contextHandlerFactory; } @@ -203,27 +213,19 @@ public void setContextHandlerFactory(DefaultContextHandlerFactory contextHandler */ public String getDefaultEnvironmentName() { - return this.contextHandlerFactory.getDefaultEnvironmentName(); + if (defaultEnvironmentName == null) + { + return Environment.getAll().stream() + .map(Environment::getName) + .max(Deployable.ENVIRONMENT_COMPARATOR) + .orElse(null); + } + return defaultEnvironmentName; } public void setDefaultEnvironmentName(String name) { - this.contextHandlerFactory.setDefaultEnvironmentName(name); - } - - /** - * Get the deploymentManager. - * - * @return the deploymentManager - */ - public DeploymentManager getDeploymentManager() - { - return deploymentManager; - } - - public void setDeploymentManager(DeploymentManager deploymentManager) - { - this.deploymentManager = deploymentManager; + this.defaultEnvironmentName = name; } public Path getEnvironmentsDirectory() @@ -389,26 +391,23 @@ else if (isEnvironmentConfigPath(path)) } } - // Now we know the DefaultApp instances that are changed by the incoming - // Scanner changes alone. + // Now we know the DefaultApp instances that are changed by processing + // the incoming Scanner changes. + // Now we want to convert this list of changes to a DeployAction list + // that will perform the add/remove logic in a consistent way. List changedApps = changedBaseNames .stream() .map(name -> apps.get(name)) .collect(Collectors.toList()); - if (changedEnvironments.isEmpty()) - { - // We have a set of changes, with no environment configuration - // changes present. - List actions = buildActionList(changedApps); - performActions(actions); - } - else + if (!changedEnvironments.isEmpty()) { // We have incoming environment configuration changes // We need to add any missing DefaultApp that have changed - // due to incoming environment configuration changes. + // due to incoming environment configuration changes, + // along with loading any ${jetty.base}/environments/-*.properties + // into a layer for that Environment. for (String changedEnvName : changedEnvironments) { @@ -427,14 +426,12 @@ else if (isEnvironmentConfigPath(path)) } } - // Load any Environment properties files into Environment attributes. + // Replace current tracked Environment Attributes, with a new Attributes.Layer. + this.environmentAttributesMap.remove(changedEnvName); try { - Properties envProps = loadEnvironmentProperties(changedEnvName); - Environment environment = Environment.get(changedEnvName); - envProps.stringPropertyNames().forEach( - k -> environment.setAttribute(k, envProps.getProperty(k)) - ); + Attributes envAttributes = loadEnvironmentAttributes(changedEnvName); + this.environmentAttributesMap.put(changedEnvName, envAttributes); } catch (IOException e) { @@ -442,10 +439,14 @@ else if (isEnvironmentConfigPath(path)) LOG.debug("Unable to load environment properties for environment [{}]", changedEnvName, e); } } - - List actions = buildActionList(changedApps); - performActions(actions); } + else + { + Environment.getAll().forEach((env) -> environmentAttributesMap.put(env.getName(), env)); + } + + List actions = buildActionList(changedApps); + performActions(actions); } @ManagedOperation(value = "Scan the monitored directories", impact = "ACTION") @@ -503,8 +504,8 @@ protected void doStart() throws Exception if (LOG.isDebugEnabled()) LOG.debug("{} doStart()", this); - if (getDeploymentManager() == null) - throw new IllegalStateException("No DeploymentManager defined"); + if (this.manager == null) + throw new IllegalStateException("No " + AppProvider.Manager.class.getName() + " defined"); if (monitoredDirs.isEmpty()) throw new IllegalStateException("No monitored dir specified"); @@ -556,7 +557,7 @@ protected void doStart() throws Exception { // Setup listener to wait for Server in STARTED state, which // triggers the first scan of the monitored directories - getDeploymentManager().getServer().addEventListener( + manager.getServer().addEventListener( new LifeCycle.Listener() { @Override @@ -643,7 +644,7 @@ protected void performActions(List actions) case REMOVE -> { apps.remove(step.getName()); - deploymentManager.removeApp(step.getApp()); + manager.removeApp(step.getApp()); } case ADD -> { @@ -654,12 +655,14 @@ protected void performActions(List actions) String appEnvironment = step.getApp().getEnvironmentName(); if (StringUtil.isBlank(appEnvironment)) appEnvironment = getDefaultEnvironmentName(); - step.getApp().setEnvironmentName(appEnvironment); + step.getApp().setEnvironment(Environment.get(appEnvironment)); - // Create a new Attributes for the App, which is the - // combination of Environment Attributes with App Attributes overlaying them. - Environment environment = Environment.get(appEnvironment); - Attributes deployAttributes = initAttributes(environment, step.getApp()); + // Create a new Attributes layer for the App deployment, which is the + // combination of layered Environment Attributes with App Attributes overlaying them. + Attributes envAttributes = environmentAttributesMap.get(appEnvironment); + Attributes.Layer deployAttributes = new Attributes.Layer(envAttributes, step.getApp().getAttributes()); + deployAttributes.setAttribute(Deployable.MAIN_PATH, step.getApp().getMainPath()); + deployAttributes.setAttribute(Deployable.OTHER_PATHS, step.getApp().getPaths().keySet()); // Ensure that Environment configuration XMLs are listed in deployAttributes List envXmlPaths = findEnvironmentXmlPaths(deployAttributes); @@ -667,11 +670,12 @@ protected void performActions(List actions) DefaultContextHandlerFactory.setEnvironmentXmlPaths(deployAttributes, envXmlPaths); // Create the Context Handler - ContextHandler contextHandler = getContextHandlerFactory().newContextHandler(this, step.getApp(), deployAttributes); + Server server = manager.getServer(); + ContextHandler contextHandler = getContextHandlerFactory().newContextHandler(server, step.getApp(), deployAttributes); step.getApp().setContextHandler(contextHandler); // Introduce the App to the DeploymentManager - deploymentManager.addApp(step.getApp()); + manager.addApp(step.getApp(), AppLifeCycle.STARTED); } } } @@ -704,7 +708,7 @@ private List findEnvironmentXmlPaths(Attributes deployAttributes) { List rawEnvXmlPaths = deployAttributes.getAttributeNameSet() .stream() - .filter(k -> k.startsWith(Deployable.ENVIRONMENT_XML)) + .filter(k -> k.startsWith(DefaultContextHandlerFactory.ENVIRONMENT_XML)) .map(k -> Path.of((String)deployAttributes.getAttribute(k))) .toList(); @@ -762,20 +766,6 @@ else if (!rawPath.isAbsolute()) return ret; } - private Attributes initAttributes(Environment environment, DefaultApp app) throws IOException - { - Attributes attributes = new Attributes.Mapped(); - - // Grab Environment attributes first - copyAttributes(environment, attributes); - - // Overlay the app attributes - copyAttributes(app.getAttributes(), attributes); - - // The now merged attributes - return attributes; - } - /** * Load all of the {@link Environment} specific {@code [-].properties} files * found in the directory provided. @@ -787,21 +777,26 @@ private Attributes initAttributes(Environment environment, DefaultApp app) throw * * @param env the environment name */ - private Properties loadEnvironmentProperties(String env) throws IOException + private Attributes loadEnvironmentAttributes(String env) throws IOException { - Properties props = new Properties(); + Attributes envAttributes = Environment.get(env); + if (envAttributes == null) + { + LOG.warn("Not an environment: {}", env); + return Attributes.NULL; + } Path dir = getEnvironmentsDirectory(); if (dir == null) { // nothing to load - return props; + return envAttributes; } if (!Files.isDirectory(dir)) { LOG.warn("Not an environments directory: {}", dir); - return props; + return envAttributes; } List envPropertyFiles; @@ -824,6 +819,8 @@ private Properties loadEnvironmentProperties(String env) throws IOException if (LOG.isDebugEnabled()) LOG.debug("Environment property files {}", envPropertyFiles); + Attributes attributesLayer = envAttributes; + // Load each *.properties file for (Path file : envPropertyFiles) { @@ -831,12 +828,15 @@ private Properties loadEnvironmentProperties(String env) throws IOException { Properties tmp = new Properties(); tmp.load(stream); + + Attributes.Layer layer = new Attributes.Layer(attributesLayer); //put each property into our substitution pool - tmp.stringPropertyNames().forEach(k -> props.put(k, tmp.getProperty(k))); + tmp.stringPropertyNames().forEach(k -> layer.setAttribute(k, tmp.getProperty(k))); + attributesLayer = layer; } } - return props; + return attributesLayer; } /** @@ -935,11 +935,11 @@ public void setContainerScanJarPattern(String pattern) *

        * * @param classname the classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS + * @see DefaultContextHandlerFactory#CONTEXT_HANDLER_CLASS */ public void setContextHandlerClass(String classname) { - _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS, classname); + _environment.setAttribute(DefaultContextHandlerFactory.CONTEXT_HANDLER_CLASS, classname); } /** @@ -952,11 +952,11 @@ public void setContextHandlerClass(String classname) *

        * * @param classname the default classname for this environment's context deployable. - * @see Deployable#CONTEXT_HANDLER_CLASS_DEFAULT + * @see DefaultContextHandlerFactory#CONTEXT_HANDLER_CLASS_DEFAULT */ public void setDefaultContextHandlerClass(String classname) { - _environment.setAttribute(Deployable.CONTEXT_HANDLER_CLASS_DEFAULT, classname); + _environment.setAttribute(DefaultContextHandlerFactory.CONTEXT_HANDLER_CLASS_DEFAULT, classname); } /** diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index f4a4032e7c34..1a5dd6cd9a4c 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -30,24 +30,23 @@ public void testStateTransitionNewToDeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); - mockProvider.setDeploymentManager(depman); + depman.addAppProvider(mockProvider); depman.addLifeCycleBinding(pathtracker); depman.setContexts(new ContextHandlerCollection()); - depman.addBean(mockProvider); // Start DepMan depman.start(); // Trigger new App - mockProvider.createWebapp("foo-webapp-1"); - App app = depman.getApp("foo-webapp-1"); + App foo = mockProvider.createWebapp("foo-webapp-1"); + mockProvider.getManager().addApp(foo, AppLifeCycle.UNDEPLOYED); // Request Deploy of App - depman.requestAppGoal(app, "deployed"); + App app = depman.getApp("foo-webapp-1"); + depman.requestAppGoal(app, AppLifeCycle.DEPLOYED); // Setup Expectations. List expected = new ArrayList(); @@ -63,19 +62,17 @@ public void testStateTransitionReceive() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); - mockProvider.setDeploymentManager(depman); - + depman.addAppProvider(mockProvider); depman.addLifeCycleBinding(pathtracker); - depman.addBean(mockProvider); // Start DepMan depman.start(); - // Trigger new App - mockProvider.createWebapp("foo-webapp-1.war"); + // Create new App + App app = mockProvider.createWebapp("foo-webapp-1.war"); + mockProvider.getManager().addApp(app, AppLifeCycle.UNDEPLOYED); // Perform no goal request. @@ -89,28 +86,27 @@ public void testStateTransitionReceive() throws Exception public void testStateTransitionDeployedToUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); - depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); - mockProvider.setDeploymentManager(depman); + depman.addAppProvider(mockProvider); // Setup JMX MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); depman.addBean(mbContainer); depman.addLifeCycleBinding(pathtracker); - depman.addBean(mockProvider); depman.setContexts(new ContextHandlerCollection()); // Start DepMan depman.start(); - // Trigger new App + // Create new App App foo = mockProvider.createWebapp("foo-webapp-1"); - App app = depman.getApp(foo.getName()); + mockProvider.getManager().addApp(foo, AppLifeCycle.UNDEPLOYED); // Request Deploy of App - depman.requestAppGoal(app, "deployed"); + App app = depman.getApp(foo.getName()); + depman.requestAppGoal(app, AppLifeCycle.DEPLOYED); JmxServiceConnection jmxConnection = new JmxServiceConnection(); jmxConnection.connect(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 48cc1d85bb24..9f9e3d0a5c6a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -58,13 +58,10 @@ public void testReceiveApp() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); - mockProvider.setDeploymentManager(depman); - + depman.addAppProvider(mockProvider); depman.addLifeCycleBinding(pathtracker); - depman.addBean(mockProvider); // Start DepMan depman.start(); @@ -72,7 +69,8 @@ public void testReceiveApp() throws Exception try { // Trigger new App - mockProvider.createWebapp("foo-webapp-1.war"); + App foo = mockProvider.createWebapp("foo-webapp-1.war"); + mockProvider.getManager().addApp(foo, AppLifeCycle.UNDEPLOYED); // Test app tracking Collection apps = depman.getApps(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java index 3f1fa51dbe91..b23ebe9ea7d3 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.deploy; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.component.Environment; public class MockApp implements App { @@ -42,6 +43,12 @@ public ContextHandler getContextHandler() return contextHandler; } + @Override + public Environment getEnvironment() + { + return null; + } + @Override public String toString() { diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java index eb5fad253dca..d47eae603917 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java @@ -21,9 +21,9 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Environment; -public class MockAppProvider extends AbstractLifeCycle +public class MockAppProvider extends AbstractLifeCycle implements AppProvider { - private DeploymentManager deployMan; + private AppProvider.Manager deployMan; private Path webappsDir; public String getEnvironmentName() @@ -31,9 +31,15 @@ public String getEnvironmentName() return Environment.ensure("mock").getName(); } - public void setDeploymentManager(DeploymentManager deploymentManager) + public Manager getManager() { - this.deployMan = deploymentManager; + return this.deployMan; + } + + @Override + public void setManager(Manager manager) + { + this.deployMan = manager; } @Override @@ -47,7 +53,6 @@ public App createWebapp(String name) String basename = FileID.getBasename(name); MockApp app = new MockApp(basename); app.setContextHandler(createContextHandler(app)); - this.deployMan.addApp(app); return app; } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java index ee98a8ad3ea9..380c2f80761b 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderCoreWebappTest.java @@ -28,7 +28,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.server.handler.CoreWebAppContext; +import org.eclipse.jetty.server.handler.CoreContextHandler; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenPaths; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; @@ -64,9 +64,7 @@ public void startServer(DefaultProvider provider) throws Exception DeploymentManager deploymentManager = new DeploymentManager(); deploymentManager.setContexts(contexts); - - deploymentManager.addBean(provider); - provider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(provider); server.addBean(deploymentManager); server.start(); @@ -101,7 +99,7 @@ public void testExampleCoreDir() throws Exception String demoXmlStr = """ - + /demo @@ -113,7 +111,7 @@ public void testExampleCoreDir() throws Exception DefaultProvider defaultProvider = new DefaultProvider(); defaultProvider.addMonitoredDirectory(webapps); DefaultProvider.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); - coreConfig.setContextHandlerClass(CoreWebAppContext.class.getName()); + coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); startServer(defaultProvider); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java index cd60a063fec3..57c80a6b3dc7 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/DefaultProviderStartupTest.java @@ -23,7 +23,6 @@ import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; import org.eclipse.jetty.deploy.BarContextHandler; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenPaths; @@ -103,8 +102,8 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("core.properties"), Deployable.ENVIRONMENT_XML + "=etc/core-context.xml", StandardOpenOption.CREATE_NEW); - Files.writeString(environments.resolve("core-other.properties"), Deployable.ENVIRONMENT_XML + ".other=etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); + Files.writeString(environments.resolve("core.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/core-context.xml", StandardOpenOption.CREATE_NEW); + Files.writeString(environments.resolve("core-other.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + ".other=etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING); Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING); @@ -132,10 +131,10 @@ public void testStartupWithAbsoluteEnvironmentContext() throws Exception FS.ensureDirExists(environments); Files.writeString(environments.resolve("core.properties"), - String.format("%s=%s%n", Deployable.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), + String.format("%s=%s%n", DefaultContextHandlerFactory.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), StandardOpenOption.CREATE_NEW); Files.writeString(environments.resolve("core-other.properties"), - String.format("%s=%s%n", (Deployable.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), + String.format("%s=%s%n", (DefaultContextHandlerFactory.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), StandardOpenOption.CREATE_NEW); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); @@ -161,11 +160,11 @@ public void testNonEnvironmentPropertyFileNotApplied() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("non-env.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee8.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee9.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee10.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("not-core.properties"), Deployable.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("non-env.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee8.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee9.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee10.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("not-core.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -187,10 +186,10 @@ public void testPropertyOverriding() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("core-a.properties"), Deployable.ENVIRONMENT_XML + "=etc/a.xml"); - Files.writeString(environments.resolve("core-b.properties"), Deployable.ENVIRONMENT_XML + "=etc/b.xml"); - Files.writeString(environments.resolve("core-c.properties"), Deployable.ENVIRONMENT_XML + "=etc/c.xml"); - Files.writeString(environments.resolve("core-d.properties"), Deployable.ENVIRONMENT_XML + "=etc/d.xml"); + Files.writeString(environments.resolve("core-a.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/a.xml"); + Files.writeString(environments.resolve("core-b.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/b.xml"); + Files.writeString(environments.resolve("core-c.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/c.xml"); + Files.writeString(environments.resolve("core-d.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/d.xml"); Path etc = jettyBase.resolve("etc"); FS.ensureDirExists(etc); diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml index dc6dd93334a4..a9ccb0b34e6e 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -7,7 +7,7 @@ core - + diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties b/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties index 7b80290a89f1..142319a06adc 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties @@ -1,5 +1,5 @@ # Jetty Logging using jetty-slf4j-impl #org.eclipse.jetty.deploy.DeploymentTempDirTest.LEVEL=DEBUG -#org.eclipse.jetty.deploy.LEVEL=DEBUG +org.eclipse.jetty.deploy.LEVEL=DEBUG #org.eclipse.jetty.ee9.webapp.LEVEL=DEBUG #org.eclipse.jetty.util.Scanner=DEBUG diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 6b966aea9537..db20501264ad 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -16,7 +16,7 @@ import java.util.Objects; import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; @@ -35,11 +35,11 @@ * Jetty that have been discovered via OSGI either as bundles or services. *

        */ -public abstract class AbstractContextProvider extends AbstractLifeCycle +public abstract class AbstractContextProvider extends AbstractLifeCycle implements AppProvider { private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); - private DeploymentManager _deploymentManager; + private AppProvider.Manager _deploymentManager; private Server _server; private ContextFactory _contextFactory; private String _environment; @@ -68,25 +68,25 @@ public ContextHandler createContextHandler(App app) throws Exception return null; //Create a ContextHandler suitable to deploy in OSGi - ContextHandler h = _contextFactory.createContextHandler(this, app); - return h; + return _contextFactory.createContextHandler(this, app); } - public void setDeploymentManager(DeploymentManager deploymentManager) + public String getEnvironmentName() { - _deploymentManager = deploymentManager; + return _environment; } - public String getEnvironmentName() + @Override + public void setManager(Manager manager) { - return _environment; + _deploymentManager = manager; } - public DeploymentManager getDeploymentManager() + public AppProvider.Manager getManager() { return _deploymentManager; } - + /** * @param tldBundles Comma separated list of bundles that contain tld jars * that should be setup on the context instances created here. diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index 7bc69b407c5f..1d5867a29095 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -190,7 +190,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception _appMap.put(app.getPath(), app); List apps = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); apps.add(app); - getDeploymentManager().addApp(app); + getManager().addApp(app); added = true; } @@ -214,7 +214,7 @@ public boolean bundleRemoved(Bundle bundle) throws Exception { if (_appMap.remove(app.getPath()) != null) { - getDeploymentManager().removeApp(app); + getManager().removeApp(app); removed = true; } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index d70bd829aec4..26234bc3cef0 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -189,7 +189,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception app.setPathToResourceBase(staticResourcesLocation); app.setContextHandler(createContextHandler(app)); _bundleMap.put(bundle, app); - getDeploymentManager().addApp(app); + getManager().addApp(app); return true; } @@ -201,7 +201,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception app.setContextHandler(createContextHandler(app)); app.setPathToResourceBase(base); _bundleMap.put(bundle, app); - getDeploymentManager().addApp(app); + getManager().addApp(app); return true; } @@ -214,7 +214,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception app.setPathToResourceBase(base); app.setContextHandler(createContextHandler(app)); _bundleMap.put(bundle, app); - getDeploymentManager().addApp(app); + getManager().addApp(app); return true; } @@ -239,7 +239,7 @@ public boolean bundleRemoved(Bundle bundle) throws Exception App app = _bundleMap.remove(bundle); if (app != null) { - getDeploymentManager().removeApp(app); + getManager().removeApp(app); return true; } return false; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java index f38a593b3a4e..8bd44954a85b 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java @@ -44,6 +44,7 @@ public class OSGiApp implements App { private static final Logger LOG = LoggerFactory.getLogger(OSGiApp.class); + private static final String ENVIRONMENT = "environment"; private final String _bundleName; private final Path _bundlePath; @@ -132,9 +133,9 @@ public OSGiApp(Bundle bundle) { String key = keys.nextElement(); String val = headers.get(key); - if (Deployable.ENVIRONMENT.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_ENVIRONMENT.equalsIgnoreCase(key)) + if (ENVIRONMENT.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_ENVIRONMENT.equalsIgnoreCase(key)) { - getProperties().put(Deployable.ENVIRONMENT, val); + getProperties().put(ENVIRONMENT, val); } else if (Deployable.DEFAULTS_DESCRIPTOR.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH.equalsIgnoreCase(key)) { diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml index 692929f174d3..4cd022f933f5 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml @@ -7,7 +7,7 @@ core - +
        diff --git a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod index 6e02d6341f04..48470e95e611 100644 --- a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod +++ b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod @@ -12,4 +12,4 @@ etc/jetty-core-deploy.xml [ini-template] ## Default ContextHandler class for "core" environment deployments -# contextHandlerClass=org.eclipse.jetty.server.handler.CoreWebAppContext +# contextHandlerClass=org.eclipse.jetty.server.handler.CoreContextHandler diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index 7a3d25858e2f..df4475c3a162 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -13,15 +13,18 @@ package org.eclipse.jetty.server; +import java.io.File; import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; /** - * Interface that can be implemented by ContextHandlers within Environments to allow configuration - * to be passed from the DeploymentManager without dependencies on the Deployment module itself. + * Interface that can be implemented by a {@link ContextHandler} + * to allow configuration to be passed from the DeploymentManager without + * dependencies on the jetty-deploy module itself. */ public interface Deployable { @@ -53,22 +56,108 @@ public interface Deployable }; String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; + + /** + *

        Attribute key name: Temp Directory for context.

        + * + *

        Value can be a {@link File}, {@code String}, or {@link java.nio.file.Path}

        + * + * @see ContextHandler#setTempDirectory(File) + */ String TEMP_DIR = "jetty.deploy.tempDir"; + /** + *

        Attribute key name: The Configuration Classes for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a {@code String[]} (String Array)

        + */ String CONFIGURATION_CLASSES = "jetty.deploy.configurationClasses"; + /** + *

        Attribute key name: The Container Scan Jar Pattern for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a regex {@code String}

        + */ String CONTAINER_SCAN_JARS = "jetty.deploy.containerScanJarPattern"; + /** + *

        Attribute key name: Specifies the context-path of the {@link ContextHandler}

        + * + *

        Value is a {@code String}

        + * + * @see ContextHandler#setContextPath(String) + */ String CONTEXT_PATH = "jetty.deploy.contextPath"; - String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; - String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; + /** + *

        Attribute key name: Specifies the default descriptor to user for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a {@code String} pointing to a filesystem path

        + */ String DEFAULTS_DESCRIPTOR = "jetty.deploy.defaultsDescriptor"; - String ENVIRONMENT = "environment"; - String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; + /** + *

        Attribute key name: Specifies the flag to extract/unpack a WAR file for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a {@link Boolean}

        + */ String EXTRACT_WARS = "jetty.deploy.extractWars"; + /** + *

        Attribute key name: Specifies the Parent ClassLoader Priority for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a {@link Boolean}

        + */ String PARENT_LOADER_PRIORITY = "jetty.deploy.parentLoaderPriority"; + /** + *

        Attribute key name: Specifies the Servlet Container Initializer Exclusion Pattern for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a regex {@code String}

        + */ String SCI_EXCLUSION_PATTERN = "jetty.deploy.servletContainerInitializerExclusionPattern"; + /** + *

        Attribute key name: Specifies the Servlet Container Initializer Ordering for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a comma-delimited {@code String}

        + * + * @see "ServletContainerInitializerOrdering in EE specific package for details on syntax" + */ String SCI_ORDER = "jetty.deploy.servletContainerInitializerOrder"; + /** + *

        Attribute key name: Specifies the WAR file (if relevant) of the deployable for EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a {@code String} pointing to a filesystem path

        + */ String WAR = "jetty.deploy.war"; + /** + *

        Attribute key name: Specifies the pattern of Jars in {@code WEB-INF/lib} to scan for annotations in EE based deployments.

        + * + *

        Non-EE deployments will not use this configuration.

        + * + *

        Value is a regex {@code String}

        + */ String WEBINF_SCAN_JARS = "jetty.deploy.webInfScanJarPattern"; + /** + *

        Attribute key name: Specifies the main {@link java.nio.file.Path} that is being deployed.

        + * + *

        Value is a {@link java.nio.file.Path}

        + */ String MAIN_PATH = "jetty.deploy.paths.main"; + /** + *

        Attribute key name: Specifies the list of other {@link java.nio.file.Path} that are relevant to the deployment.

        + * + *

        Value is a {@link java.util.Collection} of {@link java.nio.file.Path} instances

        + */ String OTHER_PATHS = "jetty.deploy.paths.other"; void initializeDefaults(Attributes attributes); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreContextHandler.java similarity index 96% rename from jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java rename to jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreContextHandler.java index 8cf039b2cd99..4f70a47aac7b 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreWebAppContext.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/CoreContextHandler.java @@ -43,7 +43,7 @@ * A classloader isolated Core WebApp. * *

        - * The Base Resource represents the metadata base that defines this {@code CoreWebAppContext}. + * The Base Resource represents the metadata base that defines this {@code CoreContextHandler}. *

        *

        * The metadata base can be a directory on disk, or a non-traditional {@code war} file with the following contents. @@ -59,20 +59,21 @@ * unpacked into the temp directory defined by this core webapp. *

        */ -public class CoreWebAppContext extends ContextHandler implements Deployable +// TODO: rename CoreContextHandler +public class CoreContextHandler extends ContextHandler implements Deployable { - private static final Logger LOG = LoggerFactory.getLogger(CoreWebAppContext.class); + private static final Logger LOG = LoggerFactory.getLogger(CoreContextHandler.class); private static final String ORIGINAL_BASE_RESOURCE = "org.eclipse.jetty.webapp.originalBaseResource"; private boolean _initialized = false; private List _extraClasspath; private boolean _builtClassLoader = false; - public CoreWebAppContext() + public CoreContextHandler() { this("/"); } - public CoreWebAppContext(String contextPath) + public CoreContextHandler(String contextPath) { super(); setContextPath(contextPath); @@ -92,7 +93,7 @@ public void initializeDefaults(Attributes attributes) { try { - // This CoreWebAppContext is arriving via a Deployer + // This CoreContextHandler is arriving via a Deployer for (String keyName : attributes.getAttributeNameSet()) { Object value = attributes.getAttribute(keyName); diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index 9716e9395378..4b354e75fdc9 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -167,13 +167,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE10ContextFactory(_myBundle)); - contextProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE10WebAppFactory(_myBundle)); - contextProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(webAppProvider); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -272,7 +272,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -281,7 +281,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -308,9 +308,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -367,7 +367,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -376,7 +376,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -483,9 +483,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 3b35da43939d..6b7922397adb 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -107,8 +107,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception appProvider.configureEnvironment("ee10"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - appProvider.setDeploymentManager(deploymentManager); - deploymentManager.addBean(appProvider); + deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 7741b3343bd8..1290efad8163 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -167,13 +167,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE11ContextFactory(_myBundle)); - contextProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE11WebAppFactory(_myBundle)); - webAppProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(webAppProvider); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -272,7 +272,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -281,7 +281,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -308,9 +308,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -367,7 +367,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -376,7 +376,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -483,9 +483,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index 374bdb8072f3..b39e0e324982 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -107,8 +107,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception appProvider.configureEnvironment("ee11"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - appProvider.setDeploymentManager(deploymentManager); - deploymentManager.addBean(appProvider); + deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 3cf44cb4298b..701c7d9dd61a 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -166,13 +166,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE9ContextFactory(_myBundle)); - contextProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE9WebAppFactory(_myBundle)); - webAppProvider.setDeploymentManager(deploymentManager); + deploymentManager.addAppProvider(webAppProvider); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -270,7 +270,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -279,7 +279,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -306,9 +306,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -365,7 +365,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App if (!(app instanceof OSGiApp osgiApp)) throw new IllegalArgumentException("App is not OSGi"); - String jettyHome = (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -374,7 +374,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -480,9 +480,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getDeploymentManager().getServer()); + xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getDeploymentManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index e6c1efab755d..ada52d250fab 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -102,8 +102,7 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro appProvider.configureEnvironment("ee9"); appProvider.addMonitoredDirectory(docroots); appProvider.setScanInterval(1); - appProvider.setDeploymentManager(deploymentManager); - deploymentManager.addBean(appProvider); + deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); // Server handlers From a36a26e39a3189e0784a4219b8f30f783203fa40 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 29 Jan 2025 14:54:19 -0600 Subject: [PATCH 050/104] Removal of App interface, it compiles --- .../src/main/config/etc/jetty-deploy.xml | 4 +- .../config/etc/jetty-deployment-manager.xml | 2 +- .../src/main/java/module-info.java | 2 +- .../java/org/eclipse/jetty/deploy/App.java | 47 ----- .../org/eclipse/jetty/deploy/AppProvider.java | 49 ----- ...ycle.java => ContextHandlerLifeCycle.java} | 41 ++-- ...ory.java => ContextHandlerManagement.java} | 17 +- .../jetty/deploy/DeploymentManager.java | 191 ++++++++---------- .../jetty/deploy/bindings/DebugBinding.java | 10 +- .../deploy/bindings/OrderedGroupBinding.java | 22 +- .../deploy/bindings/StandardDeployer.java | 16 +- .../deploy/bindings/StandardStarter.java | 15 +- .../deploy/bindings/StandardStopper.java | 13 +- .../deploy/bindings/StandardUndeployer.java | 12 +- .../deploy/jmx/DeploymentManagerMBean.java | 50 ++--- .../DefaultContextHandlerFactory.java | 54 +++-- .../{providers => scan}/DefaultProvider.java | 103 ++++------ .../{providers => scan}/DeployAction.java | 8 +- .../DeployActionComparator.java | 2 +- .../ScanTrackedApp.java} | 24 +-- .../{providers => scan}/package-info.java | 4 +- .../deploy/AppLifeCyclePathCollector.java | 5 +- ....java => ContextHandlerLifeCycleTest.java} | 10 +- .../DeploymentManagerLifeCycleRouteTest.java | 42 ++-- .../jetty/deploy/DeploymentManagerTest.java | 61 ++---- .../org/eclipse/jetty/deploy/MockApp.java | 57 ------ ...rovider.java => PhonyContextProvider.java} | 30 ++- .../{providers => scan}/DefaultAppTest.java | 112 +++++----- .../DefaultProviderCoreWebappTest.java | 11 +- .../DefaultProviderDeferredStartupTest.java | 2 +- .../DefaultProviderRuntimeUpdatesTest.java | 2 +- .../DefaultProviderStartupTest.java | 2 +- .../DefaultProviderTest.java | 103 ++++++---- .../DeployActionComparatorTest.java | 46 ++--- .../resources/jetty-core-deploy-custom.xml | 2 +- .../jetty/osgi/AbstractContextProvider.java | 39 ++-- .../jetty/osgi/BundleContextProvider.java | 13 +- .../jetty/osgi/BundleWebAppProvider.java | 20 +- .../eclipse/jetty/osgi/ContextFactory.java | 4 +- .../jetty/osgi/JettyServerFactory.java | 5 +- .../java/org/eclipse/jetty/osgi/OSGiApp.java | 78 ++++--- .../org/eclipse/jetty/osgi/OSGiDeployer.java | 27 ++- .../eclipse/jetty/osgi/OSGiUndeployer.java | 37 ++-- .../src/main/config/etc/jetty-core-deploy.xml | 2 +- .../jetty/server/handler/ContextHandler.java | 28 +++ .../eclipse/jetty/xml/XmlConfiguration.java | 12 +- .../jetty/ee10/osgi/boot/EE10Activator.java | 33 ++- .../jetty/ee10/test/DeploymentErrorTest.java | 75 +++---- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 2 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 33 ++- .../jetty/ee11/test/DeploymentErrorTest.java | 75 +++---- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee11-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee8-deploy.xml | 2 +- .../jetty/ee9/osgi/boot/EE9Activator.java | 32 ++- .../jetty/ee9/test/DeploymentErrorTest.java | 118 +++++------ .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 2 +- 62 files changed, 800 insertions(+), 1022 deletions(-) delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{AppLifeCycle.java => ContextHandlerLifeCycle.java} (75%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ContextHandlerFactory.java => ContextHandlerManagement.java} (62%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultContextHandlerFactory.java (90%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProvider.java (92%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers => scan}/DeployAction.java (85%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers => scan}/DeployActionComparator.java (97%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers/DefaultApp.java => scan/ScanTrackedApp.java} (93%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{providers => scan}/package-info.java (85%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{AppLifeCycleTest.java => ContextHandlerLifeCycleTest.java} (94%) delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockApp.java rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{MockAppProvider.java => PhonyContextProvider.java} (73%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultAppTest.java (54%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProviderCoreWebappTest.java (94%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProviderDeferredStartupTest.java (99%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProviderRuntimeUpdatesTest.java (99%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProviderStartupTest.java (99%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DefaultProviderTest.java (84%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{providers => scan}/DeployActionComparatorTest.java (80%) diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 8a85aa04bb20..e19057164720 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -8,7 +8,7 @@ - + @@ -37,7 +37,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml index 5bb7dc16084c..da1f70a91121 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-manager.xml @@ -16,7 +16,7 @@
        - - + core diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 735dfb47b863..500043787d5d 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -134,6 +134,7 @@ public static ContextHandler getContextHandler(Request request) private final List _contextListeners = new CopyOnWriteArrayList<>(); private final List _vhosts = new ArrayList<>(); + private String _id; private String _displayName; private String _contextPath = "/"; private boolean _rootContext = true; @@ -191,6 +192,10 @@ public ContextHandler(Handler handler, String contextPath) if (contextPath != null) setContextPath(contextPath); + // Copy ID if ContextHandler is wrapped (it can be overridden if needed) + if (handler instanceof ContextHandler contextHandler) + _id = contextHandler.getID(); + if (File.separatorChar == '/') addAliasCheck(new SymlinkAllowedResourceAliasChecker(this)); @@ -499,6 +504,17 @@ public String getDisplayName() return _contextPath; } + /** + * The ID of the context. + * + * @return the ID of the context, or null if not defined + */ + @ManagedAttribute(value = "ID of the Context") + public String getID() + { + return _id; + } + /** * Get if this context support cross context dispatch, either as originator or target. * @return True if this context supports cross context dispatch. @@ -1125,6 +1141,16 @@ public void setDisplayName(String servletContextName) _displayName = servletContextName; } + /** + * A unique optional ID for this ContextHandler. + * + * @param id the id; + */ + public void setID(String id) + { + _id = id; + } + /** * @return Returns the base resource as a string. */ @@ -1343,6 +1369,8 @@ public String toString() b.append(TypeUtil.toShortName(getClass())).append('@').append(Integer.toString(hashCode(), 16)); b.append('{'); + if (getID() != null) + b.append("id=").append(getID()).append(","); if (getDisplayName() != null) b.append(getDisplayName()).append(','); b.append(getContextPath()); diff --git a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 7494f94621a0..fe644c0bc9c7 100644 --- a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -228,9 +228,9 @@ private static int calculateDepth(Class c) *
      * * @param server The Server object to set - * @param webapp The webapps Resource + * @param webappsDir The webapps Resource directory */ - public void setJettyStandardIdsAndProperties(Object server, Path webapp) + public void setJettyStandardIdsAndProperties(Object server, Path webappsDir) { try { @@ -245,11 +245,11 @@ public void setJettyStandardIdsAndProperties(Object server, Path webapp) getProperties().put("jetty.base", base.toString()); getProperties().put("jetty.base.uri", normalizeURI(base.toUri().toASCIIString())); - if (webapp != null) + if (webappsDir != null && Files.isDirectory(webappsDir)) { - getProperties().put("jetty.webapp", webapp.toString()); - getProperties().put("jetty.webapps", webapp.getParent().toString()); - getProperties().put("jetty.webapps.uri", normalizeURI(webapp.getParent().toUri().toString())); + getProperties().put("jetty.webapp", webappsDir.toString()); + getProperties().put("jetty.webapps", webappsDir.getParent().toString()); + getProperties().put("jetty.webapps.uri", normalizeURI(webappsDir.getParent().toUri().toString())); } } catch (Exception e) diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index 4b354e75fdc9..f2106edf0af5 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -25,7 +25,6 @@ import java.util.Map; import java.util.Optional; -import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -166,14 +165,12 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE10ContextFactory(_myBundle)); - deploymentManager.addAppProvider(contextProvider); + contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE10ContextFactory(_myBundle)); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE10WebAppFactory(_myBundle)); - deploymentManager.addAppProvider(webAppProvider); + webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE10WebAppFactory(_myBundle)); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -268,11 +265,10 @@ public EE10ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) throws Exception { - OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -281,7 +277,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -304,13 +300,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(contextHandler.getClassLoader()); WebAppClassLoader.runWithServerClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(contextHandler).newResource(contextXmlURI)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -361,13 +358,10 @@ public EE10WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) throws Exception { - if (!(app instanceof OSGiApp osgiApp)) - throw new IllegalArgumentException("App is not OSGi"); - - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -376,7 +370,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -479,13 +473,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); WebAppClassLoader.runWithServerClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(webApp).newResource(contextXmlUri)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 6b7922397adb..93337ac1808a 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.net.URI; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,11 +25,10 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.DefaultProvider; +import org.eclipse.jetty.deploy.scan.DefaultProvider; import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -61,7 +59,9 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(WorkDirExtension.class) public class DeploymentErrorTest @@ -103,11 +103,10 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(); + DefaultProvider appProvider = new DefaultProvider(deploymentManager); appProvider.configureEnvironment("ee10"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); @@ -127,7 +126,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } @AfterEach - public void tearDownServer() throws Exception + public void tearDownServer() { if (stacklessLogging != null) stacklessLogging.close(); @@ -175,11 +174,11 @@ public void testInitialBadAppUnavailableFalse() throws Exception { startServer(docroots -> copyBadApp("badapp-unavailable-false.xml", docroots)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); + List contexts = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", contexts.size(), is(1)); String contextPath = "/badapp-uaf"; - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + ContextHandler context = findContext(contextPath, contexts); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(true)); assertThat("ContextHandler.isFailed", context.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -219,12 +218,12 @@ public void testDelayedAddBadAppUnavailableTrue() throws Exception copyBadApp("badapp.xml", docroots); // Wait for deployment manager to do its thing - assertThat("AppLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); + assertThat("ContextHandlerLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler context = findContext(contextPath, apps); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(false)); assertThat("ContextHandler.isFailed", context.isFailed(), is(true)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -264,12 +263,12 @@ public void testDelayedAddBadAppUnavailableFalse() throws Exception copyBadApp("badapp-unavailable-false.xml", docroots); // Wait for deployment manager to do its thing - startTracking.startedLatch.await(3, TimeUnit.SECONDS); + assertTrue(startTracking.startedLatch.await(3, TimeUnit.SECONDS)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler context = findContext(contextPath, apps); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(true)); assertThat("ContextHandler.isFailed", context.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -292,28 +291,20 @@ public void testDelayedAddBadAppUnavailableFalse() throws Exception private void assertHttpState(String contextPath, int expectedStatusCode) throws Exception { URI destURI = server.getURI().resolve(contextPath); - HttpClient client = new HttpClient(); - try + try (HttpClient client = new HttpClient()) { client.start(); ContentResponse response = client.newRequest(destURI).method(HttpMethod.GET).send(); assertThat("GET Response: " + destURI, response.getStatus(), is(expectedStatusCode)); } - finally - { - client.stop(); - } } - private App findApp(String contextPath, List apps) + private ContextHandler findContext(String contextPath, List apps) { - for (App app : apps) + for (ContextHandler contextHandler : apps) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - continue; // skip if (contextPath.equals(contextHandler.getContextPath())) - return app; + return contextHandler; } return null; } @@ -359,7 +350,7 @@ public void postConfigure(WebAppContext context) } } - public static class AppLifeCycleTrackingBinding implements AppLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -374,27 +365,25 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) @Override public String[] getBindingTargets() { - return new String[]{AppLifeCycle.STARTING, AppLifeCycle.STARTED, AppLifeCycle.FAILED}; + return new String[]{ + ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + }; } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - return; - if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index e5d3040f5a35..d326d8a07c9d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index a54eddd9c001..51413e5e9d7a 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 607d0151d03d..142c9f6d92ad 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -4,7 +4,7 @@ - + ee10 diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 1290efad8163..b57c4d1c56ad 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -25,7 +25,6 @@ import java.util.Map; import java.util.Optional; -import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -166,14 +165,12 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE11ContextFactory(_myBundle)); - deploymentManager.addAppProvider(contextProvider); + contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE11ContextFactory(_myBundle)); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE11WebAppFactory(_myBundle)); - deploymentManager.addAppProvider(webAppProvider); + webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE11WebAppFactory(_myBundle)); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -268,11 +265,10 @@ public EE11ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) throws Exception { - OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -281,7 +277,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -304,13 +300,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(contextHandler.getClassLoader()); WebAppClassLoader.runWithHiddenClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(contextHandler).newResource(contextXmlURI)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -361,13 +358,10 @@ public EE11WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) throws Exception { - if (!(app instanceof OSGiApp osgiApp)) - throw new IllegalArgumentException("App is not OSGi"); - - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -376,7 +370,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -479,13 +473,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); WebAppClassLoader.runWithHiddenClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(webApp).newResource(contextXmlUri)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index b39e0e324982..9c1e7b479871 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.net.URI; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,11 +25,10 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.DefaultProvider; +import org.eclipse.jetty.deploy.scan.DefaultProvider; import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -61,7 +59,9 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(WorkDirExtension.class) public class DeploymentErrorTest @@ -103,11 +103,10 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(); + DefaultProvider appProvider = new DefaultProvider(deploymentManager); appProvider.configureEnvironment("ee11"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); - deploymentManager.addAppProvider(appProvider); server.addBean(deploymentManager); @@ -127,7 +126,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } @AfterEach - public void tearDownServer() throws Exception + public void tearDownServer() { if (stacklessLogging != null) stacklessLogging.close(); @@ -175,11 +174,11 @@ public void testInitialBadAppUnavailableFalse() throws Exception { startServer(docroots -> copyBadApp("badapp-unavailable-false.xml", docroots)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); + List contexts = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", contexts.size(), is(1)); String contextPath = "/badapp-uaf"; - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + ContextHandler context = findContext(contextPath, contexts); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(true)); assertThat("ContextHandler.isFailed", context.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -219,12 +218,12 @@ public void testDelayedAddBadAppUnavailableTrue() throws Exception copyBadApp("badapp.xml", docroots); // Wait for deployment manager to do its thing - assertThat("AppLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); + assertThat("ContextHandlerLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler context = findContext(contextPath, apps); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(false)); assertThat("ContextHandler.isFailed", context.isFailed(), is(true)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -264,12 +263,12 @@ public void testDelayedAddBadAppUnavailableFalse() throws Exception copyBadApp("badapp-unavailable-false.xml", docroots); // Wait for deployment manager to do its thing - startTracking.startedLatch.await(3, TimeUnit.SECONDS); + assertTrue(startTracking.startedLatch.await(3, TimeUnit.SECONDS)); - List apps = new ArrayList<>(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler context = app.getContextHandler(); + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler context = findContext(contextPath, apps); + assertNotNull(context); assertThat("ContextHandler.isStarted", context.isStarted(), is(true)); assertThat("ContextHandler.isFailed", context.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); @@ -292,28 +291,20 @@ public void testDelayedAddBadAppUnavailableFalse() throws Exception private void assertHttpState(String contextPath, int expectedStatusCode) throws Exception { URI destURI = server.getURI().resolve(contextPath); - HttpClient client = new HttpClient(); - try + try (HttpClient client = new HttpClient()) { client.start(); ContentResponse response = client.newRequest(destURI).method(HttpMethod.GET).send(); assertThat("GET Response: " + destURI, response.getStatus(), is(expectedStatusCode)); } - finally - { - client.stop(); - } } - private App findApp(String contextPath, List apps) + private ContextHandler findContext(String contextPath, List apps) { - for (App app : apps) + for (ContextHandler contextHandler : apps) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - continue; // skip if (contextPath.equals(contextHandler.getContextPath())) - return app; + return contextHandler; } return null; } @@ -359,7 +350,7 @@ public void postConfigure(WebAppContext context) } } - public static class AppLifeCycleTrackingBinding implements AppLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -374,27 +365,25 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) @Override public String[] getBindingTargets() { - return new String[]{AppLifeCycle.STARTING, AppLifeCycle.STARTED, AppLifeCycle.FAILED}; + return new String[]{ + ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + }; } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - return; - if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 4e3216db9a79..199209666938 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 8a5fbdf44277..f13f5490a7af 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index e10bced8c15b..ca3dd53b9ef7 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -4,7 +4,7 @@ - + ee11 diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index e0b6e3b4541e..2610aa3fbcce 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -4,7 +4,7 @@ - + ee8 diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 701c7d9dd61a..00f3ee9e802c 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -25,7 +25,6 @@ import java.util.Map; import java.util.Optional; -import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -165,14 +164,12 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(ENVIRONMENT, server, new EE9ContextFactory(_myBundle)); - deploymentManager.addAppProvider(contextProvider); + contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE9ContextFactory(_myBundle)); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(ENVIRONMENT, server, new EE9WebAppFactory(_myBundle)); - deploymentManager.addAppProvider(webAppProvider); + webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE9WebAppFactory(_myBundle)); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -266,11 +263,11 @@ public EE9ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp app) throws Exception { OSGiApp osgiApp = OSGiApp.class.cast(app); - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -279,7 +276,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App contextHandler.setBaseResource(osgiApp.getBundleResource()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); @@ -302,13 +299,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(contextHandler.getClassLoader()); WebAppClassLoader.runWithServerClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(contextHandler).newResource(contextXmlURI)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); return null; @@ -359,13 +357,10 @@ public EE9WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, App app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) throws Exception { - if (!(app instanceof OSGiApp osgiApp)) - throw new IllegalArgumentException("App is not OSGi"); - - String jettyHome = (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -374,7 +369,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App webApp.initializeDefaults(provider.getAttributes()); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getManager().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); @@ -476,13 +471,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, App Thread.currentThread().setContextClassLoader(webApp.getClassLoader()); WebAppClassLoader.runWithServerClassAccess(() -> { + Server server = provider.getServer(); XmlConfiguration xmlConfiguration = new XmlConfiguration(ResourceFactory.of(webApp).newResource(contextXmlUri)); + xmlConfiguration.setJettyStandardIdsAndProperties(server, null); WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - xmlConfiguration.getIdMap().put("Server", provider.getManager().getServer()); properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); - properties.put(OSGiServerConstants.JETTY_HOME, (String)provider.getManager().getServer().getAttribute(OSGiServerConstants.JETTY_HOME)); + properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); return null; diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index ada52d250fab..6f955cbdad48 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.net.URI; import java.nio.file.Path; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -26,11 +25,10 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.App; -import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.providers.DefaultProvider; +import org.eclipse.jetty.deploy.scan.DefaultProvider; import org.eclipse.jetty.ee9.webapp.AbstractConfiguration; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -50,6 +48,7 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.component.Environment; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -60,6 +59,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(WorkDirExtension.class) public class DeploymentErrorTest @@ -98,11 +98,10 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(); + DefaultProvider appProvider = new DefaultProvider(deploymentManager); appProvider.configureEnvironment("ee9"); - appProvider.addMonitoredDirectory(docroots); appProvider.setScanInterval(1); - deploymentManager.addAppProvider(appProvider); + appProvider.addMonitoredDirectory(docroots); server.addBean(deploymentManager); // Server handlers @@ -121,11 +120,11 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } @AfterEach - public void tearDownServer() throws Exception + public void tearDownServer() { if (stacklessLogging != null) stacklessLogging.close(); - server.stop(); + LifeCycle.stop(server); } private void copyBadApp(String sourceXml, Path docroots) @@ -170,22 +169,15 @@ public void testInitialBadAppUnavailableFalse(WorkDir workDir) throws Exception { startServer(docroots -> copyBadApp("badapp-unavailable-false.xml", docroots), workDir.getEmptyPathDir()); - List apps = new ArrayList<>(); - apps.addAll(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); + List contexts = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", contexts.size(), is(1)); String contextPath = "/badapp-uaf"; - App app = findApp(contextPath, apps); - ContextHandler coreContext = app.getContextHandler(); - org.eclipse.jetty.ee9.nested.ContextHandler contextHandler = null; - if (coreContext instanceof org.eclipse.jetty.ee9.nested.ContextHandler.CoreContextHandler coreContextHandler) - { - contextHandler = coreContextHandler.getContextHandler(); - } + ContextHandler contextHandler = findContext(contextPath, contexts); assertNotNull(contextHandler); assertThat("ContextHandler.isStarted", contextHandler.isStarted(), is(true)); assertThat("ContextHandler.isFailed", contextHandler.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", contextHandler.isAvailable(), is(false)); - WebAppContext webapp = (WebAppContext)contextHandler; + WebAppContext webapp = getWebAppContext(contextHandler); TrackedConfiguration trackedConfiguration = null; for (Configuration webappConfig : webapp.getConfigurations()) { @@ -221,24 +213,16 @@ public void testDelayedAddBadAppUnavailableTrue(WorkDir workDir) throws Exceptio copyBadApp("badapp.xml", docroots); // Wait for deployment manager to do its thing - assertThat("AppLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); - - List apps = new ArrayList<>(); - apps.addAll(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler coreContext = app.getContextHandler(); - org.eclipse.jetty.ee9.nested.ContextHandler contextHandler = null; - if (coreContext instanceof org.eclipse.jetty.ee9.nested.ContextHandler.CoreContextHandler coreContextHandler) - { - contextHandler = coreContextHandler.getContextHandler(); - } + assertThat("ContextHandlerLifeCycle.FAILED event occurred", startTracking.failedLatch.await(3, TimeUnit.SECONDS), is(true)); + + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler contextHandler = findContext(contextPath, apps); assertNotNull(contextHandler); - assertThat("ContextHandler.isStarted", contextHandler.isStarted(), is(false)); assertThat("ContextHandler.isFailed", contextHandler.isFailed(), is(true)); assertThat("ContextHandler.isAvailable", contextHandler.isAvailable(), is(false)); - WebAppContext webapp = (WebAppContext)contextHandler; + WebAppContext webapp = getWebAppContext(contextHandler); TrackedConfiguration trackedConfiguration = null; for (Configuration webappConfig : webapp.getConfigurations()) { @@ -274,24 +258,16 @@ public void testDelayedAddBadAppUnavailableFalse(WorkDir workDir) throws Excepti copyBadApp("badapp-unavailable-false.xml", docroots); // Wait for deployment manager to do its thing - startTracking.startedLatch.await(3, TimeUnit.SECONDS); - - List apps = new ArrayList<>(); - apps.addAll(deploymentManager.getApps()); - assertThat("Apps tracked", apps.size(), is(1)); - App app = findApp(contextPath, apps); - ContextHandler coreContext = app.getContextHandler(); - org.eclipse.jetty.ee9.nested.ContextHandler contextHandler = null; - if (coreContext instanceof org.eclipse.jetty.ee9.nested.ContextHandler.CoreContextHandler coreContextHandler) - { - contextHandler = coreContextHandler.getContextHandler(); - } + assertTrue(startTracking.startedLatch.await(3, TimeUnit.SECONDS)); + + List apps = deploymentManager.getContextHandlers().stream().toList(); + assertThat("Contexts tracked", apps.size(), is(1)); + ContextHandler contextHandler = findContext(contextPath, apps); assertNotNull(contextHandler); - assertThat("ContextHandler.isStarted", contextHandler.isStarted(), is(true)); assertThat("ContextHandler.isFailed", contextHandler.isFailed(), is(false)); assertThat("ContextHandler.isAvailable", contextHandler.isAvailable(), is(false)); - WebAppContext webapp = (WebAppContext)contextHandler; + WebAppContext webapp = getWebAppContext(contextHandler); TrackedConfiguration trackedConfiguration = null; for (Configuration webappConfig : webapp.getConfigurations()) { @@ -307,32 +283,34 @@ public void testDelayedAddBadAppUnavailableFalse(WorkDir workDir) throws Excepti assertHttpState(contextPath, HttpStatus.SERVICE_UNAVAILABLE_503); } + private WebAppContext getWebAppContext(ContextHandler contextHandler) + { + if (contextHandler instanceof org.eclipse.jetty.ee9.nested.ContextHandler.CoreContextHandler coreContextHandler) + { + org.eclipse.jetty.ee9.nested.ContextHandler nestedContextHandler = coreContextHandler.getContextHandler(); + if (nestedContextHandler instanceof WebAppContext webAppContext) + return webAppContext; + } + return null; + } + private void assertHttpState(String contextPath, int expectedStatusCode) throws Exception { URI destURI = server.getURI().resolve(contextPath); - HttpClient client = new HttpClient(); - try + try (HttpClient client = new HttpClient()) { client.start(); ContentResponse response = client.newRequest(destURI).method(HttpMethod.GET).send(); assertThat("GET Response: " + destURI, response.getStatus(), is(expectedStatusCode)); } - finally - { - client.stop(); - } } - private App findApp(String contextPath, List apps) + private ContextHandler findContext(String contextPath, List apps) { - for (App app : apps) + for (ContextHandler contextHandler : apps) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - continue; // skip - if (contextPath.equals(contextHandler.getContextPath())) - return app; + return contextHandler; } return null; } @@ -378,7 +356,7 @@ public void postConfigure(WebAppContext context) throws Exception } } - public static class AppLifeCycleTrackingBinding implements AppLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -393,27 +371,25 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) @Override public String[] getBindingTargets() { - return new String[]{AppLifeCycle.STARTING, AppLifeCycle.STARTED, AppLifeCycle.FAILED}; + return new String[]{ + ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + }; } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, App app) + public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) { - ContextHandler contextHandler = app.getContextHandler(); - if (contextHandler == null) - return; - if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(AppLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 781a06e7883e..551984418106 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -80,7 +80,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 8b66790c0a45..c6cb39637fdf 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index 9b032b5fde6e..bb0ccf2ac265 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -4,7 +4,7 @@ - + ee9 From 5b3ac2798f2bc22131599bec65658bb06a70fb22 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 29 Jan 2025 16:20:52 -0600 Subject: [PATCH 051/104] Replace OSGiApp with OSGiDeployableBundleMetadata --- .../jetty/osgi/AbstractContextProvider.java | 10 +- .../jetty/osgi/BundleContextProvider.java | 44 ++-- .../eclipse/jetty/osgi/BundleProvider.java | 4 +- .../jetty/osgi/BundleWebAppProvider.java | 38 ++- .../eclipse/jetty/osgi/ContextFactory.java | 2 +- ...java => OSGiDeployableBundleMetadata.java} | 217 ++++++------------ .../org/eclipse/jetty/osgi/OSGiDeployer.java | 4 +- .../eclipse/jetty/osgi/OSGiUndeployer.java | 4 +- .../jetty/ee10/osgi/boot/EE10Activator.java | 23 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 61 +++-- .../jetty/ee9/osgi/boot/EE9Activator.java | 64 +++--- 11 files changed, 199 insertions(+), 272 deletions(-) rename jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/{OSGiApp.java => OSGiDeployableBundleMetadata.java} (61%) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 42d7cfcc1505..e8161f16a550 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -56,15 +56,15 @@ public Attributes getAttributes() return _attributes; } - public ContextHandler createContextHandler(OSGiApp app) throws Exception + public ContextHandler createContextHandler(OSGiDeployableBundleMetadata metadata) throws Exception { - if (app == null) + if (metadata == null) return null; // Create a ContextHandler suitable to deploy in OSGi - ContextHandler contextHandler = _contextFactory.createContextHandler(this, app); - contextHandler.setID(app.getName()); - contextHandler.setAttribute(OSGiApp.BUNDLE, app.getBundle()); + ContextHandler contextHandler = _contextFactory.createContextHandler(this, metadata); + contextHandler.setID(metadata.getID()); + OSGiDeployableBundleMetadata.setBundle(contextHandler, metadata.getBundle()); return contextHandler; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index fe64efdd9450..34d4337bc058 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.ContextHandlerManagement; import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -45,9 +46,9 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu { private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); - private Map _appMap = new HashMap<>(); + private Map _contextHandlerMap = new HashMap<>(); - private Map> _bundleMap = new HashMap<>(); + private Map> _bundleMap = new HashMap<>(); private ServiceRegistration _serviceRegForBundles; @@ -182,16 +183,19 @@ public boolean bundleAdded(Bundle bundle) throws Exception String[] tmp = contextFiles.split("[,;]"); for (String contextFile : tmp) { - OSGiApp app = new OSGiApp(bundle); + OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); URI contextFilePath = Util.resolvePathAsLocalizedURI(contextFile, app.getBundle(), jettyHomePath); - - //set up the single context file for this deployment - app.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toString()); - app.setContextHandler(createContextHandler(app)); - _appMap.put(app.getPath(), app); - List apps = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); - apps.add(app); - getContextHandlerManagement().addContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.STARTED); + + if (contextFilePath != null) + { + // set up the single context file for this deployment + app.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); + } + ContextHandler contextHandler = createContextHandler(app); + _contextHandlerMap.put(contextHandler.getID(), contextHandler); + List contextHandlers = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); + contextHandlers.add(contextHandler); + getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); added = true; } @@ -205,19 +209,19 @@ public boolean bundleAdded(Bundle bundle) throws Exception * @return true if this was a context we had deployed, false otherwise */ @Override - public boolean bundleRemoved(Bundle bundle) throws Exception + public boolean bundleRemoved(Bundle bundle) { - List apps = _bundleMap.remove(bundle); + List contexts = _bundleMap.remove(bundle); + if (contexts == null || contexts.isEmpty()) + return false; + boolean removed = false; - if (apps != null) + for (ContextHandler context : contexts) { - for (OSGiApp app : apps) + if (_contextHandlerMap.remove(context.getID()) != null) { - if (_appMap.remove(app.getPath()) != null) - { - getContextHandlerManagement().removeContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.UNDEPLOYED); - removed = true; - } + getContextHandlerManagement().removeContextHandler(context, ContextHandlerLifeCycle.UNDEPLOYED); + removed = true; } } return removed; // true if even 1 context was removed associated with this bundle diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleProvider.java index 22ef681e19f1..f35b68368949 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleProvider.java @@ -22,7 +22,7 @@ */ public interface BundleProvider { - public boolean bundleAdded(Bundle bundle) throws Exception; + boolean bundleAdded(Bundle bundle) throws Exception; - public boolean bundleRemoved(Bundle bundle) throws Exception; + boolean bundleRemoved(Bundle bundle) throws Exception; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index d2dc8ed3062f..822ceefe213c 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.ContextHandlerManagement; import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; @@ -43,7 +44,7 @@ public class BundleWebAppProvider extends AbstractContextProvider implements Bun /** * Map of Bundle to App. Used when a Bundle contains a webapp. */ - private Map _bundleMap = new HashMap<>(); + private Map _bundleMap = new HashMap<>(); private ServiceRegistration _serviceRegForBundles; @@ -182,14 +183,11 @@ public boolean bundleAdded(Bundle bundle) throws Exception String staticResourcesLocation = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers); if (staticResourcesLocation != null) { - //TODO : we don't know whether an app is actually deployed, as deploymentManager swallows all - //exceptions inside the impl of addApp. Need to send the Event and also register as a service - //only if the deployment succeeded - OSGiApp app = new OSGiApp(bundle); + OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); app.setPathToResourceBase(staticResourcesLocation); - app.setContextHandler(createContextHandler(app)); - _bundleMap.put(bundle, app); - getContextHandlerManagement().addContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.STARTED); + ContextHandler contextHandler = createContextHandler(app); + _bundleMap.put(bundle, contextHandler); + getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); return true; } @@ -197,11 +195,11 @@ public boolean bundleAdded(Bundle bundle) throws Exception if (bundle.getEntry("/WEB-INF/web.xml") != null) { String base = "."; - OSGiApp app = new OSGiApp(bundle); - app.setContextHandler(createContextHandler(app)); + OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); + ContextHandler contextHandler = createContextHandler(app); app.setPathToResourceBase(base); - _bundleMap.put(bundle, app); - getContextHandlerManagement().addContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.STARTED); + _bundleMap.put(bundle, contextHandler); + getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); return true; } @@ -210,11 +208,11 @@ public boolean bundleAdded(Bundle bundle) throws Exception { //Could be a static webapp with no web.xml String base = "."; - OSGiApp app = new OSGiApp(bundle); + OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); app.setPathToResourceBase(base); - app.setContextHandler(createContextHandler(app)); - _bundleMap.put(bundle, app); - getContextHandlerManagement().addContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.STARTED); + ContextHandler contextHandler = createContextHandler(app); + _bundleMap.put(bundle, contextHandler); + getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); return true; } @@ -234,12 +232,12 @@ public boolean bundleAdded(Bundle bundle) throws Exception * @return true if this was a webapp we had deployed, false otherwise */ @Override - public boolean bundleRemoved(Bundle bundle) throws Exception + public boolean bundleRemoved(Bundle bundle) { - OSGiApp app = _bundleMap.remove(bundle); - if (app != null) + ContextHandler contextHandler = _bundleMap.remove(bundle); + if (contextHandler != null) { - getContextHandlerManagement().removeContextHandler(app.getContextHandler(), ContextHandlerLifeCycle.UNDEPLOYED); + getContextHandlerManagement().removeContextHandler(contextHandler, ContextHandlerLifeCycle.UNDEPLOYED); return true; } return false; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java index 1ec58e0197fb..896eb19e5203 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java @@ -17,5 +17,5 @@ public interface ContextFactory { - ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp app) throws Exception; + ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata osgiBundleDeployment) throws Exception; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java similarity index 61% rename from jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java rename to jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java index 734f2bf62082..973e0b3f8b3b 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiApp.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java @@ -18,13 +18,10 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Dictionary; -import java.util.Enumeration; import java.util.Hashtable; -import java.util.Objects; import java.util.Properties; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; -import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; @@ -35,199 +32,127 @@ import org.osgi.framework.Version; /** - * OSGiApp - * - * Base class representing info about a WebAppContext/ContextHandler to be deployed into jetty. + * Metadata useful for a deployment that will result in a {@link org.eclipse.jetty.server.handler.ContextHandler} */ -public class OSGiApp +public class OSGiDeployableBundleMetadata { - public static final String BUNDLE = OSGiApp.class.getPackageName() + ".bundle"; - public static final String REGISTRATION = OSGiApp.class.getPackageName() + ".registration"; + private static final String BUNDLE = OSGiDeployableBundleMetadata.class.getPackageName() + ".bundle"; + private static final String REGISTRATION = OSGiDeployableBundleMetadata.class.getPackageName() + ".registration"; - private static final String ENVIRONMENT = "environment"; + private final Bundle bundle; + private final Path bundlePath; + private final String contextPath; + private final Properties properties = new Properties(); + private String pathToResourceBase; - private final String _bundleName; - private final Path _bundlePath; - private final Properties _properties = new Properties(); - protected Bundle _bundle; - protected ServiceRegistration _registration; - protected ContextHandler _contextHandler; - protected String _pathToResourceBase; - protected String _contextPath; - protected Resource _bundleResource; - - /** - * Get the install location of a Bundle as a Path - * @param bundle the Bundle whose location to return - * @return the installed location of the Bundle as a Path - */ - private static Path getBundlePath(Bundle bundle) throws Exception + public OSGiDeployableBundleMetadata(Bundle bundle) throws Exception { - String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); - File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); - File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); - return Paths.get(root.toURI()); - + this.bundle = bundle; + this.bundlePath = getBundlePath(bundle); + this.contextPath = getContextPath(bundle); } /** - * Convert a bundle installed location into a Resource, taking account of + * Convert a bundle installed location into a Resource, taking account of * any locations that are actually packed jars, but without a ".jar" extension, eg * as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile - * @param bundle the bundle + * + * @param resourceFactory the ResourceFactory to create Resource from * @return a Resource representing the bundle's installed location */ - private static Resource getBundleAsResource(Bundle bundle) throws Exception + public Resource getBundleResource(ResourceFactory resourceFactory) throws Exception { String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); - //Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar + // Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()); - - return ResourceFactory.root().newResource(url); - } - - /** - * Get or create a contextPath from bundle headers and information - * - * @param bundle the bundle - * @return a contextPath - */ - private static String getContextPath(Bundle bundle) - { - Dictionary headers = bundle.getHeaders(); - String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); - if (contextPath == null) - { - // extract from the last token of the bundle's location: - // (really ?could consider processing the symbolic name as an alternative - // the location will often reflect the version. - // maybe this is relevant when the file is a war) - String location = bundle.getLocation(); - String[] toks = StringUtil.replace(location, '\\', '/').split("/"); - contextPath = toks[toks.length - 1]; - // remove .jar, .war etc: - int lastDot = contextPath.lastIndexOf('.'); - if (lastDot != -1) - contextPath = contextPath.substring(0, lastDot); - } - if (!contextPath.startsWith("/")) - contextPath = "/" + contextPath; - return contextPath; - } - - public OSGiApp(Bundle bundle) - throws Exception - { - _bundleName = bundle.getSymbolicName(); - _bundlePath = getBundlePath(bundle); - _bundle = Objects.requireNonNull(bundle); - _bundleResource = getBundleAsResource(bundle); - - // copy selected bundle headers into the properties - Dictionary headers = bundle.getHeaders(); - Enumeration keys = headers.keys(); - while (keys.hasMoreElements()) - { - String key = keys.nextElement(); - String val = headers.get(key); - if (ENVIRONMENT.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_ENVIRONMENT.equalsIgnoreCase(key)) - { - getProperties().put(ENVIRONMENT, val); - } - else if (Deployable.DEFAULTS_DESCRIPTOR.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH.equalsIgnoreCase(key)) - { - getProperties().put(Deployable.DEFAULTS_DESCRIPTOR, val); - } - else if (OSGiWebappConstants.JETTY_WEB_XML_PATH.equalsIgnoreCase(key)) - { - getProperties().put(key, val); - } - else if (OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH.equalsIgnoreCase(key)) - { - getProperties().put(key, val); - } - } - - //set up the context path based on the supplied value, or the calculated default - setContextPath(getContextPath(bundle)); + return resourceFactory.newResource(url); } - public String getName() + public String getID() { - return _bundleName; + return bundle.getSymbolicName(); } - public Path getPath() + public Bundle getBundle() { - return _bundlePath; + return bundle; } public Properties getProperties() { - return _properties; - } - - public Resource getBundleResource() - { - return _bundleResource; + return properties; } - public ContextHandler getContextHandler() + public Path getPath() { - return _contextHandler; + return bundlePath; } - public void setContextHandler(ContextHandler contextHandler) + public String getContextPath() { - _contextHandler = contextHandler; - _contextHandler.setID(_bundleName); - _contextHandler.setAttribute(BUNDLE, _bundle); - _contextHandler.setAttribute(REGISTRATION, _registration); + return contextPath; } public String getPathToResourceBase() { - return _pathToResourceBase; + return pathToResourceBase; } - public void setPathToResourceBase(String path) + public void setPathToResourceBase(String resourceBase) { - _pathToResourceBase = path; + this.pathToResourceBase = resourceBase; } - public String getContextPath() - { - return _contextPath; - } - - public void setContextPath(String contextPath) - { - _contextPath = contextPath; - } - - public String getBundleSymbolicName() + /** + * Get the install location of a Bundle as a Path + * + * @param bundle the Bundle whose location to return + * @return the installed location of the Bundle as a Path + */ + private static Path getBundlePath(Bundle bundle) throws Exception { - return _bundle.getSymbolicName(); + String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); + return Paths.get(root.toURI()); } - public String getBundleVersionAsString() + /** + * Get or create a contextPath from bundle headers and information + * + * @param bundle the bundle + * @return a contextPath + */ + private static String getContextPath(Bundle bundle) { - if (_bundle.getVersion() == null) - return null; - return _bundle.getVersion().toString(); - } + Dictionary headers = bundle.getHeaders(); + String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath == null) + { + // extract from the last token of the bundle's location: + // (really ?could consider processing the symbolic name as an alternative + // the location will often reflect the version. + // maybe this is relevant when the file is a war) + String location = bundle.getLocation(); + String[] toks = StringUtil.replace(location, '\\', '/').split("/"); + contextPath = toks[toks.length - 1]; + // remove .jar, .war etc: + int lastDot = contextPath.lastIndexOf('.'); + if (lastDot != -1) + contextPath = contextPath.substring(0, lastDot); + } + if (!contextPath.startsWith("/")) + contextPath = "/" + contextPath; - public Bundle getBundle() - { - return _bundle; + return contextPath; } - public ServiceRegistration getRegistration() + public static void setBundle(ContextHandler contextHandler, Bundle bundle) { - return _registration; + contextHandler.setAttribute(BUNDLE, bundle); } public static Bundle getBundle(ContextHandler contextHandler) @@ -276,7 +201,7 @@ public static void registerAsOSGiService(ContextHandler contextHandler) properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, contextHandler.getContextPath()); - serviceRegistration = FrameworkUtil.getBundle(OSGiApp.class).getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, properties); + serviceRegistration = FrameworkUtil.getBundle(OSGiDeployableBundleMetadata.class).getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, properties); contextHandler.setAttribute(REGISTRATION, serviceRegistration); } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java index bcd2c835698b..0a26a07fc9f4 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java @@ -42,7 +42,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte //TODO how to NOT send this event if its not a webapp: //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler) - Bundle bundle = (Bundle)contextHandler.getAttribute(OSGiApp.BUNDLE); + Bundle bundle = OSGiDeployableBundleMetadata.getBundle(contextHandler); if (bundle == null) { // Not an OSGI based ContextHandler @@ -55,7 +55,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte try { doProcessBinding(deploymentManager, node, contextHandler); - OSGiApp.registerAsOSGiService(contextHandler); + OSGiDeployableBundleMetadata.registerAsOSGiService(contextHandler); EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, bundle, contextPath); } catch (Exception e) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java index 45426c0cabf4..2e706f61d906 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java @@ -41,7 +41,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte { String contextPath = contextHandler.getContextPath(); - Bundle bundle = (Bundle)contextHandler.getAttribute(OSGiApp.BUNDLE); + Bundle bundle = OSGiDeployableBundleMetadata.getBundle(contextHandler); if (bundle != null) { // Only act on ContextHandler that is managed by OSGi @@ -58,7 +58,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte Thread.currentThread().setContextClassLoader(old); } EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, bundle, contextPath); - OSGiApp.deregisterAsOSGiService(contextHandler); + OSGiDeployableBundleMetadata.deregisterAsOSGiService(contextHandler); } } } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index f2106edf0af5..58b5d256d76b 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.osgi.BundleContextProvider; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiApp; +import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -265,16 +265,17 @@ public EE10ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(osgiApp.getBundleResource()); + contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -285,12 +286,12 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); //Use a classloader that knows about the common jetty parent loader, and also the bundle - OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, osgiApp.getBundle()); + OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, metadata.getBundle()); contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); - final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) { @@ -306,7 +307,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); @@ -327,7 +328,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //osgi Enterprise Spec r4 p.427 - contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); //make sure we protect also the osgi dirs specified by OSGi Enterprise spec String[] targets = contextHandler.getProtectedTargets(); @@ -358,13 +359,14 @@ public EE10WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata osgiApp) throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); + ResourceFactory resourceFactory = ResourceFactory.of(webApp); //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); @@ -521,10 +523,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); - Path bundlePath = osgiApp.getPath(); - Resource bundleResource = osgiApp.getBundleResource(); + Resource bundleResource = osgiApp.getBundleResource(resourceFactory); String pathToResourceBase = osgiApp.getPathToResourceBase(); diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index b57c4d1c56ad..613140d6f251 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.osgi.BundleContextProvider; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiApp; +import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -265,16 +265,17 @@ public EE11ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(osgiApp.getBundleResource()); + contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -285,12 +286,12 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); //Use a classloader that knows about the common jetty parent loader, and also the bundle - OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, osgiApp.getBundle()); + OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, metadata.getBundle()); contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); - final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) { @@ -306,7 +307,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); @@ -327,7 +328,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //osgi Enterprise Spec r4 p.427 - contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); //make sure we protect also the osgi dirs specified by OSGi Enterprise spec String[] targets = contextHandler.getProtectedTargets(); @@ -358,13 +359,14 @@ public EE11WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); + ResourceFactory resourceFactory = ResourceFactory.of(webApp); //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); @@ -401,20 +403,20 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG .toArray(Configuration[]::new)); //Make a webapp classloader - OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, osgiApp.getBundle()); + OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, metadata.getBundle()); //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)osgiApp.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); - List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, osgiApp.getBundle().getBundleContext()); + List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) webAppLoader.addClassPath(p.toUri().toString()); //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -428,19 +430,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //Set up some attributes // rfc66 - webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, osgiApp.getBundle().getBundleContext()); + webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, metadata.getBundle().getBundleContext()); // spring-dm-1.2.1 looks for the BundleContext as a different attribute. // not a spec... but if we want to support // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext // then we need to do this to: - webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), osgiApp.getBundle().getBundleContext()); + webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), metadata.getBundle().getBundleContext()); // also pass the bundle directly. sometimes a bundle does not have a // bundlecontext. // it is still useful to have access to the Bundle from the servlet // context. - webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, osgiApp.getBundle()); + webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, metadata.getBundle()); // apply any META-INF/context.xml file that is found to configure @@ -448,18 +450,18 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //First try looking for one in /META-INF URI tmpUri = null; - URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", osgiApp.getBundle()); + URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", metadata.getBundle()); if (contextXmlURL != null) tmpUri = contextXmlURL.toURI(); //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); - tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], osgiApp.getBundle(), jettyHomePath); + tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], metadata.getBundle(), jettyHomePath); } } @@ -479,7 +481,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithHiddenClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); @@ -500,10 +502,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //ensure the context path is set - webApp.setContextPath(osgiApp.getContextPath()); + webApp.setContextPath(metadata.getContextPath()); //osgi Enterprise Spec r4 p.427 - webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); //Indicate the webapp has been deployed, so that we don't try and redeploy again webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); @@ -521,12 +523,9 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); + Resource bundleResource = metadata.getBundleResource(resourceFactory); - Path bundlePath = osgiApp.getPath(); - - Resource bundleResource = osgiApp.getBundleResource(); - - String pathToResourceBase = osgiApp.getPathToResourceBase(); + String pathToResourceBase = metadata.getPathToResourceBase(); //if the path wasn't set or it was ., then it is the root of the bundle's installed location if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) @@ -556,19 +555,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //web.xml - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { - URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (webXml != null) webApp.setDescriptor(webXml.toString()); } // webdefault-ee11.xml - tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { - URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (defaultWebXml != null) { webApp.setDefaultsDescriptor(defaultWebXml.toString()); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 00f3ee9e802c..6b4445e53c5c 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.osgi.BundleContextProvider; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiApp; +import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -263,17 +263,17 @@ public EE9ContextFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp app) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) throws Exception { - OSGiApp osgiApp = OSGiApp.class.cast(app); String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); + ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(osgiApp.getBundleResource()); + contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -284,12 +284,12 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); //Use a classloader that knows about the common jetty parent loader, and also the bundle - OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, osgiApp.getBundle()); + OSGiClassLoader classLoader = new OSGiClassLoader(environmentLoader, metadata.getBundle()); contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); - final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) { @@ -305,7 +305,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(contextHandler); @@ -326,7 +326,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //osgi Enterprise Spec r4 p.427 - contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + contextHandler.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); //make sure we protect also the osgi dirs specified by OSGi Enterprise spec String[] targets = contextHandler.getProtectedTargets(); @@ -357,13 +357,14 @@ public EE9WebAppFactory(Bundle bundle) } @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiApp osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); + ResourceFactory resourceFactory = ResourceFactory.of(webApp); //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); @@ -396,23 +397,23 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } webApp.setConfigurations(Configurations.getKnown().stream() - .filter(c -> c.isEnabledByDefault()) + .filter(Configuration::isEnabledByDefault) .toArray(Configuration[]::new)); //Make a webapp classloader - OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, osgiApp.getBundle()); + OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, metadata.getBundle()); //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)osgiApp.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); - List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, osgiApp.getBundle().getBundleContext()); + String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) webAppLoader.addClassPath(p.toUri().toString()); //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -426,19 +427,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //Set up some attributes // rfc66 - webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, osgiApp.getBundle().getBundleContext()); + webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, metadata.getBundle().getBundleContext()); // spring-dm-1.2.1 looks for the BundleContext as a different attribute. // not a spec... but if we want to support // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext // then we need to do this to: - webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), osgiApp.getBundle().getBundleContext()); + webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), metadata.getBundle().getBundleContext()); // also pass the bundle directly. sometimes a bundle does not have a // bundlecontext. // it is still useful to have access to the Bundle from the servlet // context. - webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, osgiApp.getBundle()); + webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, metadata.getBundle()); // apply any META-INF/context.xml file that is found to configure @@ -446,18 +447,18 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //First try looking for one in /META-INF URI tmpUri = null; - URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", osgiApp.getBundle()); + URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", metadata.getBundle()); if (contextXmlURL != null) tmpUri = contextXmlURL.toURI(); //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); - tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], osgiApp.getBundle(), jettyHomePath); + tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], metadata.getBundle(), jettyHomePath); } } @@ -477,7 +478,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); @@ -498,10 +499,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //ensure the context path is set - webApp.setContextPath(osgiApp.getContextPath()); + webApp.setContextPath(metadata.getContextPath()); //osgi Enterprise Spec r4 p.427 - webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); + webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); //Indicate the webapp has been deployed, so that we don't try and redeploy again webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); @@ -519,12 +520,11 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); + Path bundlePath = metadata.getPath(); - Path bundlePath = osgiApp.getPath(); + Resource bundleResource = metadata.getBundleResource(resourceFactory); - Resource bundleResource = osgiApp.getBundleResource(); - - String pathToResourceBase = osgiApp.getPathToResourceBase(); + String pathToResourceBase = metadata.getPathToResourceBase(); //if the path wasn't set or it was ., then it is the root of the bundle's installed location if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) @@ -554,19 +554,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //web.xml - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { - URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (webXml != null) webApp.setDescriptor(webXml.toString()); } // webdefault-ee9.xml - tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { - URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (defaultWebXml != null) { webApp.setDefaultsDescriptor(defaultWebXml.toString()); From 3b8f36e3be0b6a60f2f23af233f20e280f228c7c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 29 Jan 2025 16:21:26 -0600 Subject: [PATCH 052/104] Core rearrange --- .../osgi/OSGiDeployableBundleMetadata.java | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java index 973e0b3f8b3b..cf39ed77cca9 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java @@ -52,58 +52,19 @@ public OSGiDeployableBundleMetadata(Bundle bundle) throws Exception this.contextPath = getContextPath(bundle); } - /** - * Convert a bundle installed location into a Resource, taking account of - * any locations that are actually packed jars, but without a ".jar" extension, eg - * as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile - * - * @param resourceFactory the ResourceFactory to create Resource from - * @return a Resource representing the bundle's installed location - */ - public Resource getBundleResource(ResourceFactory resourceFactory) throws Exception - { - String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); - File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); - File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); - // Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar - URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()); - - return resourceFactory.newResource(url); - } - - public String getID() - { - return bundle.getSymbolicName(); - } - - public Bundle getBundle() - { - return bundle; - } - - public Properties getProperties() - { - return properties; - } - - public Path getPath() - { - return bundlePath; - } - - public String getContextPath() + public static void deregisterAsOSGiService(ContextHandler contextHandler) { - return contextPath; - } + ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); + if (serviceRegistration == null) + return; - public String getPathToResourceBase() - { - return pathToResourceBase; + serviceRegistration.unregister(); + contextHandler.removeAttribute(REGISTRATION); } - public void setPathToResourceBase(String resourceBase) + public static Bundle getBundle(ContextHandler contextHandler) { - this.pathToResourceBase = resourceBase; + return (Bundle)contextHandler.getAttribute(BUNDLE); } /** @@ -120,6 +81,25 @@ private static Path getBundlePath(Bundle bundle) throws Exception return Paths.get(root.toURI()); } + public static String getBundleSymbolicName(ContextHandler contextHandler) + { + Bundle bundle = getBundle(contextHandler); + if (bundle == null) + return null; + return bundle.getSymbolicName(); + } + + public static String getBundleVersionAsString(ContextHandler contextHandler) + { + Bundle bundle = getBundle(contextHandler); + if (bundle == null) + return null; + Version version = bundle.getVersion(); + if (version == null) + return null; + return version.toString(); + } + /** * Get or create a contextPath from bundle headers and information * @@ -150,35 +130,6 @@ private static String getContextPath(Bundle bundle) return contextPath; } - public static void setBundle(ContextHandler contextHandler, Bundle bundle) - { - contextHandler.setAttribute(BUNDLE, bundle); - } - - public static Bundle getBundle(ContextHandler contextHandler) - { - return (Bundle)contextHandler.getAttribute(BUNDLE); - } - - public static String getBundleSymbolicName(ContextHandler contextHandler) - { - Bundle bundle = getBundle(contextHandler); - if (bundle == null) - return null; - return bundle.getSymbolicName(); - } - - public static String getBundleVersionAsString(ContextHandler contextHandler) - { - Bundle bundle = getBundle(contextHandler); - if (bundle == null) - return null; - Version version = bundle.getVersion(); - if (version == null) - return null; - return version.toString(); - } - /** * Register the Jetty deployed context/webapp as a service, as * according to the OSGi Web Application Specification. @@ -206,13 +157,62 @@ public static void registerAsOSGiService(ContextHandler contextHandler) } } - public static void deregisterAsOSGiService(ContextHandler contextHandler) + public static void setBundle(ContextHandler contextHandler, Bundle bundle) { - ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); - if (serviceRegistration == null) - return; + contextHandler.setAttribute(BUNDLE, bundle); + } - serviceRegistration.unregister(); - contextHandler.removeAttribute(REGISTRATION); + public Bundle getBundle() + { + return bundle; + } + + /** + * Convert a bundle installed location into a Resource, taking account of + * any locations that are actually packed jars, but without a ".jar" extension, eg + * as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile + * + * @param resourceFactory the ResourceFactory to create Resource from + * @return a Resource representing the bundle's installed location + */ + public Resource getBundleResource(ResourceFactory resourceFactory) throws Exception + { + String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); + // Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar + URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toURI().toURL()); + + return resourceFactory.newResource(url); + } + + public String getContextPath() + { + return contextPath; + } + + public String getID() + { + return bundle.getSymbolicName(); + } + + public Path getPath() + { + return bundlePath; + } + + public String getPathToResourceBase() + { + return pathToResourceBase; + } + + public void setPathToResourceBase(String resourceBase) + { + this.pathToResourceBase = resourceBase; + } + + public Properties getProperties() + { + return properties; } } From 39639dc5894ee22643912062e95e94470393c1f3 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 29 Jan 2025 16:35:28 -0600 Subject: [PATCH 053/104] Fixing some naming --- .../eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java | 4 ++-- ...ector.java => ContextHandlerLifeCyclePathCollector.java} | 2 +- .../jetty/deploy/DeploymentManagerLifeCycleRouteTest.java | 6 +++--- .../org/eclipse/jetty/deploy/DeploymentManagerTest.java | 4 ++-- .../scan/{DefaultAppTest.java => ScanTrackedAppTest.java} | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{AppLifeCyclePathCollector.java => ContextHandlerLifeCyclePathCollector.java} (95%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/{DefaultAppTest.java => ScanTrackedAppTest.java} (99%) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java index 0c2e9e5eb428..89c3c573992f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java @@ -74,8 +74,8 @@ public Collection getContexts() throws Exception return Collections.unmodifiableCollection(_manager.getContextHandlers()); } - public void requestAppGoal(String appId, String nodeName) + public void requestContextHandlerGoal(String id, String nodeName) { - _manager.requestContextHandlerGoal(appId, nodeName); + _manager.requestContextHandlerGoal(id, nodeName); } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java similarity index 95% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java index 26b02a991951..06a35479807a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCyclePathCollector.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java @@ -24,7 +24,7 @@ /** * Binds to all lifecycle nodes, and tracks the order of the lifecycle nodes for testing purposes. */ -public class AppLifeCyclePathCollector implements ContextHandlerLifeCycle.Binding +public class ContextHandlerLifeCyclePathCollector implements ContextHandlerLifeCycle.Binding { List actualOrder = new ArrayList(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index 5f0f243cee3c..e73d880b3a48 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -31,7 +31,7 @@ public void testStateTransitionNewToDeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); + ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); PhonyContextProvider provider = new PhonyContextProvider(depman); depman.addLifeCycleBinding(pathtracker); @@ -63,7 +63,7 @@ public void testStateTransitionReceive() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); + ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); PhonyContextProvider provider = new PhonyContextProvider(depman); depman.addLifeCycleBinding(pathtracker); @@ -86,7 +86,7 @@ public void testStateTransitionReceive() throws Exception public void testStateTransitionDeployedToUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); - AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); + ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); PhonyContextProvider mockProvider = new PhonyContextProvider(depman); // Setup JMX diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 05c8bc9470c1..290cc6721da5 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -40,7 +40,7 @@ public void testAddContext() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); + ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); PhonyContextProvider provider = new PhonyContextProvider(depman); depman.addLifeCycleBinding(pathtracker); @@ -74,7 +74,7 @@ public void testAddContext() throws Exception @Test public void testBinding() { - AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); + ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); DeploymentManager depman = new DeploymentManager(); depman.addLifeCycleBinding(pathtracker); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultAppTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java similarity index 99% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultAppTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java index 2993a6a43cb2..0199ad27f5c3 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultAppTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java @@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class DefaultAppTest +public class ScanTrackedAppTest { public WorkDir workDir; From 80dce8e1c4048e44e92b9aae3da5223895909557 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 29 Jan 2025 19:00:57 -0600 Subject: [PATCH 054/104] Rollback incorrect change to setJettyStandardIdsAndProperties --- .../java/org/eclipse/jetty/xml/XmlConfiguration.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index fe644c0bc9c7..7494f94621a0 100644 --- a/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-core/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -228,9 +228,9 @@ private static int calculateDepth(Class c) *
    * * @param server The Server object to set - * @param webappsDir The webapps Resource directory + * @param webapp The webapps Resource */ - public void setJettyStandardIdsAndProperties(Object server, Path webappsDir) + public void setJettyStandardIdsAndProperties(Object server, Path webapp) { try { @@ -245,11 +245,11 @@ public void setJettyStandardIdsAndProperties(Object server, Path webappsDir) getProperties().put("jetty.base", base.toString()); getProperties().put("jetty.base.uri", normalizeURI(base.toUri().toASCIIString())); - if (webappsDir != null && Files.isDirectory(webappsDir)) + if (webapp != null) { - getProperties().put("jetty.webapp", webappsDir.toString()); - getProperties().put("jetty.webapps", webappsDir.getParent().toString()); - getProperties().put("jetty.webapps.uri", normalizeURI(webappsDir.getParent().toUri().toString())); + getProperties().put("jetty.webapp", webapp.toString()); + getProperties().put("jetty.webapps", webapp.getParent().toString()); + getProperties().put("jetty.webapps.uri", normalizeURI(webapp.getParent().toUri().toString())); } } catch (Exception e) From b41cbd7b4141a3d75d33d0de31a256caca94eed9 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 30 Jan 2025 10:20:10 -0600 Subject: [PATCH 055/104] Fixing XML declarations --- .../src/main/config/etc/jetty-deploy.xml | 5 +++- .../resources/jetty-core-deploy-custom.xml | 30 ++++++++++--------- .../src/main/config/etc/jetty-core-deploy.xml | 16 +++++----- .../src/test/resources/RFC2616Base.xml | 4 +-- .../src/test/resources/deploy.xml | 4 +-- .../src/test/resources/RFC2616Base.xml | 4 +-- .../src/test/resources/deploy.xml | 4 +-- .../src/test/resources/RFC2616Base.xml | 4 +-- .../src/test/resources/deploy.xml | 4 +-- 9 files changed, 41 insertions(+), 34 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index e19057164720..73fb4f8a6f02 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -9,6 +9,9 @@ + + + @@ -35,7 +38,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml index f7f6e11ff45a..3379ce59aa5f 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -1,18 +1,20 @@ - - - core - - - core - - - - - - - + + + + core - + + core + + + + + + + + + + diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml index a18e862fca59..31526040bb12 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml @@ -3,11 +3,13 @@ - - - core - - - - + + + + core + + + + + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index d326d8a07c9d..b05412416dd9 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -94,9 +94,9 @@ - + - + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index 51413e5e9d7a..928b18c91a7b 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -12,9 +12,9 @@ - + - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 199209666938..3e53fe2da8b8 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -94,9 +94,9 @@ - + - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index f13f5490a7af..d794e829283c 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -12,9 +12,9 @@ - + - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 551984418106..1ce7cbffe51e 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -78,9 +78,9 @@ - + - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index c6cb39637fdf..c174f506cbd6 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -12,9 +12,9 @@ - + - + From 87c328380cbe960be82c4d00ab0c4f7019fbdbd7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 30 Jan 2025 10:20:19 -0600 Subject: [PATCH 056/104] Removing DEBUG --- .../jetty-deploy/src/test/resources/jetty-logging.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties b/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties index 142319a06adc..7b80290a89f1 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-logging.properties @@ -1,5 +1,5 @@ # Jetty Logging using jetty-slf4j-impl #org.eclipse.jetty.deploy.DeploymentTempDirTest.LEVEL=DEBUG -org.eclipse.jetty.deploy.LEVEL=DEBUG +#org.eclipse.jetty.deploy.LEVEL=DEBUG #org.eclipse.jetty.ee9.webapp.LEVEL=DEBUG #org.eclipse.jetty.util.Scanner=DEBUG From 205e372917744db5cedfcab03d0f8816567fd7ff Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 30 Jan 2025 10:21:00 -0600 Subject: [PATCH 057/104] Correcting addBean / JMX --- .../jetty/deploy/DeploymentManager.java | 288 +++++++++--------- .../deploy/jmx/DeploymentManagerMBean.java | 1 + .../jetty/deploy/scan/DefaultProvider.java | 7 +- .../DeploymentManagerLifeCycleRouteTest.java | 2 +- .../jetty/deploy/DeploymentManagerTest.java | 2 + .../scan/DefaultProviderCoreWebappTest.java | 2 +- .../jetty/deploy/test/XmlConfiguredJetty.java | 17 +- 7 files changed, 169 insertions(+), 150 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index e9353a5cdb13..16c0bd6aedbd 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -60,42 +60,10 @@ public class DeploymentManager extends ContainerLifeCycle implements ContextHandlerManagement { private static final Logger LOG = LoggerFactory.getLogger(DeploymentManager.class); - - /** - * A mutable record tracking a single context within the deployment manager. - */ - private static class TrackedContext - { - /** - * The context being tracked. - */ - private ContextHandler contextHandler; - - /** - * The lifecycle node location of this App - */ - private Node lifecyleNode; - - public ContextHandler getContextHandler() - { - return contextHandler; - } - - public Node getLifecyleNode() - { - return lifecyleNode; - } - - void setLifeCycleNode(Node node) - { - this.lifecyleNode = node; - } - } - private final AutoLock _lock = new AutoLock(); - private Throwable _onStartupErrors; private final ContextHandlerLifeCycle _lifecycle = new ContextHandlerLifeCycle(); private final Queue _tracked = new ConcurrentLinkedQueue<>(); + private Throwable _onStartupErrors; private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; @@ -122,81 +90,11 @@ public void addContextHandler(ContextHandler contextHandler, String nodeName) } } - public void setLifeCycleBindings(Collection bindings) - { - if (isRunning()) - throw new IllegalStateException(); - for (ContextHandlerLifeCycle.Binding b : _lifecycle.getBindings()) - { - _lifecycle.removeBinding(b); - } - for (ContextHandlerLifeCycle.Binding b : bindings) - { - _lifecycle.addBinding(b); - } - } - - public Collection getLifeCycleBindings() - { - return Collections.unmodifiableSet(_lifecycle.getBindings()); - } - public void addLifeCycleBinding(ContextHandlerLifeCycle.Binding binding) { _lifecycle.addBinding(binding); } - /** - * Convenience method to allow for insertion of nodes into the lifecycle. - * - * @param existingFromNodeName the existing node start - * @param existingToNodeName the existing node end - * @param insertedNodeName the new node to create between the existing nodes - */ - public void insertLifeCycleNode(String existingFromNodeName, String existingToNodeName, String insertedNodeName) - { - Node fromNode = _lifecycle.getNodeByName(existingFromNodeName); - Node toNode = _lifecycle.getNodeByName(existingToNodeName); - Edge edge = new Edge(fromNode, toNode); - _lifecycle.insertNode(edge, insertedNodeName); - } - - @Override - protected void doStart() throws Exception - { - if (getContexts() == null) - throw new IllegalStateException("No " + ContextHandlerCollection.class.getName() + " defined"); - - if (_useStandardBindings) - { - LOG.debug("DeploymentManager using standard bindings"); - addLifeCycleBinding(new StandardDeployer()); - addLifeCycleBinding(new StandardStarter()); - addLifeCycleBinding(new StandardStopper()); - addLifeCycleBinding(new StandardUndeployer()); - } - - try (AutoLock l = _lock.lock()) - { - ExceptionUtil.ifExceptionThrow(_onStartupErrors); - } - - super.doStart(); - } - - private TrackedContext findTrackedContext(String id) - { - if (id == null) - return null; - - for (TrackedContext entry : _tracked) - { - if (id.equals(entry.contextHandler.getID())) - return entry; - } - return null; - } - public ContextHandler findContextHandler(String id) { TrackedContext entry = findTrackedContext(id); @@ -242,11 +140,40 @@ public ContextHandlerCollection getContexts() return _contexts; } + public void setContexts(ContextHandlerCollection contexts) + { + this._contexts = contexts; + } + public ContextHandlerLifeCycle getLifeCycle() { return _lifecycle; } + public Collection getLifeCycleBindings() + { + return Collections.unmodifiableSet(_lifecycle.getBindings()); + } + + public void setLifeCycleBindings(Collection bindings) + { + if (isRunning()) + throw new IllegalStateException(); + for (ContextHandlerLifeCycle.Binding b : _lifecycle.getBindings()) + { + _lifecycle.removeBinding(b); + } + for (ContextHandlerLifeCycle.Binding b : bindings) + { + _lifecycle.addBinding(b); + } + } + + public Collection getNodes() + { + return _lifecycle.getNodes(); + } + public Server getServer() { if (_contexts == null) @@ -256,6 +183,31 @@ public Server getServer() return _contexts.getServer(); } + /** + * Convenience method to allow for insertion of nodes into the lifecycle. + * + * @param existingFromNodeName the existing node start + * @param existingToNodeName the existing node end + * @param insertedNodeName the new node to create between the existing nodes + */ + public void insertLifeCycleNode(String existingFromNodeName, String existingToNodeName, String insertedNodeName) + { + Node fromNode = _lifecycle.getNodeByName(existingFromNodeName); + Node toNode = _lifecycle.getNodeByName(existingToNodeName); + Edge edge = new Edge(fromNode, toNode); + _lifecycle.insertNode(edge, insertedNodeName); + } + + public boolean isUseStandardBindings() + { + return _useStandardBindings; + } + + public void setUseStandardBindings(boolean useStandardBindings) + { + this._useStandardBindings = useStandardBindings; + } + /** * Remove the ContextHandler from the DeploymentManager tracking. * @@ -287,7 +239,6 @@ public void removeContextHandler(ContextHandler contextHandler, String goalName) * @param nodeName the name of the node to attain */ @Override - @ManagedOperation(value = "request the context handler to be moved to the specified lifecycle node", impact = "ACTION") public void requestContextHandlerGoal(ContextHandler contextHandler, String nodeName) { TrackedContext tracked = findTrackedContext(contextHandler.getID()); @@ -299,6 +250,77 @@ public void requestContextHandlerGoal(ContextHandler contextHandler, String node requestContextHandlerGoal(tracked, nodeName); } + /** + * Move an ContextHandler.ID through the {@link ContextHandlerLifeCycle} to the desired {@link Node}, executing each lifecycle step + * in the process to reach the desired state. + * + * @param contextHandlerId the ID of ContextHandler to move through the process + * @param nodeName the name of the node to attain + */ + @ManagedOperation(value = "request the context handler to be moved to the specified lifecycle node", impact = "ACTION") + public void requestContextHandlerGoal(@Name("contextId") String contextHandlerId, @Name("nodeName") String nodeName) + { + TrackedContext tracked = findTrackedContext(contextHandlerId); + if (tracked == null) + { + throw new IllegalStateException("ContextHandler not being tracked by Deployment Manager: " + contextHandlerId); + } + requestContextHandlerGoal(tracked, nodeName); + } + + public void undeployAll() + { + LOG.debug("Undeploy All"); + for (TrackedContext entry : _tracked) + { + requestContextHandlerGoal(entry, ContextHandlerLifeCycle.UNDEPLOYED); + } + } + + @Override + protected void doStart() throws Exception + { + if (getContexts() == null) + throw new IllegalStateException("No " + ContextHandlerCollection.class.getName() + " defined"); + + if (_useStandardBindings) + { + LOG.debug("DeploymentManager using standard bindings"); + addLifeCycleBinding(new StandardDeployer()); + addLifeCycleBinding(new StandardStarter()); + addLifeCycleBinding(new StandardStopper()); + addLifeCycleBinding(new StandardUndeployer()); + } + + try (AutoLock l = _lock.lock()) + { + ExceptionUtil.ifExceptionThrow(_onStartupErrors); + } + + super.doStart(); + } + + private void addOnStartupError(Throwable cause) + { + try (AutoLock l = _lock.lock()) + { + _onStartupErrors = ExceptionUtil.combine(_onStartupErrors, cause); + } + } + + private TrackedContext findTrackedContext(String id) + { + if (id == null) + return null; + + for (TrackedContext entry : _tracked) + { + if (id.equals(entry.contextHandler.getID())) + return entry; + } + return null; + } + /** * Move a {@link TrackedContext} through the {@link ContextHandlerLifeCycle} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. @@ -342,7 +364,7 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) catch (Throwable t) { LOG.warn("Unable to reach node goal: {}", nodeName, t); - + // migrate to FAILED node Node failed = _lifecycle.getNodeByName(ContextHandlerLifeCycle.FAILED); tracked.setLifeCycleNode(failed); @@ -363,50 +385,34 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) } } - private void addOnStartupError(Throwable cause) + /** + * A mutable record tracking a single context within the deployment manager. + */ + private static class TrackedContext { - try (AutoLock l = _lock.lock()) + /** + * The context being tracked. + */ + private ContextHandler contextHandler; + + /** + * The lifecycle node location of this App + */ + private Node lifecyleNode; + + public ContextHandler getContextHandler() { - _onStartupErrors = ExceptionUtil.combine(_onStartupErrors, cause); + return contextHandler; } - } - public void requestContextHandlerGoal(@Name("contextId") String contextHandlerId, @Name("nodeName") String nodeName) - { - TrackedContext tracked = findTrackedContext(contextHandlerId); - if (tracked == null) + public Node getLifecyleNode() { - throw new IllegalStateException("ContextHandler not being tracked by Deployment Manager: " + contextHandlerId); + return lifecyleNode; } - requestContextHandlerGoal(tracked, nodeName); - } - - public void setContexts(ContextHandlerCollection contexts) - { - this._contexts = contexts; - } - public void undeployAll() - { - LOG.debug("Undeploy All"); - for (TrackedContext entry : _tracked) + void setLifeCycleNode(Node node) { - requestContextHandlerGoal(entry, ContextHandlerLifeCycle.UNDEPLOYED); + this.lifecyleNode = node; } } - - public boolean isUseStandardBindings() - { - return _useStandardBindings; - } - - public void setUseStandardBindings(boolean useStandardBindings) - { - this._useStandardBindings = useStandardBindings; - } - - public Collection getNodes() - { - return _lifecycle.getNodes(); - } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java index 89c3c573992f..1e4e39a97a2d 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java @@ -74,6 +74,7 @@ public Collection getContexts() throws Exception return Collections.unmodifiableCollection(_manager.getContextHandlers()); } + @ManagedOperation(value = "list ContextHandlers that are located at specified ContextHandlerLifeCycle nodes", impact = "ACTION") public void requestContextHandlerGoal(String id, String nodeName) { _manager.requestContextHandlerGoal(id, nodeName); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java index 2bcdd1f2cb72..f844b43e649f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java @@ -48,6 +48,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.component.LifeCycle; @@ -131,12 +132,13 @@ public class DefaultProvider extends ContainerLifeCycle implements Scanner.Chang private boolean deferInitialScan = false; private String defaultEnvironmentName; - public DefaultProvider(ContextHandlerManagement contextManagement) + public DefaultProvider(@Name("contextManagement") ContextHandlerManagement contextManagement) { this(contextManagement, null); } - public DefaultProvider(ContextHandlerManagement contextManagement, FilenameFilter filter) + public DefaultProvider(@Name("contextManagement") ContextHandlerManagement contextManagement, + @Name("filenameFilter") FilenameFilter filter) { this.contextManagement = Objects.requireNonNull(contextManagement); this.filenameFilter = Objects.requireNonNullElse(filter, new MonitoredPathFilter(monitoredDirs)); @@ -642,6 +644,7 @@ protected void performActions(List actions) // Create the Context Handler Server server = contextManagement.getServer(); ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, step.getApp(), deployAttributes); + contextHandler.setID(step.getName()); // force ID to what we need (XML could have set it to something else) step.getApp().setContextHandler(contextHandler); // Introduce the App to the DeploymentManager diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index e73d880b3a48..b80341827dc6 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -115,7 +115,7 @@ public void testStateTransitionDeployedToUndeployed() throws Exception ObjectName dmObjName = new ObjectName("org.eclipse.jetty.deploy:type=deploymentmanager,id=0"); String[] params = new String[]{"foo-webapp-1", "undeployed"}; String[] signature = new String[]{"java.lang.String", "java.lang.String"}; - mbsConnection.invoke(dmObjName, "requestAppGoal", params, signature); + mbsConnection.invoke(dmObjName, "requestContextHandlerGoal", params, signature); // Setup Expectations. List expected = new ArrayList<>(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 290cc6721da5..ab10ef26e335 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -97,6 +97,8 @@ public void testXmlConfigured(WorkDir workDir) throws Exception jetty = new XmlConfiguredJetty(testdir); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Should not throw an Exception diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java index 2094d14e9cd4..660f4548900a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java @@ -111,7 +111,7 @@ public void testExampleCoreDir() throws Exception defaultProvider.addMonitoredDirectory(webapps); DefaultProvider.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); - + deploymentManager.addBean(defaultProvider); startServer(deploymentManager); URI destURI = server.getURI().resolve("/demo/"); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java index 601eb57d63a1..07d1df2258b9 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/test/XmlConfiguredJetty.java @@ -98,11 +98,18 @@ public static Map configure(List xmls, Map Date: Thu, 30 Jan 2025 15:28:34 -0600 Subject: [PATCH 058/104] typo --- .../org/eclipse/jetty/ee10/osgi/boot/OSGiWebappClassLoader.java | 2 +- .../org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java | 2 +- .../org/eclipse/jetty/ee9/osgi/boot/OSGiWebappClassLoader.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappClassLoader.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappClassLoader.java index 9b6eefe8d7a4..89365ec1faa5 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappClassLoader.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/OSGiWebappClassLoader.java @@ -43,7 +43,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe * @param parent The parent classloader. * @param context The WebAppContext * @param contributor The bundle that defines this web-application. - * @throws IOException if unable to cerate the OSGiWebappClassLoader + * @throws IOException if unable to create the OSGiWebappClassLoader */ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) throws IOException diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java index 0665078accff..b331a6521bbd 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/OSGiWebappClassLoader.java @@ -43,7 +43,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe * @param parent The parent classloader. * @param context The WebAppContext * @param contributor The bundle that defines this web-application. - * @throws IOException if unable to cerate the OSGiWebappClassLoader + * @throws IOException if unable to create the OSGiWebappClassLoader */ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) throws IOException diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/OSGiWebappClassLoader.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/OSGiWebappClassLoader.java index c325d6cbc96d..ac39c1342a47 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/OSGiWebappClassLoader.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/OSGiWebappClassLoader.java @@ -43,7 +43,7 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe * @param parent The parent classloader. * @param context The WebAppContext * @param contributor The bundle that defines this web-application. - * @throws IOException if unable to cerate the OSGiWebappClassLoader + * @throws IOException if unable to create the OSGiWebappClassLoader */ public OSGiWebappClassLoader(ClassLoader parent, WebAppContext context, Bundle contributor) throws IOException From d14e5b5c098bf744f5256514e158f8897553767f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 30 Jan 2025 15:29:35 -0600 Subject: [PATCH 059/104] More jetty-osgi fixes --- .../jetty/osgi/AbstractContextProvider.java | 7 +- .../jetty/osgi/BundleContextProvider.java | 20 +- .../eclipse/jetty/osgi/BundleMetadata.java | 75 ++++ .../jetty/osgi/BundleWebAppProvider.java | 20 +- .../eclipse/jetty/osgi/ContextFactory.java | 2 +- .../osgi/OSGiDeployableBundleMetadata.java | 219 ----------- .../org/eclipse/jetty/osgi/OSGiDeployer.java | 6 +- .../eclipse/jetty/osgi/OSGiUndeployer.java | 7 +- .../org/eclipse/jetty/osgi/util/Util.java | 368 ++++++++++++------ .../jetty/ee10/osgi/boot/EE10Activator.java | 116 +++--- .../jsp/TLDServerClasspathContributor.java | 3 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 95 ++--- .../osgi/test/TestJettyOSGiBootWithJsp.java | 2 +- .../jetty/ee9/osgi/boot/EE9Activator.java | 84 ++-- 14 files changed, 512 insertions(+), 512 deletions(-) create mode 100644 jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java delete mode 100644 jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index e8161f16a550..c7c6e5d55e8c 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -16,6 +16,7 @@ import java.util.Objects; import org.eclipse.jetty.deploy.ContextHandlerManagement; +import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; @@ -56,15 +57,15 @@ public Attributes getAttributes() return _attributes; } - public ContextHandler createContextHandler(OSGiDeployableBundleMetadata metadata) throws Exception + public ContextHandler createContextHandler(BundleMetadata metadata) throws Exception { if (metadata == null) return null; // Create a ContextHandler suitable to deploy in OSGi ContextHandler contextHandler = _contextFactory.createContextHandler(this, metadata); - contextHandler.setID(metadata.getID()); - OSGiDeployableBundleMetadata.setBundle(contextHandler, metadata.getBundle()); + contextHandler.setID(metadata.getID()); // force ID to what this context handler source needs (as XML might have set it) + Util.setBundle(contextHandler, metadata.getBundle()); return contextHandler; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index 34d4337bc058..5df7bffd4040 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -44,22 +44,22 @@ */ public class BundleContextProvider extends AbstractContextProvider implements BundleProvider { - private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(BundleContextProvider.class); - private Map _contextHandlerMap = new HashMap<>(); + private final Map _contextHandlerMap = new HashMap<>(); - private Map> _bundleMap = new HashMap<>(); + private final Map> _bundleMap = new HashMap<>(); - private ServiceRegistration _serviceRegForBundles; + private ServiceRegistration _serviceRegForBundles; - private BundleTracker _tracker; + private BundleTracker _tracker; /** * ContextBundleTracker * * Track deployment of Bundles that should be deployed to Jetty as contexts. */ - public class ContextBundleTracker extends BundleTracker + public class ContextBundleTracker extends BundleTracker { protected String _serverName; @@ -183,15 +183,15 @@ public boolean bundleAdded(Bundle bundle) throws Exception String[] tmp = contextFiles.split("[,;]"); for (String contextFile : tmp) { - OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); - URI contextFilePath = Util.resolvePathAsLocalizedURI(contextFile, app.getBundle(), jettyHomePath); + BundleMetadata metadata = new BundleMetadata(bundle); + URI contextFilePath = Util.resolvePathAsLocalizedURI(contextFile, metadata.getBundle(), jettyHomePath); if (contextFilePath != null) { // set up the single context file for this deployment - app.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); + metadata.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); } - ContextHandler contextHandler = createContextHandler(app); + ContextHandler contextHandler = createContextHandler(metadata); _contextHandlerMap.put(contextHandler.getID(), contextHandler); List contextHandlers = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); contextHandlers.add(contextHandler); diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java new file mode 100644 index 000000000000..511b3d474cbc --- /dev/null +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java @@ -0,0 +1,75 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.osgi; + +import java.nio.file.Path; +import java.util.Properties; + +import org.eclipse.jetty.osgi.util.Util; +import org.osgi.framework.Bundle; + +/** + * Metadata useful for a deployment that will result in a {@link org.eclipse.jetty.server.handler.ContextHandler} + */ +public class BundleMetadata +{ + private final Bundle bundle; + private final Path bundlePath; + private final String contextPath; + private final Properties properties = new Properties(); + private final String pathToResourceBase; + + public BundleMetadata(Bundle bundle) throws Exception + { + this(bundle, null); + } + + public BundleMetadata(Bundle bundle, String pathToResourceBase) throws Exception + { + this.bundle = bundle; + this.bundlePath = Util.getBundlePath(bundle); + this.contextPath = Util.getContextPath(bundle); + this.pathToResourceBase = pathToResourceBase; + } + + public Bundle getBundle() + { + return bundle; + } + + public String getContextPath() + { + return contextPath; + } + + public String getID() + { + return bundle.getSymbolicName(); + } + + public Path getPath() + { + return bundlePath; + } + + public String getPathToResourceBase() + { + return pathToResourceBase; + } + + public Properties getProperties() + { + return properties; + } +} diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index 822ceefe213c..6901ad6541ea 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -39,18 +39,18 @@ */ public class BundleWebAppProvider extends AbstractContextProvider implements BundleProvider { - private static final Logger LOG = LoggerFactory.getLogger(AbstractContextProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(BundleWebAppProvider.class); /** * Map of Bundle to App. Used when a Bundle contains a webapp. */ private Map _bundleMap = new HashMap<>(); - private ServiceRegistration _serviceRegForBundles; + private ServiceRegistration _serviceRegForBundles; private WebAppTracker _webappTracker; - public class WebAppTracker extends BundleTracker + public class WebAppTracker extends BundleTracker { protected String _managedServerName; @@ -183,8 +183,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception String staticResourcesLocation = Util.getManifestHeaderValue(OSGiWebappConstants.JETTY_WAR_RESOURCE_PATH, headers); if (staticResourcesLocation != null) { - OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); - app.setPathToResourceBase(staticResourcesLocation); + BundleMetadata app = new BundleMetadata(bundle, staticResourcesLocation); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); @@ -195,9 +194,8 @@ public boolean bundleAdded(Bundle bundle) throws Exception if (bundle.getEntry("/WEB-INF/web.xml") != null) { String base = "."; - OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); + BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); - app.setPathToResourceBase(base); _bundleMap.put(bundle, contextHandler); getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); return true; @@ -208,8 +206,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception { //Could be a static webapp with no web.xml String base = "."; - OSGiDeployableBundleMetadata app = new OSGiDeployableBundleMetadata(bundle); - app.setPathToResourceBase(base); + BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); @@ -219,6 +216,11 @@ public boolean bundleAdded(Bundle bundle) throws Exception //not a webapp return false; } + catch (Throwable t) + { + t.printStackTrace(System.err); + throw t; + } finally { Thread.currentThread().setContextClassLoader(cl); diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java index 896eb19e5203..5fdb2b57b3d0 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/ContextFactory.java @@ -17,5 +17,5 @@ public interface ContextFactory { - ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata osgiBundleDeployment) throws Exception; + ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata osgiBundleDeployment) throws Exception; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java deleted file mode 100644 index d4f01552211a..000000000000 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployableBundleMetadata.java +++ /dev/null @@ -1,219 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.osgi; - -import java.io.File; -import java.net.URL; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Dictionary; -import java.util.Hashtable; -import java.util.Properties; - -import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.URIUtil; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceFactory; -import org.osgi.framework.Bundle; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceRegistration; -import org.osgi.framework.Version; - -/** - * Metadata useful for a deployment that will result in a {@link org.eclipse.jetty.server.handler.ContextHandler} - */ -public class OSGiDeployableBundleMetadata -{ - private static final String BUNDLE = OSGiDeployableBundleMetadata.class.getPackageName() + ".bundle"; - private static final String REGISTRATION = OSGiDeployableBundleMetadata.class.getPackageName() + ".registration"; - - private final Bundle bundle; - private final Path bundlePath; - private final String contextPath; - private final Properties properties = new Properties(); - private String pathToResourceBase; - - public OSGiDeployableBundleMetadata(Bundle bundle) throws Exception - { - this.bundle = bundle; - this.bundlePath = getBundlePath(bundle); - this.contextPath = getContextPath(bundle); - } - - public static void deregisterAsOSGiService(ContextHandler contextHandler) - { - ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); - if (serviceRegistration == null) - return; - - serviceRegistration.unregister(); - contextHandler.removeAttribute(REGISTRATION); - } - - public static Bundle getBundle(ContextHandler contextHandler) - { - return (Bundle)contextHandler.getAttribute(BUNDLE); - } - - /** - * Get the install location of a Bundle as a Path - * - * @param bundle the Bundle whose location to return - * @return the installed location of the Bundle as a Path - */ - private static Path getBundlePath(Bundle bundle) throws Exception - { - String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); - File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); - File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); - return Paths.get(root.toURI()); - } - - public static String getBundleSymbolicName(ContextHandler contextHandler) - { - Bundle bundle = getBundle(contextHandler); - if (bundle == null) - return null; - return bundle.getSymbolicName(); - } - - public static String getBundleVersionAsString(ContextHandler contextHandler) - { - Bundle bundle = getBundle(contextHandler); - if (bundle == null) - return null; - Version version = bundle.getVersion(); - if (version == null) - return null; - return version.toString(); - } - - /** - * Get or create a contextPath from bundle headers and information - * - * @param bundle the bundle - * @return a contextPath - */ - private static String getContextPath(Bundle bundle) - { - Dictionary headers = bundle.getHeaders(); - String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); - if (contextPath == null) - { - // extract from the last token of the bundle's location: - // (really ?could consider processing the symbolic name as an alternative - // the location will often reflect the version. - // maybe this is relevant when the file is a war) - String location = bundle.getLocation(); - String[] toks = StringUtil.replace(location, '\\', '/').split("/"); - contextPath = toks[toks.length - 1]; - // remove .jar, .war etc: - int lastDot = contextPath.lastIndexOf('.'); - if (lastDot != -1) - contextPath = contextPath.substring(0, lastDot); - } - if (!contextPath.startsWith("/")) - contextPath = "/" + contextPath; - - return contextPath; - } - - /** - * Register the Jetty deployed context/webapp as a service, as - * according to the OSGi Web Application Specification. - */ - public static void registerAsOSGiService(ContextHandler contextHandler) - { - ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); - if (serviceRegistration == null) - { - Dictionary properties = new Hashtable<>(); - properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); - - String bundleSymbolicName = getBundleSymbolicName(contextHandler); - if (StringUtil.isNotBlank(bundleSymbolicName)) - properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, bundleSymbolicName); - - String bundleVersion = getBundleVersionAsString(contextHandler); - if (StringUtil.isNotBlank(bundleVersion)) - properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, bundleVersion); - - properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, contextHandler.getContextPath()); - - serviceRegistration = FrameworkUtil.getBundle(OSGiDeployableBundleMetadata.class).getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, properties); - contextHandler.setAttribute(REGISTRATION, serviceRegistration); - } - } - - public static void setBundle(ContextHandler contextHandler, Bundle bundle) - { - contextHandler.setAttribute(BUNDLE, bundle); - } - - public Bundle getBundle() - { - return bundle; - } - - /** - * Convert a bundle installed location into a Resource, taking account of - * any locations that are actually packed jars, but without a ".jar" extension, eg - * as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile - * - * @param resourceFactory the ResourceFactory to create Resource from - * @return a Resource representing the bundle's installed location - */ - public Resource getBundleResource(ResourceFactory resourceFactory) throws Exception - { - String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); - File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); - File root = (bundleOverrideLocation == null ? bundleLocation : new File(bundleOverrideLocation)); - // Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar - URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(URIUtil.correctURI(root.toURI()).toURL()); - - return resourceFactory.newResource(url); - } - - public String getContextPath() - { - return contextPath; - } - - public String getID() - { - return bundle.getSymbolicName(); - } - - public Path getPath() - { - return bundlePath; - } - - public String getPathToResourceBase() - { - return pathToResourceBase; - } - - public void setPathToResourceBase(String resourceBase) - { - this.pathToResourceBase = resourceBase; - } - - public Properties getProperties() - { - return properties; - } -} diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java index 0a26a07fc9f4..3f201eb53a9e 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java @@ -17,6 +17,7 @@ import org.eclipse.jetty.deploy.bindings.StandardDeployer; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; +import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.osgi.framework.Bundle; @@ -42,7 +43,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte //TODO how to NOT send this event if its not a webapp: //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler) - Bundle bundle = OSGiDeployableBundleMetadata.getBundle(contextHandler); + Bundle bundle = Util.getBundle(contextHandler); if (bundle == null) { // Not an OSGI based ContextHandler @@ -50,12 +51,13 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte } else { + // This is a ContextHandler that is managed by jetty-osgi. String contextPath = contextHandler.getContextPath(); EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, bundle, contextPath); try { doProcessBinding(deploymentManager, node, contextHandler); - OSGiDeployableBundleMetadata.registerAsOSGiService(contextHandler); + Util.registerAsOSGiService(contextHandler); EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, bundle, contextPath); } catch (Exception e) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java index 2e706f61d906..77134ae8ba84 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java @@ -17,6 +17,7 @@ import org.eclipse.jetty.deploy.bindings.StandardUndeployer; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; +import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.osgi.framework.Bundle; @@ -41,10 +42,10 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte { String contextPath = contextHandler.getContextPath(); - Bundle bundle = OSGiDeployableBundleMetadata.getBundle(contextHandler); + Bundle bundle = Util.getBundle(contextHandler); if (bundle != null) { - // Only act on ContextHandler that is managed by OSGi + // This is a ContextHandler that is managed by jetty-osgi. EventSender.getInstance().send(EventSender.UNDEPLOYING_EVENT, bundle, contextPath); ClassLoader old = Thread.currentThread().getContextClassLoader(); ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); @@ -58,7 +59,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte Thread.currentThread().setContextClassLoader(old); } EventSender.getInstance().send(EventSender.UNDEPLOYED_EVENT, bundle, contextPath); - OSGiDeployableBundleMetadata.deregisterAsOSGiService(contextHandler); + Util.deregisterAsOSGiService(contextHandler); } } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/util/Util.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/util/Util.java index b97afbddfd2e..315f31af8fe4 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/util/Util.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/util/Util.java @@ -23,18 +23,27 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; +import java.util.Hashtable; import java.util.List; import java.util.Objects; import java.util.StringTokenizer; +import org.eclipse.jetty.osgi.BundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; +import org.eclipse.jetty.osgi.OSGiWebappConstants; +import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.Version; import org.osgi.service.packageadmin.PackageAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,89 +53,208 @@ */ public class Util { - private static final Logger LOG = LoggerFactory.getLogger(Util.class); + private static final Logger LOG = LoggerFactory.getLogger(Util.class); + private static final String BUNDLE = BundleMetadata.class.getPackageName() + ".bundle"; + private static final String REGISTRATION = BundleMetadata.class.getPackageName() + ".registration"; /** - * Resolve a path either absolutely or against the bundle install location, or - * against jetty home. - * - * @param path the path to resolve - * @param bundle the bundle - * @param jettyHome the path to jetty home - * @return the URI resolved either absolutely or against the bundle install location or jetty home. + * Create an osgi filter for the given classname and server name. + * + * @param bundleContext the {@link BundleContext} instance to use + * @param classname the class to match on the filter + * @param managedServerName the name of the jetty server instance + * @return a new filter + * @throws InvalidSyntaxException If the filter contains an invalid string that cannot be parsed. */ - public static URI resolvePathAsLocalizedURI(String path, Bundle bundle, Path jettyHome) - throws Exception + public static Filter createFilter(BundleContext bundleContext, String classname, String managedServerName) throws InvalidSyntaxException { - if (StringUtil.isBlank(path)) - return null; - - if (path.startsWith("file:/")) - return URIUtil.correctURI(new URI(path)); - - if (path.startsWith("/") && File.separatorChar != '/') - return new URI("file:" + path); - - try + if (StringUtil.isBlank(managedServerName) || managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) { - Path p = FileSystems.getDefault().getPath(path); - if (p.isAbsolute()) - return p.toUri(); + return bundleContext.createFilter("(&(objectclass=" + classname + ")(|(managedServerName=" + managedServerName + ")(!(managedServerName=*))))"); } - catch (InvalidPathException x) + else { - //ignore and try via the jetty bundle instead - LOG.trace("IGNORED", x); + return bundleContext.createFilter("(&(objectclass=" + classname + ")(managedServerName=" + managedServerName + "))"); } + } - - //relative location - //try inside the bundle first - if (bundle != null) + public static void deregisterAsOSGiService(ContextHandler contextHandler) + { + ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); + if (serviceRegistration == null) + return; + + serviceRegistration.unregister(); + contextHandler.removeAttribute(REGISTRATION); + } + + /** + * Treating the string as a separated list of filenames, + * convert and return the list of urls. + * + * @param val the separated list of filenames + * @param delims the separators (default is ,;) + * @return the list of URLs found in the input list + * @throws Exception if unable to convert entry to a URL + */ + public static List fileNamesAsURLs(String val, String delims) + throws Exception + { + String separators = StringUtil.DEFAULT_DELIMS; + if (delims == null) + delims = separators; + + StringTokenizer tokenizer = new StringTokenizer(val, delims, false); + List urls = new ArrayList<>(); + while (tokenizer.hasMoreTokens()) { - URL url = bundle.getEntry(path); - if (url != null) - { - return BundleFileLocatorHelper.DEFAULT.getLocalURL(url).toURI(); - } + urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tokenizer.nextToken()))); } + return urls; + } - //try resolving against jetty.home - if (jettyHome != null) + public static Bundle getBundle(ContextHandler contextHandler) + { + return (Bundle)contextHandler.getAttribute(BUNDLE); + } + + /** + * Get the install location of a Bundle as a Path + * + * @param bundle the Bundle whose location to return + * @return the installed location of the Bundle as a Path + */ + public static Path getBundlePath(Bundle bundle) throws Exception + { + String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + return (bundleOverrideLocation == null ? bundleLocation.toPath() : Path.of(bundleOverrideLocation)); + } + + /** + * Convert a bundle installed location into a Resource, taking account of + * any locations that are actually packed jars, but without a ".jar" extension, eg + * as found on equinox. Eg file:///a/b/c/org.eclipse.osgi/89/0/bundleFile + * + * @param bundle the Bundle to convert + * @param resourceFactory the ResourceFactory to create the new Resource in + * @return a new Resource representing the bundle's installed location + */ + public static Resource newBundleResource(Bundle bundle, ResourceFactory resourceFactory) throws Exception + { + String bundleOverrideLocation = bundle.getHeaders().get(OSGiWebappConstants.JETTY_BUNDLE_INSTALL_LOCATION_OVERRIDE); + File bundleLocation = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bundle); + Path root = (bundleOverrideLocation == null ? bundleLocation.toPath() : Path.of(bundleOverrideLocation)); + // Fix some osgiPaths.get( locations which point to an archive, but that doesn't end in .jar + URL url = BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(root.toUri().toURL()); + return resourceFactory.newResource(url); + } + + public static String getBundleSymbolicName(ContextHandler contextHandler) + { + Bundle bundle = getBundle(contextHandler); + if (bundle == null) + return null; + return bundle.getSymbolicName(); + } + + public static String getBundleVersionAsString(ContextHandler contextHandler) + { + Bundle bundle = getBundle(contextHandler); + if (bundle == null) + return null; + Version version = bundle.getVersion(); + if (version == null) + return null; + return version.toString(); + } + + /** + * Get or create a contextPath from bundle headers and information + * + * @param bundle the bundle + * @return a contextPath + */ + public static String getContextPath(Bundle bundle) + { + Dictionary headers = bundle.getHeaders(); + String contextPath = (String)headers.get(OSGiWebappConstants.RFC66_WEB_CONTEXTPATH); + if (contextPath == null) { - Path p = jettyHome.resolve(path); - if (Files.exists(p)) - return p.toUri(); + // extract from the last token of the bundle's location: + // (really ?could consider processing the symbolic name as an alternative + // the location will often reflect the version. + // maybe this is relevant when the file is a war) + String location = bundle.getLocation(); + String[] toks = StringUtil.replace(location, '\\', '/').split("/"); + contextPath = toks[toks.length - 1]; + // remove .jar, .war etc: + int lastDot = contextPath.lastIndexOf('.'); + if (lastDot != -1) + contextPath = contextPath.substring(0, lastDot); } + if (!contextPath.startsWith("/")) + contextPath = "/" + contextPath; - return null; + return contextPath; } - + public static URL getLocalURL(URL url) - throws Exception + throws Exception { if (url == null) return null; - + return BundleFileLocatorHelper.DEFAULT.getLocalURL(url); } - + public static URL getLocalizedEntry(String file, Bundle bundle) - throws Exception + throws Exception { if (file == null || bundle == null) return null; - + URL url = bundle.getEntry(file); if (url == null) return null; - + return BundleFileLocatorHelper.DEFAULT.getLocalURL(url); } - + + /** + * Get the value of a manifest header. + * + * @param name the name of the header + * @param altName an alternative name for the header (useful for deprecated names) + * @param manifest the dictionary + * @return the value from the manifest + */ + public static String getManifestHeaderValue(String name, String altName, Dictionary manifest) + { + if (manifest == null) + return null; + if (name == null && altName == null) + return null; + if (name != null) + return (String)manifest.get(name); + return (String)manifest.get(altName); + } + + /** + * Get the value of a manifest header. + * + * @param name the name of the header + * @param manifest the dictionary + * @return the value from the manifest + */ + public static String getManifestHeaderValue(String name, Dictionary manifest) + { + return getManifestHeaderValue(name, null, manifest); + } + /** * Resolve the file system paths to bundles identified by their symbolic names. - * + * * @param bundleSymbolicNames comma separated list of symbolic bundle names * @param bundleContext the bundle on whose behalf to resolve * @return List of resolved Paths matching the bundle symbolic names @@ -159,93 +287,90 @@ public static List getPathsToBundlesBySymbolicNames(String bundleSymbolicN File f = BundleFileLocatorHelperFactory.getFactory().getHelper().getBundleInstallLocation(bs[0]); paths.add(f.toPath()); } - + return paths; } - + /** - * Create an osgi filter for the given classname and server name. - * - * @param bundleContext the {@link BundleContext} instance to use - * @param classname the class to match on the filter - * @param managedServerName the name of the jetty server instance - * @return a new filter - * @throws InvalidSyntaxException If the filter contains an invalid string that cannot be parsed. + * Register the Jetty deployed context/webapp as a service, as + * according to the OSGi Web Application Specification. */ - public static Filter createFilter(BundleContext bundleContext, String classname, String managedServerName) throws InvalidSyntaxException + public static void registerAsOSGiService(ContextHandler contextHandler) { - if (StringUtil.isBlank(managedServerName) || managedServerName.equals(OSGiServerConstants.MANAGED_JETTY_SERVER_DEFAULT_NAME)) - { - return bundleContext.createFilter("(&(objectclass=" + classname + ")(|(managedServerName=" + managedServerName + ")(!(managedServerName=*))))"); - } - else + ServiceRegistration serviceRegistration = (ServiceRegistration)contextHandler.getAttribute(REGISTRATION); + if (serviceRegistration == null) { - return bundleContext.createFilter("(&(objectclass=" + classname + ")(managedServerName=" + managedServerName + "))"); + Dictionary properties = new Hashtable<>(); + properties.put(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); + + String bundleSymbolicName = getBundleSymbolicName(contextHandler); + if (StringUtil.isNotBlank(bundleSymbolicName)) + properties.put(OSGiWebappConstants.OSGI_WEB_SYMBOLICNAME, bundleSymbolicName); + + String bundleVersion = getBundleVersionAsString(contextHandler); + if (StringUtil.isNotBlank(bundleVersion)) + properties.put(OSGiWebappConstants.OSGI_WEB_VERSION, bundleVersion); + + properties.put(OSGiWebappConstants.OSGI_WEB_CONTEXTPATH, contextHandler.getContextPath()); + + serviceRegistration = FrameworkUtil.getBundle(BundleMetadata.class).getBundleContext().registerService(ContextHandler.class.getName(), contextHandler, properties); + contextHandler.setAttribute(REGISTRATION, serviceRegistration); } } /** - * Get the value of a manifest header. + * Resolve a path either absolutely or against the bundle install location, or + * against jetty home. * - * @param name the name of the header - * @param altName an alternative name for the header (useful for deprecated names) - * @param manifest the dictionary - * @return the value from the manifest + * @param path the path to resolve + * @param bundle the bundle + * @param jettyHome the path to jetty home + * @return the URI resolved either absolutely or against the bundle install location or jetty home. */ - public static String getManifestHeaderValue(String name, String altName, Dictionary manifest) + public static URI resolvePathAsLocalizedURI(String path, Bundle bundle, Path jettyHome) + throws Exception { - if (manifest == null) - return null; - if (name == null && altName == null) + if (StringUtil.isBlank(path)) return null; - if (name != null) - return (String)manifest.get(name); - return (String)manifest.get(altName); - } - /** - * Get the value of a manifest header. - * - * @param name the name of the header - * @param manifest the dictionary - * @return the value from the manifest - */ - public static String getManifestHeaderValue(String name, Dictionary manifest) - { - return getManifestHeaderValue(name, null, manifest); - } + if (path.startsWith("file:/")) + return URIUtil.correctURI(new URI(path)); - /** - * Treating the string as a separated list of filenames, - * convert and return the list of urls. - * - * @param val the separated list of filenames - * @param delims the separators (default is ,;) - * @return the list of URLs found in the input list - * @throws Exception if unable to convert entry to a URL - */ - public static List fileNamesAsURLs(String val, String delims) - throws Exception - { - String separators = StringUtil.DEFAULT_DELIMS; - if (delims == null) - delims = separators; + if (path.startsWith("/") && File.separatorChar != '/') + return new URI("file:" + path); - StringTokenizer tokenizer = new StringTokenizer(val, delims, false); - List urls = new ArrayList<>(); - while (tokenizer.hasMoreTokens()) + try { - urls.add(BundleFileLocatorHelperFactory.getFactory().getHelper().getLocalURL(new URL(tokenizer.nextToken()))); + Path p = FileSystems.getDefault().getPath(path); + if (p.isAbsolute()) + return p.toUri(); + } + catch (InvalidPathException x) + { + //ignore and try via the jetty bundle instead + LOG.trace("IGNORED", x); } - return urls; - } - public static void setProperty(Dictionary properties, String key, Object value) - { - if (value != null) + //relative location + //try inside the bundle first + if (bundle != null) { - properties.put(key, value); + URL url = bundle.getEntry(path); + if (url != null) + { + return BundleFileLocatorHelper.DEFAULT.getLocalURL(url).toURI(); + } + } + + //try resolving against jetty.home + if (jettyHome != null) + { + Path p = jettyHome.resolve(path); + if (Files.exists(p)) + return p.toUri(); } + + return null; } /** @@ -296,4 +421,17 @@ public static String resolvePropertyValue(String value) return value.substring(0, ind) + defaultValue + reminder; } } + + public static void setBundle(ContextHandler contextHandler, Bundle bundle) + { + contextHandler.setAttribute(BUNDLE, bundle); + } + + public static void setProperty(Dictionary properties, String key, Object value) + { + if (value != null) + { + properties.put(key, value); + } + } } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index 58b5d256d76b..a8f37b6e65eb 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -32,9 +32,9 @@ import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.osgi.AbstractContextProvider; import org.eclipse.jetty.osgi.BundleContextProvider; +import org.eclipse.jetty.osgi.BundleMetadata; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -67,9 +67,9 @@ public class EE10Activator implements BundleActivator { private static final Logger LOG = LoggerFactory.getLogger(EE10Activator.class); - + public static final String ENVIRONMENT = "ee10"; - + private static Collection __serverClasspathContributors = new ArrayList<>(); public static void registerServerClasspathContributor(ServerClasspathContributor contributor) @@ -89,20 +89,19 @@ public static Collection getServerClasspathContribut /** * ServerTracker - * - * Tracks appearance of Server instances as OSGi services, and then configures them - * for deployment of EE10 contexts and webapps. * + * Tracks appearance of Server instances as OSGi services, and then configures them + * for deployment of EE10 contexts and webapps. */ public static class ServerTracker implements ServiceTrackerCustomizer { private Bundle _myBundle = null; - + public ServerTracker(Bundle bundle) { _myBundle = bundle; } - + @Override public Object addingService(ServiceReference sr) { @@ -126,7 +125,7 @@ public Object addingService(ServiceReference sr) if (LOG.isDebugEnabled()) LOG.debug("Server classloader for contexts = {}", server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER)); - } + } server.setAttribute(OSGiServerConstants.SERVER_CLASSPATH_BUNDLES, contributedBundles); } @@ -212,7 +211,7 @@ private Optional getDeploymentManager(Server server) Collection deployers = server.getBeans(DeploymentManager.class); return deployers.stream().findFirst(); } - + private List convertBundleToURL(Bundle bundle) { List urls = new ArrayList<>(); @@ -254,19 +253,19 @@ else if (f.isDirectory() && f.getName().equals("lib")) return urls; } } - + public static class EE10ContextFactory implements ContextFactory { private Bundle _myBundle; - + public EE10ContextFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) - throws Exception + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) + throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); @@ -275,13 +274,13 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); - + contextHandler.setBaseResource(Util.newBundleResource(metadata.getBundle(), resourceFactory)); + // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); - + //provide access to all ee10 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); @@ -344,7 +343,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); contextHandler.setProtectedTargets(updatedTargets); - + return contextHandler; } } @@ -352,14 +351,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG public static class EE10WebAppFactory implements ContextFactory { private Bundle _myBundle; - + public EE10WebAppFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata osgiApp) + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); @@ -370,19 +369,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); - + // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); - + //provide access to all ee10 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); if (LOG.isDebugEnabled()) LOG.debug("Environment classloader = {}", environmentLoader); - + //Ensure Configurations.getKnown is called with a classloader that can see all of the ee10 and core classes - + ClassLoader old = Thread.currentThread().getContextClassLoader(); try { @@ -397,71 +396,72 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG { Thread.currentThread().setContextClassLoader(old); } - + webApp.setConfigurations(Configurations.getKnown().stream() .filter(c -> c.isEnabledByDefault()) .toArray(Configuration[]::new)); //Make a webapp classloader - OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, osgiApp.getBundle()); + OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, metadata.getBundle()); //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)osgiApp.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); - List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, osgiApp.getBundle().getBundleContext()); + List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) + { webAppLoader.addClassPath(p.toUri().toString()); - + } + //Set up configuration from manifest headers //extra classpath - String extraClasspath = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); webApp.setClassLoader(webAppLoader); - + //Take care of extra provider properties webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); - + //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); //Set up some attributes // rfc66 - webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, osgiApp.getBundle().getBundleContext()); + webApp.setAttribute(OSGiWebappConstants.RFC66_OSGI_BUNDLE_CONTEXT, metadata.getBundle().getBundleContext()); // spring-dm-1.2.1 looks for the BundleContext as a different attribute. // not a spec... but if we want to support // org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext // then we need to do this to: - webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), osgiApp.getBundle().getBundleContext()); + webApp.setAttribute("org.springframework.osgi.web." + BundleContext.class.getName(), metadata.getBundle().getBundleContext()); // also pass the bundle directly. sometimes a bundle does not have a // bundlecontext. // it is still useful to have access to the Bundle from the servlet // context. - webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, osgiApp.getBundle()); - + webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, metadata.getBundle()); // apply any META-INF/context.xml file that is found to configure // the webapp first //First try looking for one in /META-INF URI tmpUri = null; - URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", osgiApp.getBundle()); + URL contextXmlURL = Util.getLocalizedEntry("/META-INF/jetty-webapp-context.xml", metadata.getBundle()); if (contextXmlURL != null) tmpUri = contextXmlURL.toURI(); //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); - tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], osgiApp.getBundle(), jettyHomePath); + tmpUri = Util.resolvePathAsLocalizedURI(filenames[0], metadata.getBundle(), jettyHomePath); } } @@ -481,7 +481,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG WebAppClassLoader.runWithServerClassAccess(() -> { Map properties = new HashMap<>(); - properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, osgiApp.getPath().toUri().toString()); + properties.put(OSGiWebappConstants.JETTY_BUNDLE_ROOT, metadata.getPath().toUri().toString()); properties.put(OSGiServerConstants.JETTY_HOME, (String)server.getAttribute(OSGiServerConstants.JETTY_HOME)); xmlConfiguration.getProperties().putAll(properties); xmlConfiguration.configure(webApp); @@ -502,11 +502,11 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //ensure the context path is set - webApp.setContextPath(osgiApp.getContextPath()); + webApp.setContextPath(metadata.getContextPath()); //osgi Enterprise Spec r4 p.427 - webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, osgiApp.getBundle().getBundleContext()); - + webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); + //Indicate the webapp has been deployed, so that we don't try and redeploy again webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); @@ -523,14 +523,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); - Path bundlePath = osgiApp.getPath(); + Path bundlePath = metadata.getPath(); + + Resource bundleResource = Util.newBundleResource(metadata.getBundle(), resourceFactory); + + String pathToResourceBase = metadata.getPathToResourceBase(); - Resource bundleResource = osgiApp.getBundleResource(resourceFactory); - - String pathToResourceBase = osgiApp.getPathToResourceBase(); - //if the path wasn't set or it was ., then it is the root of the bundle's installed location - if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) + if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) { if (LOG.isDebugEnabled()) LOG.debug("Webapp base using bundle install location: {}", bundleResource); @@ -557,29 +557,29 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG } //web.xml - String tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { - URI webXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (webXml != null) webApp.setDescriptor(webXml.toString()); } // webdefault-ee10.xml - tmp = osgiApp.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { - URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, osgiApp.getBundle(), jettyHomePath); + URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (defaultWebXml != null) { webApp.setDefaultsDescriptor(defaultWebXml.toString()); } } - + return webApp; - } + } } - + private PackageAdminServiceTracker _packageAdminServiceTracker; private ServiceTracker _tracker; diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java index 12498cbced92..6f53b2da4470 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot-jsp/src/main/java/org/eclipse/jetty/ee11/osgi/boot/jsp/TLDServerClasspathContributor.java @@ -24,7 +24,6 @@ import org.osgi.framework.FrameworkUtil; /** - * @author janb * */ public class TLDServerClasspathContributor implements ServerClasspathContributor @@ -34,7 +33,7 @@ public class TLDServerClasspathContributor implements ServerClasspathContributor * Name of a class that belongs to the jstl bundle. From that class * we locate the corresponding bundle. */ - private static String JSTL_BUNDLE_CLASS = "org.apache.taglibs.standard.tag.el.core.WhenTag"; + private static String JSTL_BUNDLE_CLASS = "org.glassfish.wasp.taglibs.standard.tag.el.core.WhenTag"; @Override public List getScannableBundles() diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 613140d6f251..978c60b4f3fb 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee11.webapp.Configuration; @@ -32,9 +33,9 @@ import org.eclipse.jetty.ee11.webapp.WebAppContext; import org.eclipse.jetty.osgi.AbstractContextProvider; import org.eclipse.jetty.osgi.BundleContextProvider; +import org.eclipse.jetty.osgi.BundleMetadata; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -67,9 +68,9 @@ public class EE11Activator implements BundleActivator { private static final Logger LOG = LoggerFactory.getLogger(EE11Activator.class); - + public static final String ENVIRONMENT = "ee11"; - + private static Collection __serverClasspathContributors = new ArrayList<>(); public static void registerServerClasspathContributor(ServerClasspathContributor contributor) @@ -89,20 +90,19 @@ public static Collection getServerClasspathContribut /** * ServerTracker - * - * Tracks appearance of Server instances as OSGi services, and then configures them - * for deployment of EE11 contexts and webapps. * + * Tracks appearance of Server instances as OSGi services, and then configures them + * for deployment of EE11 contexts and webapps. */ public static class ServerTracker implements ServiceTrackerCustomizer { private Bundle _myBundle = null; - + public ServerTracker(Bundle bundle) { _myBundle = bundle; } - + @Override public Object addingService(ServiceReference sr) { @@ -126,7 +126,7 @@ public Object addingService(ServiceReference sr) if (LOG.isDebugEnabled()) LOG.debug("Server classloader for contexts = {}", server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER)); - } + } server.setAttribute(OSGiServerConstants.SERVER_CLASSPATH_BUNDLES, contributedBundles); } @@ -135,13 +135,11 @@ public Object addingService(ServiceReference sr) BundleContextProvider contextProvider = null; String containerScanBundlePattern = null; - if (contributedBundles != null) + if (!contributedBundles.isEmpty()) { - StringBuffer strbuff = new StringBuffer(); - contributedBundles.stream().forEach(b -> strbuff.append(b.getSymbolicName()).append("|")); - - if (strbuff.length() > 0) - containerScanBundlePattern = strbuff.toString().substring(0, strbuff.length() - 1); + containerScanBundlePattern = contributedBundles.stream() + .map(Bundle::getSymbolicName) + .collect(Collectors.joining("|")); } if (deployer.isPresent()) @@ -166,14 +164,16 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE11ContextFactory(_myBundle)); + deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE11WebAppFactory(_myBundle)); + deploymentManager.addBean(webAppProvider); } - //ensure the providers are configured with the extra bundles that must be scanned from the container classpath + // ensure the providers are configured with the extra bundles that must be scanned from the container classpath if (containerScanBundlePattern != null) { contextProvider.getAttributes().setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, containerScanBundlePattern); @@ -212,7 +212,7 @@ private Optional getDeploymentManager(Server server) Collection deployers = server.getBeans(DeploymentManager.class); return deployers.stream().findFirst(); } - + private List convertBundleToURL(Bundle bundle) { List urls = new ArrayList<>(); @@ -254,19 +254,19 @@ else if (f.isDirectory() && f.getName().equals("lib")) return urls; } } - + public static class EE11ContextFactory implements ContextFactory { private Bundle _myBundle; - + public EE11ContextFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) - throws Exception + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) + throws Exception { String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); @@ -275,13 +275,13 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); - + contextHandler.setBaseResource(Util.newBundleResource(metadata.getBundle(), resourceFactory)); + // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); - + //provide access to all ee11 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); @@ -344,7 +344,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); contextHandler.setProtectedTargets(updatedTargets); - + return contextHandler; } } @@ -352,17 +352,17 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG public static class EE11WebAppFactory implements ContextFactory { private Bundle _myBundle; - + public EE11WebAppFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) throws Exception { - String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = StringUtil.isBlank(jettyHome) ? null : ResourceFactory.of(provider.getServer()).newResource(jettyHome).getPath(); WebAppContext webApp = new WebAppContext(); @@ -370,19 +370,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); - + // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); - + //provide access to all ee11 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); if (LOG.isDebugEnabled()) LOG.debug("Environment classloader = {}", environmentLoader); - + //Ensure Configurations.getKnown is called with a classloader that can see all of the ee11 and core classes - + ClassLoader old = Thread.currentThread().getContextClassLoader(); try { @@ -397,7 +397,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG { Thread.currentThread().setContextClassLoader(old); } - + webApp.setConfigurations(Configurations.getKnown().stream() .filter(c -> c.isEnabledByDefault()) .toArray(Configuration[]::new)); @@ -412,8 +412,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) + { webAppLoader.addClassPath(p.toUri().toString()); - + } + //Set up configuration from manifest headers //extra classpath String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); @@ -421,10 +423,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG webApp.setExtraClasspath(extraClasspath); webApp.setClassLoader(webAppLoader); - + //Take care of extra provider properties webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); - + //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); @@ -444,7 +446,6 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG // context. webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, metadata.getBundle()); - // apply any META-INF/context.xml file that is found to configure // the webapp first //First try looking for one in /META-INF @@ -506,7 +507,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //osgi Enterprise Spec r4 p.427 webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); - + //Indicate the webapp has been deployed, so that we don't try and redeploy again webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); @@ -523,12 +524,12 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); - Resource bundleResource = metadata.getBundleResource(resourceFactory); + Resource bundleResource = Util.newBundleResource(metadata.getBundle(), resourceFactory); String pathToResourceBase = metadata.getPathToResourceBase(); - + //if the path wasn't set or it was ., then it is the root of the bundle's installed location - if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) + if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) { if (LOG.isDebugEnabled()) LOG.debug("Webapp base using bundle install location: {}", bundleResource); @@ -573,11 +574,11 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG webApp.setDefaultsDescriptor(defaultWebXml.toString()); } } - + return webApp; - } + } } - + private PackageAdminServiceTracker _packageAdminServiceTracker; private ServiceTracker _tracker; diff --git a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiBootWithJsp.java b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiBootWithJsp.java index 8ed634271ef2..a176bfea183e 100644 --- a/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiBootWithJsp.java +++ b/jetty-ee11/jetty-ee11-osgi/test-jetty-ee11-osgi/src/test/java/org/eclipse/jetty/ee11/osgi/test/TestJettyOSGiBootWithJsp.java @@ -20,7 +20,6 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpStatus; import org.junit.Test; -import org.junit.jupiter.api.Disabled; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.CoreOptions; @@ -51,6 +50,7 @@ public static Option[] configure() options.addAll(TestOSGiUtil.configurePaxExamLogging()); + options.add(TestOSGiUtil.optionalRemoteDebug()); options.add(CoreOptions.junitBundles()); options.addAll(TestOSGiUtil.configureJettyHomeAndPort(false, "jetty-http-boot-with-jsp.xml")); options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*")); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 6b4445e53c5c..f197b4ff1e3c 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -32,9 +32,9 @@ import org.eclipse.jetty.ee9.webapp.WebAppContext; import org.eclipse.jetty.osgi.AbstractContextProvider; import org.eclipse.jetty.osgi.BundleContextProvider; +import org.eclipse.jetty.osgi.BundleMetadata; import org.eclipse.jetty.osgi.BundleWebAppProvider; import org.eclipse.jetty.osgi.ContextFactory; -import org.eclipse.jetty.osgi.OSGiDeployableBundleMetadata; import org.eclipse.jetty.osgi.OSGiServerConstants; import org.eclipse.jetty.osgi.OSGiWebappConstants; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -67,9 +67,9 @@ public class EE9Activator implements BundleActivator { private static final Logger LOG = LoggerFactory.getLogger(EE9Activator.class); - + public static final String ENVIRONMENT = "ee9"; - + private static Collection __serverClasspathContributors = new ArrayList<>(); public static void registerServerClasspathContributor(ServerClasspathContributor contributor) @@ -89,26 +89,25 @@ public static Collection getServerClasspathContribut /** * ServerTracker - * - * Tracks appearance of Server instances as OSGi services, and then configures them - * for deployment of EE9 contexts and webapps. * + * Tracks appearance of Server instances as OSGi services, and then configures them + * for deployment of EE9 contexts and webapps. */ public static class ServerTracker implements ServiceTrackerCustomizer { private Bundle _myBundle = null; - + public ServerTracker(Bundle bundle) { _myBundle = bundle; } - + @Override public Object addingService(ServiceReference sr) { Bundle contributor = sr.getBundle(); Server server = (Server)contributor.getBundleContext().getService(sr); - + //find bundles that should be on the container classpath and convert to URLs List contributedURLs = new ArrayList<>(); List contributedBundles = new ArrayList<>(); @@ -127,10 +126,10 @@ public Object addingService(ServiceReference sr) if (LOG.isDebugEnabled()) LOG.debug("Server classloader for contexts = {}", server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER)); - } + } server.setAttribute(OSGiServerConstants.SERVER_CLASSPATH_BUNDLES, contributedBundles); } - + Optional deployer = getDeploymentManager(server); BundleWebAppProvider webAppProvider = null; BundleContextProvider contextProvider = null; @@ -171,7 +170,7 @@ public Object addingService(ServiceReference sr) { webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE9WebAppFactory(_myBundle)); } - + //ensure the providers are configured with the extra bundles that must be scanned from the container classpath if (containerScanBundlePattern != null) { @@ -210,7 +209,7 @@ private Optional getDeploymentManager(Server server) Collection deployers = server.getBeans(DeploymentManager.class); return deployers.stream().findFirst(); } - + private List convertBundleToURL(Bundle bundle) { List urls = new ArrayList<>(); @@ -252,19 +251,19 @@ else if (f.isDirectory() && f.getName().equals("lib")) return urls; } } - + public static class EE9ContextFactory implements ContextFactory { private Bundle _myBundle; - + public EE9ContextFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) - throws Exception + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) + throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); @@ -273,13 +272,13 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG ResourceFactory resourceFactory = ResourceFactory.of(contextHandler); //Make base resource that of the bundle - contextHandler.setBaseResource(metadata.getBundleResource(resourceFactory)); - + contextHandler.setBaseResource(Util.newBundleResource(metadata.getBundle(), resourceFactory)); + // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); - + //provide access to all ee9 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); @@ -342,7 +341,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG updatedTargets = new String[OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length]; System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); contextHandler.setProtectedTargets(updatedTargets); - + return contextHandler; } } @@ -350,14 +349,14 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG public static class EE9WebAppFactory implements ContextFactory { private Bundle _myBundle; - + public EE9WebAppFactory(Bundle bundle) { _myBundle = bundle; } - + @Override - public ContextHandler createContextHandler(AbstractContextProvider provider, OSGiDeployableBundleMetadata metadata) + public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) throws Exception { String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); @@ -368,19 +367,19 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //Apply defaults from the deployer providers webApp.initializeDefaults(provider.getAttributes()); - + // provides access to core classes ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader); - + //provide access to all ee9 classes ClassLoader environmentLoader = new OSGiClassLoader(coreLoader, _myBundle); if (LOG.isDebugEnabled()) LOG.debug("Environment classloader = {}", environmentLoader); - + //Ensure Configurations.getKnown is called with a classloader that can see all of the ee9 and core classes - + ClassLoader old = Thread.currentThread().getContextClassLoader(); try { @@ -395,11 +394,11 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG { Thread.currentThread().setContextClassLoader(old); } - + webApp.setConfigurations(Configurations.getKnown().stream() .filter(Configuration::isEnabledByDefault) .toArray(Configuration[]::new)); - + //Make a webapp classloader OSGiWebappClassLoader webAppLoader = new OSGiWebappClassLoader(environmentLoader, webApp, metadata.getBundle()); @@ -409,8 +408,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) + { webAppLoader.addClassPath(p.toUri().toString()); - + } + //Set up configuration from manifest headers //extra classpath String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); @@ -418,10 +419,10 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG webApp.setExtraClasspath(extraClasspath); webApp.setClassLoader(webAppLoader); - + //Take care of extra provider properties webApp.setAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN, provider.getAttributes().getAttribute(OSGiMetaInfConfiguration.CONTAINER_BUNDLE_PATTERN)); - + //TODO needed? webApp.setAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE, requireTldBundles); @@ -441,7 +442,6 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG // context. webApp.setAttribute(OSGiWebappConstants.JETTY_OSGI_BUNDLE, metadata.getBundle()); - // apply any META-INF/context.xml file that is found to configure // the webapp first //First try looking for one in /META-INF @@ -503,7 +503,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG //osgi Enterprise Spec r4 p.427 webApp.setAttribute(OSGiWebappConstants.OSGI_BUNDLECONTEXT, metadata.getBundle().getBundleContext()); - + //Indicate the webapp has been deployed, so that we don't try and redeploy again webApp.setAttribute(OSGiWebappConstants.WATERMARK, OSGiWebappConstants.WATERMARK); @@ -522,12 +522,12 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG Path bundlePath = metadata.getPath(); - Resource bundleResource = metadata.getBundleResource(resourceFactory); + Resource bundleResource = Util.newBundleResource(metadata.getBundle(), resourceFactory); String pathToResourceBase = metadata.getPathToResourceBase(); - + //if the path wasn't set or it was ., then it is the root of the bundle's installed location - if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) + if (StringUtil.isBlank(pathToResourceBase) || ".".equals(pathToResourceBase)) { if (LOG.isDebugEnabled()) LOG.debug("Webapp base using bundle install location: {}", bundleResource); @@ -572,11 +572,11 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, OSG webApp.setDefaultsDescriptor(defaultWebXml.toString()); } } - + return webApp.getCoreContextHandler(); - } + } } - + private PackageAdminServiceTracker _packageAdminServiceTracker; private ServiceTracker _tracker; From de22789708692f773bc5c13c9e67ef1773a42966 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 30 Jan 2025 17:27:52 -0600 Subject: [PATCH 060/104] Fixing more deploy test setup --- .../jetty/ee10/osgi/boot/EE10Activator.java | 17 ++++++++--------- .../jetty/ee10/test/DeploymentErrorTest.java | 8 ++++---- .../src/test/resources/RFC2616Base.xml | 3 +++ .../src/test/resources/deploy.xml | 3 +++ .../jetty/ee11/osgi/boot/EE11Activator.java | 4 ++-- .../jetty/ee11/test/DeploymentErrorTest.java | 9 +++++---- .../src/test/resources/RFC2616Base.xml | 3 +++ .../src/test/resources/deploy.xml | 3 +++ .../jetty/ee9/osgi/boot/EE9Activator.java | 11 ++++++----- .../jetty/ee9/test/DeploymentErrorTest.java | 10 +++++----- .../src/test/resources/RFC2616Base.xml | 3 +++ .../src/test/resources/deploy.xml | 3 +++ 12 files changed, 48 insertions(+), 29 deletions(-) diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index a8f37b6e65eb..766f332c1c6a 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee10.webapp.Configuration; @@ -134,13 +135,11 @@ public Object addingService(ServiceReference sr) BundleContextProvider contextProvider = null; String containerScanBundlePattern = null; - if (contributedBundles != null) + if (!contributedBundles.isEmpty()) { - StringBuffer strbuff = new StringBuffer(); - contributedBundles.stream().forEach(b -> strbuff.append(b.getSymbolicName()).append("|")); - - if (strbuff.length() > 0) - containerScanBundlePattern = strbuff.toString().substring(0, strbuff.length() - 1); + containerScanBundlePattern = contributedBundles.stream() + .map(Bundle::getSymbolicName) + .collect(Collectors.joining("|")); } if (deployer.isPresent()) @@ -165,11 +164,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE10ContextFactory(_myBundle)); + deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE10WebAppFactory(_myBundle)); + deploymentManager.addBean(webAppProvider); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -398,7 +399,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } webApp.setConfigurations(Configurations.getKnown().stream() - .filter(c -> c.isEnabledByDefault()) + .filter(Configuration::isEnabledByDefault) .toArray(Configuration[]::new)); //Make a webapp classloader @@ -523,8 +524,6 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); - Path bundlePath = metadata.getPath(); - Resource bundleResource = Util.newBundleResource(metadata.getBundle(), resourceFactory); String pathToResourceBase = metadata.getPathToResourceBase(); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 93337ac1808a..4b14bf4569d5 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -84,9 +84,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception // Empty contexts collections ContextHandlerCollection contexts = new ContextHandlerCollection(); - //Environment - Environment ee10 = Environment.ensure("ee10"); - ee10.setAttribute("contextHandlerClass", "org.eclipse.jetty.ee10.webapp.WebAppContext"); + // Environment + Environment.ensure("ee10"); // Deployment Manager deploymentManager = new DeploymentManager(); @@ -104,7 +103,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); DefaultProvider appProvider = new DefaultProvider(deploymentManager); - appProvider.configureEnvironment("ee10"); + DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee10"); + envConfig.setContextHandlerClass("org.eclipse.jetty.ee10.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index b05412416dd9..952be27627fa 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -97,6 +97,9 @@ + + + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index 928b18c91a7b..e6ddbe28e54d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index 978c60b4f3fb..dc1aa61df42b 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -268,7 +268,7 @@ public EE11ContextFactory(Bundle bundle) public ContextHandler createContextHandler(AbstractContextProvider provider, BundleMetadata metadata) throws Exception { - String jettyHome = (String)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.JETTY_HOME); + String jettyHome = (String)provider.getServer().getAttribute(OSGiServerConstants.JETTY_HOME); Path jettyHomePath = (StringUtil.isBlank(jettyHome) ? null : Paths.get(jettyHome)); ContextHandler contextHandler = new ContextHandler(); @@ -278,7 +278,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun contextHandler.setBaseResource(Util.newBundleResource(metadata.getBundle(), resourceFactory)); // provides access to core classes - ClassLoader coreLoader = (ClassLoader)provider.getContextHandlerManagement().getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); + ClassLoader coreLoader = (ClassLoader)provider.getServer().getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); if (LOG.isDebugEnabled()) LOG.debug("Core classloader = {}", coreLoader.getClass()); diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index 9c1e7b479871..4d6abc6ac254 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -84,9 +84,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception // Empty contexts collections ContextHandlerCollection contexts = new ContextHandlerCollection(); - //Environment - Environment ee11 = Environment.ensure("ee11"); - ee11.setAttribute("contextHandlerClass", "org.eclipse.jetty.ee11.webapp.WebAppContext"); + // Environment + Environment.ensure("ee11"); // Deployment Manager deploymentManager = new DeploymentManager(); @@ -104,9 +103,11 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); DefaultProvider appProvider = new DefaultProvider(deploymentManager); - appProvider.configureEnvironment("ee11"); + DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee11"); + envConfig.setContextHandlerClass("org.eclipse.jetty.ee11.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); + deploymentManager.addBean(appProvider); server.addBean(deploymentManager); diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 3e53fe2da8b8..d48a57e20c8d 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -97,6 +97,9 @@ + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index d794e829283c..3e67dd3c5829 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index f197b4ff1e3c..661d788dd2ce 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.ee9.webapp.Configuration; @@ -137,9 +138,9 @@ public Object addingService(ServiceReference sr) String containerScanBundlePattern = null; if (!contributedBundles.isEmpty()) { - StringBuffer strbuff = new StringBuffer(); - contributedBundles.forEach(b -> strbuff.append(b.getSymbolicName()).append("|")); - containerScanBundlePattern = strbuff.substring(0, strbuff.length() - 1); + containerScanBundlePattern = contributedBundles.stream() + .map(Bundle::getSymbolicName) + .collect(Collectors.joining("|")); } if (deployer.isPresent()) @@ -164,11 +165,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE9ContextFactory(_myBundle)); + deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE9WebAppFactory(_myBundle)); + deploymentManager.addBean(webAppProvider); } //ensure the providers are configured with the extra bundles that must be scanned from the container classpath @@ -520,8 +523,6 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun System.arraycopy(OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS, 0, updatedTargets, targets.length, OSGiWebappConstants.DEFAULT_PROTECTED_OSGI_TARGETS.length); webApp.setProtectedTargets(updatedTargets); - Path bundlePath = metadata.getPath(); - Resource bundleResource = Util.newBundleResource(metadata.getBundle(), resourceFactory); String pathToResourceBase = metadata.getPathToResourceBase(); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index 6f955cbdad48..3361196f6c3c 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -82,10 +82,9 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro // Empty contexts collections contexts = new ContextHandlerCollection(); - //Environment - Environment ee9 = Environment.ensure("ee9"); - ee9.setAttribute("contextHandlerClass", "org.eclipse.jetty.ee9.webapp.WebAppContext"); - + // Environment + Environment.ensure("ee9"); + // Deployment Manager deploymentManager = new DeploymentManager(); deploymentManager.setContexts(contexts); @@ -99,7 +98,8 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); DefaultProvider appProvider = new DefaultProvider(deploymentManager); - appProvider.configureEnvironment("ee9"); + DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee9"); + envConfig.setContextHandlerClass("org.eclipse.jetty.ee9.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); server.addBean(deploymentManager); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 1ce7cbffe51e..28bc3af0bd9b 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -81,6 +81,9 @@ + + + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index c174f506cbd6..3e3eca311831 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + From 25a1e254fcc391c4c1fbcb51d5b6668584420f56 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 10:15:02 -0600 Subject: [PATCH 061/104] Revert ContextHandler.id work --- .../jetty/server/handler/ContextHandler.java | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 500043787d5d..735dfb47b863 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -134,7 +134,6 @@ public static ContextHandler getContextHandler(Request request) private final List _contextListeners = new CopyOnWriteArrayList<>(); private final List _vhosts = new ArrayList<>(); - private String _id; private String _displayName; private String _contextPath = "/"; private boolean _rootContext = true; @@ -192,10 +191,6 @@ public ContextHandler(Handler handler, String contextPath) if (contextPath != null) setContextPath(contextPath); - // Copy ID if ContextHandler is wrapped (it can be overridden if needed) - if (handler instanceof ContextHandler contextHandler) - _id = contextHandler.getID(); - if (File.separatorChar == '/') addAliasCheck(new SymlinkAllowedResourceAliasChecker(this)); @@ -504,17 +499,6 @@ public String getDisplayName() return _contextPath; } - /** - * The ID of the context. - * - * @return the ID of the context, or null if not defined - */ - @ManagedAttribute(value = "ID of the Context") - public String getID() - { - return _id; - } - /** * Get if this context support cross context dispatch, either as originator or target. * @return True if this context supports cross context dispatch. @@ -1141,16 +1125,6 @@ public void setDisplayName(String servletContextName) _displayName = servletContextName; } - /** - * A unique optional ID for this ContextHandler. - * - * @param id the id; - */ - public void setID(String id) - { - _id = id; - } - /** * @return Returns the base resource as a string. */ @@ -1369,8 +1343,6 @@ public String toString() b.append(TypeUtil.toShortName(getClass())).append('@').append(Integer.toString(hashCode(), 16)); b.append('{'); - if (getID() != null) - b.append("id=").append(getID()).append(","); if (getDisplayName() != null) b.append(getDisplayName()).append(','); b.append(getContextPath()); From dd0a6136f7c22e220c221e4ab4eedf9f6ddf0c48 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 10:56:29 -0600 Subject: [PATCH 062/104] The mass renaming of deploy begins. --- .../src/main/config/etc/jetty-deploy.xml | 5 +- .../src/main/java/module-info.java | 3 +- .../jetty/deploy/ContextHandlerDeployer.java | 51 +++ .../jetty/deploy/DeploymentManager.java | 264 ++++++------ .../jetty/deploy/DeploymentNodeBinding.java | 41 ++ ...ltProvider.java => DeploymentScanner.java} | 166 ++++--- .../jetty/deploy/bindings/DebugBinding.java | 6 +- .../deploy/bindings/OrderedGroupBinding.java | 14 +- .../deploy/bindings/StandardDeployer.java | 6 +- .../deploy/bindings/StandardStarter.java | 6 +- .../deploy/bindings/StandardStopper.java | 6 +- .../deploy/bindings/StandardUndeployer.java | 6 +- .../DefaultContextHandlerFactory.java | 6 +- .../DeploymentGraph.java} | 65 ++- .../TrackedPaths.java} | 31 +- .../deploy/{ => internal}/graph/Edge.java | 2 +- .../deploy/{ => internal}/graph/Graph.java | 4 +- .../{ => internal}/graph/GraphOutputDot.java | 2 +- .../deploy/{ => internal}/graph/Node.java | 2 +- .../deploy/{ => internal}/graph/Route.java | 2 +- .../{ => internal}/graph/package-info.java | 2 +- .../deploy/jmx/DeploymentManagerMBean.java | 48 +-- .../jetty/deploy/scan/DeployAction.java | 50 --- .../deploy/scan/DeployActionComparator.java | 57 --- .../jetty/deploy/scan/package-info.java | 18 - .../AbstractContextHandlerDeployer.java} | 32 +- .../DeploymentManagerLifeCycleRouteTest.java | 61 ++- .../jetty/deploy/DeploymentManagerTest.java | 206 ++++++++- ...a => DeploymentScannerCoreWebappTest.java} | 10 +- ...DeploymentScannerDeferredStartupTest.java} | 18 +- ...mentScannerDeployActionComparatorTest.java | 198 +++++++++ ... DeploymentScannerRuntimeUpdatesTest.java} | 18 +- ...java => DeploymentScannerStartupTest.java} | 9 +- .../jetty/deploy/DeploymentScannerTest.java | 395 +++++++++++++++++ .../{PhonyContextProvider.java => Util.java} | 49 +-- .../DeploymentLifeCyclePathCollector.java} | 10 +- .../DeploymentLifeCycleTest.java} | 18 +- .../TrackedPathsTest.java} | 114 ++--- .../{ => internal}/graph/GraphTest.java | 2 +- .../deploy/scan/DefaultProviderTest.java | 404 ------------------ .../scan/DeployActionComparatorTest.java | 196 --------- .../jetty/osgi/AbstractContextProvider.java | 8 +- .../jetty/osgi/BundleContextProvider.java | 10 +- .../jetty/osgi/BundleWebAppProvider.java | 14 +- .../jetty/osgi/JettyServerFactory.java | 4 +- .../org/eclipse/jetty/osgi/OSGiDeployer.java | 2 +- .../eclipse/jetty/osgi/OSGiUndeployer.java | 2 +- .../jetty/ee10/test/DeploymentErrorTest.java | 21 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../jetty/ee11/test/DeploymentErrorTest.java | 21 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../jetty/ee9/test/DeploymentErrorTest.java | 21 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- 56 files changed, 1441 insertions(+), 1277 deletions(-) create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java create mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{scan/DefaultProvider.java => DeploymentScanner.java} (88%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{scan => internal}/DefaultContextHandlerFactory.java (98%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ContextHandlerLifeCycle.java => internal/DeploymentGraph.java} (64%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{scan/ScanTrackedApp.java => internal/TrackedPaths.java} (94%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/Edge.java (97%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/Graph.java (98%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/GraphOutputDot.java (99%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/Node.java (96%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/Route.java (98%) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/{ => internal}/graph/package-info.java (92%) delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployAction.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployActionComparator.java delete mode 100644 jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/package-info.java rename jetty-core/jetty-deploy/src/{main/java/org/eclipse/jetty/deploy/ContextHandlerManagement.java => test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java} (51%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{scan/DefaultProviderCoreWebappTest.java => DeploymentScannerCoreWebappTest.java} (92%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{scan/DefaultProviderDeferredStartupTest.java => DeploymentScannerDeferredStartupTest.java} (91%) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{scan/DefaultProviderRuntimeUpdatesTest.java => DeploymentScannerRuntimeUpdatesTest.java} (94%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{scan/DefaultProviderStartupTest.java => DeploymentScannerStartupTest.java} (97%) create mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{PhonyContextProvider.java => Util.java} (56%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{ContextHandlerLifeCyclePathCollector.java => internal/DeploymentLifeCyclePathCollector.java} (86%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{ContextHandlerLifeCycleTest.java => internal/DeploymentLifeCycleTest.java} (91%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{scan/ScanTrackedAppTest.java => internal/TrackedPathsTest.java} (53%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/{ => internal}/graph/GraphTest.java (98%) delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderTest.java delete mode 100644 jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DeployActionComparatorTest.java diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 73fb4f8a6f02..80d492ef7b58 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -8,7 +8,10 @@ - + + + + diff --git a/jetty-core/jetty-deploy/src/main/java/module-info.java b/jetty-core/jetty-deploy/src/main/java/module-info.java index 11a95a3c63b3..970cbeeedfdb 100644 --- a/jetty-core/jetty-deploy/src/main/java/module-info.java +++ b/jetty-core/jetty-deploy/src/main/java/module-info.java @@ -23,9 +23,8 @@ exports org.eclipse.jetty.deploy; exports org.eclipse.jetty.deploy.bindings; - exports org.eclipse.jetty.deploy.graph; - exports org.eclipse.jetty.deploy.scan; exports org.eclipse.jetty.deploy.jmx to org.eclipse.jetty.jmx; + exports org.eclipse.jetty.deploy.internal; } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java new file mode 100644 index 000000000000..03017fe16ad6 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java @@ -0,0 +1,51 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import org.eclipse.jetty.server.handler.ContextHandler; + +/** + * Deployer for ContextHandlers + */ +public interface ContextHandlerDeployer +{ + /** + * Add a ContextHandler to the graph and then move it a deployed state. + * + * @param contextHandler the ContextHandler to deploy. + */ + void deploy(ContextHandler contextHandler); + + /** + * Move a ContextHandler int the graph to an undeployed state, and then remove it from the graph. + * + * @param contextHandler the ContextHandler to undeploy. + */ + void undeploy(ContextHandler contextHandler); + + /** + * Add a ContextHandler int the graph but perform no actions on it, leaving it in undeployed state. + * + * @param contextHandler the ContextHandler to add to the graph. + */ + void addUndeployed(ContextHandler contextHandler); + + /** + * Advanced usage, move a ContextHandler through the DeploymentManager graph by name. + * + * @param contextHandler the ContextHandler to move + * @param goalName the goal graph node by name + */ + void move(ContextHandler contextHandler, String goalName); +} \ No newline at end of file diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 16c0bd6aedbd..9ecb4feca1b3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -13,22 +13,23 @@ package org.eclipse.jetty.deploy; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Objects; import java.util.Queue; +import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; import org.eclipse.jetty.deploy.bindings.StandardDeployer; import org.eclipse.jetty.deploy.bindings.StandardStarter; import org.eclipse.jetty.deploy.bindings.StandardStopper; import org.eclipse.jetty.deploy.bindings.StandardUndeployer; -import org.eclipse.jetty.deploy.graph.Edge; -import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.graph.Route; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; +import org.eclipse.jetty.deploy.internal.graph.Edge; +import org.eclipse.jetty.deploy.internal.graph.Node; +import org.eclipse.jetty.deploy.internal.graph.Route; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -49,58 +50,85 @@ *

    * deployment manager roles graph *

      - *
    1. Tracking Apps and their LifeCycle Location
    2. - *
    3. Executing ContextHandlerLifeCycle on App based on current and desired LifeCycle Location.
    4. + *
    5. Tracking ContextHandlers and their location in the Deployment graph.
    6. + *
    7. Moving ContextHandlers through the Deployment graph (eg: DEPLOYED, STARTED, UNDEPLOYED, etc.).
    8. *
    *

    * deployment manager graph */ // TODO: fix dumpable to show things like context-handler-collection, contexts being tracked, etc... @ManagedObject("Deployment Manager") -public class DeploymentManager extends ContainerLifeCycle implements ContextHandlerManagement +public class DeploymentManager extends ContainerLifeCycle implements ContextHandlerDeployer { private static final Logger LOG = LoggerFactory.getLogger(DeploymentManager.class); private final AutoLock _lock = new AutoLock(); - private final ContextHandlerLifeCycle _lifecycle = new ContextHandlerLifeCycle(); + private final DeploymentGraph _lifecycle = new DeploymentGraph(); private final Queue _tracked = new ConcurrentLinkedQueue<>(); private Throwable _onStartupErrors; private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; /** - * Add a ContextHandler to the tracking, and move it to the desired node name. + * Add a DeploymentNodeBinding to the graph. * - * @param contextHandler the context handler - * @param nodeName the requested node to reach + * @param binding the binding to add. + */ + // TODO: Rename + public void addLifeCycleBinding(DeploymentNodeBinding binding) + { + _lifecycle.addBinding(binding); + } + + /** + * Add a ContextHandler int the graph but perform no actions on it, leaving it in undeployed state. + * + * @param contextHandler the ContextHandler to add to the graph. */ @Override - public void addContextHandler(ContextHandler contextHandler, String nodeName) + public void addUndeployed(ContextHandler contextHandler) { if (LOG.isDebugEnabled()) - LOG.debug("addContextHandler: {} -> {}", contextHandler, nodeName); - TrackedContext entry = new TrackedContext(); - entry.contextHandler = contextHandler; - entry.setLifeCycleNode(_lifecycle.getNodeByName(ContextHandlerLifeCycle.UNDEPLOYED)); - _tracked.add(entry); + LOG.debug("addUndeployed: {}", contextHandler); + startTracking(contextHandler); + } - if (isRunning()) - { - // Immediately attempt to go to default lifecycle state - this.requestContextHandlerGoal(entry, nodeName); - } + /** + * Add a ContextHandler to the graph and then move it a deployed state. + * + * @param contextHandler the ContextHandler to deploy. + */ + @Override + public void deploy(ContextHandler contextHandler) + { + if (LOG.isDebugEnabled()) + LOG.debug("deploy: {}", contextHandler); + TrackedContext trackedContext = startTracking(contextHandler); + requestContextHandlerGoal(trackedContext, DeploymentGraph.STARTED); } - public void addLifeCycleBinding(ContextHandlerLifeCycle.Binding binding) + /** + * Get the bindings that exist + */ + public Set getBindings() { - _lifecycle.addBinding(binding); + return _lifecycle.getBindings(); } - public ContextHandler findContextHandler(String id) + /** + * Get the bindings that exist for a specific node name. + * + * @param nodeName the node to get bindings from + */ + public Set getBindings(String nodeName) { - TrackedContext entry = findTrackedContext(id); - return entry == null ? null : entry.getContextHandler(); + return _lifecycle.getBindings(nodeName); } + /** + * Get the list of tracked {@link ContextHandler} in the DeploymentManager graph. + * + * @return the list of tracked ContextHandlers + */ public Collection getContextHandlers() { return _tracked.stream() @@ -109,28 +137,23 @@ public Collection getContextHandlers() } /** - * Get Set of {@link ContextHandler}s by {@link Node} + * Get the list of ContextHandlers present at a specific graph node name. * - * @param node the node to look for. - * @return the collection of ContextHandlers for the node + * @param nodeName the node name to look in + * @return the list of {@link ContextHandler} present on that node */ - public Collection getContextHandlers(Node node) + @ManagedOperation(value = "list ContextHandlers that are located at specified deployment manager graph node", impact = "ACTION") + public Collection getContextHandlers(@Name("nodeName") String nodeName) { - Objects.requireNonNull(node); - - List ret = new ArrayList<>(); - for (TrackedContext entry : _tracked) + Set nodeNames = getNodeNames(); + if (!nodeNames.contains(nodeName)) { - if (node.equals(entry.lifecyleNode)) - { - ret.add(entry.contextHandler); - } + throw new IllegalArgumentException("Unable to find node [" + nodeName + "] in " + + nodeNames.stream() + .sorted() + .collect(Collectors.joining(", ", "[", "]"))); } - return ret; - } - public Collection getContextHandlers(String nodeName) - { return getContextHandlers(_lifecycle.getNodeByName(nodeName)); } @@ -145,33 +168,14 @@ public void setContexts(ContextHandlerCollection contexts) this._contexts = contexts; } - public ContextHandlerLifeCycle getLifeCycle() - { - return _lifecycle; - } - - public Collection getLifeCycleBindings() - { - return Collections.unmodifiableSet(_lifecycle.getBindings()); - } - - public void setLifeCycleBindings(Collection bindings) - { - if (isRunning()) - throw new IllegalStateException(); - for (ContextHandlerLifeCycle.Binding b : _lifecycle.getBindings()) - { - _lifecycle.removeBinding(b); - } - for (ContextHandlerLifeCycle.Binding b : bindings) - { - _lifecycle.addBinding(b); - } - } - - public Collection getNodes() + @ManagedOperation(value = "list nodes that are tracked by DeploymentManager", impact = "INFO") + public Set getNodeNames() { - return _lifecycle.getNodes(); + Set names = new TreeSet<>(String::compareToIgnoreCase); + _lifecycle.getNodes().stream() + .map(Node::getName) + .forEach(names::add); + return names; } public Server getServer() @@ -209,71 +213,56 @@ public void setUseStandardBindings(boolean useStandardBindings) } /** - * Remove the ContextHandler from the DeploymentManager tracking. + * Advanced usage, move a ContextHandler through the DeploymentManager graph by name. * - * @param contextHandler the contextHandler to remove it from the deployment manager. - * @param goalName the name of the node to attain before removal of contextHandler. + * @param contextHandler the ContextHandler to move + * @param goalName the goal graph node by name */ @Override - public void removeContextHandler(ContextHandler contextHandler, String goalName) + public void move(ContextHandler contextHandler, String goalName) { - LOG.info("removeContextHandler: {}, {}", contextHandler, goalName); - Iterator it = _tracked.iterator(); - while (it.hasNext()) - { - TrackedContext entry = it.next(); - if (entry.contextHandler.equals(contextHandler)) - { - if (!goalName.equals(entry.lifecyleNode.getName())) - requestContextHandlerGoal(entry.contextHandler, goalName); - it.remove(); - } - } + if (LOG.isDebugEnabled()) + LOG.debug("move: {} -> {}", contextHandler, goalName); + TrackedContext trackedContext = findTrackedContext(contextHandler); + requestContextHandlerGoal(trackedContext, goalName); } - /** - * Move an {@link ContextHandler} through the {@link ContextHandlerLifeCycle} to the desired {@link Node}, executing each lifecycle step - * in the process to reach the desired state. - * - * @param contextHandler the ContextHandler to move through the process - * @param nodeName the name of the node to attain - */ - @Override - public void requestContextHandlerGoal(ContextHandler contextHandler, String nodeName) + public void setLifeCycleBindings(Collection bindings) { - TrackedContext tracked = findTrackedContext(contextHandler.getID()); - if (tracked == null) + if (isRunning()) + throw new IllegalStateException(); + for (DeploymentNodeBinding b : _lifecycle.getBindings()) { - throw new IllegalStateException("ContextHandler not being tracked by Deployment Manager: " + contextHandler); + _lifecycle.removeBinding(b); + } + for (DeploymentNodeBinding b : bindings) + { + _lifecycle.addBinding(b); } - - requestContextHandlerGoal(tracked, nodeName); } /** - * Move an ContextHandler.ID through the {@link ContextHandlerLifeCycle} to the desired {@link Node}, executing each lifecycle step - * in the process to reach the desired state. + * Move a ContextHandler int the graph to an undeployed state, and then remove it from the graph. * - * @param contextHandlerId the ID of ContextHandler to move through the process - * @param nodeName the name of the node to attain + * @param contextHandler the ContextHandler to undeploy. */ - @ManagedOperation(value = "request the context handler to be moved to the specified lifecycle node", impact = "ACTION") - public void requestContextHandlerGoal(@Name("contextId") String contextHandlerId, @Name("nodeName") String nodeName) + @Override + public void undeploy(ContextHandler contextHandler) { - TrackedContext tracked = findTrackedContext(contextHandlerId); - if (tracked == null) - { - throw new IllegalStateException("ContextHandler not being tracked by Deployment Manager: " + contextHandlerId); - } - requestContextHandlerGoal(tracked, nodeName); + if (LOG.isDebugEnabled()) + LOG.debug("undeploy: {}", contextHandler); + TrackedContext trackedContext = findTrackedContext(contextHandler); + requestContextHandlerGoal(trackedContext, DeploymentGraph.UNDEPLOYED); + stopTracking(trackedContext); } + @ManagedOperation(value = "undeploy all ContextHandlers being tracked by DeploymentManager") public void undeployAll() { LOG.debug("Undeploy All"); for (TrackedContext entry : _tracked) { - requestContextHandlerGoal(entry, ContextHandlerLifeCycle.UNDEPLOYED); + undeploy(entry.contextHandler); } } @@ -308,21 +297,32 @@ private void addOnStartupError(Throwable cause) } } - private TrackedContext findTrackedContext(String id) + private TrackedContext findTrackedContext(ContextHandler contextHandler) { - if (id == null) - return null; + return _tracked.stream() + .filter((e) -> e.contextHandler.equals(contextHandler)) + .findFirst() + .orElseThrow(() -> new IllegalStateException("ContextHandler[%s,%s] not being tracked".formatted(contextHandler.getContextPath(), contextHandler.getVirtualHosts()))); + } - for (TrackedContext entry : _tracked) - { - if (id.equals(entry.contextHandler.getID())) - return entry; - } - return null; + /** + * Get Set of {@link ContextHandler}s by {@link Node} + * + * @param node the node to look for. + * @return the collection of ContextHandlers for the node + */ + private Collection getContextHandlers(Node node) + { + Objects.requireNonNull(node); + + return _tracked.stream() + .filter(tracked -> node.equals(tracked.lifecyleNode)) + .map(TrackedContext::getContextHandler) + .toList(); } /** - * Move a {@link TrackedContext} through the {@link ContextHandlerLifeCycle} to the desired {@link Node}, executing each lifecycle step + * Move a {@link TrackedContext} through the {@link DeploymentGraph} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. * * @param tracked the internal tracked context to move through the process @@ -366,7 +366,7 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) LOG.warn("Unable to reach node goal: {}", nodeName, t); // migrate to FAILED node - Node failed = _lifecycle.getNodeByName(ContextHandlerLifeCycle.FAILED); + Node failed = _lifecycle.getNodeByName(DeploymentGraph.FAILED); tracked.setLifeCycleNode(failed); try { @@ -385,6 +385,20 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) } } + private TrackedContext startTracking(ContextHandler contextHandler) + { + TrackedContext entry = new TrackedContext(); + entry.contextHandler = contextHandler; + entry.setLifeCycleNode(_lifecycle.getNodeByName(DeploymentGraph.UNDEPLOYED)); + _tracked.add(entry); + return entry; + } + + private void stopTracking(TrackedContext trackedContext) + { + _tracked.remove(trackedContext); + } + /** * A mutable record tracking a single context within the deployment manager. */ diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java new file mode 100644 index 000000000000..79b196bed030 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java @@ -0,0 +1,41 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import org.eclipse.jetty.deploy.internal.graph.Node; +import org.eclipse.jetty.server.handler.ContextHandler; + +/** + * A binding for a node on the deployment graph, to perform actions when + * a ContextHandler reaches that point on the graph. + */ +public interface DeploymentNodeBinding +{ + /** + * Get a list of target nodes that this implementation should bind to. + * + * @return the array of String node names to bind to. (use "*" to bind to all known node names) + */ + String[] getBindingTargets(); + + /** + * Event called to perform an action when targeted node on the Deployment graph + * has a ContextHandler move through it. + * + * @param node the node being processed + * @param contextHandler the contextHandler being processed + * @throws Exception if any problem severe enough to halt the ContextHandler processing + */ + void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception; +} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java similarity index 88% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index f844b43e649f..03bd28952196 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy; import java.io.File; import java.io.FilenameFilter; @@ -36,8 +36,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; -import org.eclipse.jetty.deploy.ContextHandlerManagement; +import org.eclipse.jetty.deploy.internal.DefaultContextHandlerFactory; +import org.eclipse.jetty.deploy.internal.TrackedPaths; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -92,7 +92,7 @@ * Context Deployment properties will be initialized with: *

    *
      - *
    • The properties set on the application via embedded calls modifying {@link ScanTrackedApp#getAttributes()}
    • + *
    • The properties set on the application via embedded calls modifying {@link TrackedPaths#getAttributes()}
    • *
    • The app specific properties file {@code webapps/.properties}
    • *
    • The environment specific properties file {@code webapps/[-zzz].properties}
    • *
    • The {@link Attributes} from the {@link Environment}
    • @@ -105,24 +105,25 @@ *

      * *
      {@code
      - * DefaultProvider provider = new DefaultProvider();
      + * DeploymentScanner provider = new DeploymentScanner();
        * EnvironmentConfig env10config = provider.configureEnvironment("ee10");
        * env10config.setExtractWars(true);
        * env10config.setParentLoaderPriority(false);
        * }
      */ // TODO: fix dumpable to show details about monitored dirs, environments dir, configured environment attributes, scan interval, etc ... -@ManagedObject("Provider for start-up deployment of webapps based on presence in directory") -public class DefaultProvider extends ContainerLifeCycle implements Scanner.ChangeSetListener +@ManagedObject("Provider for dynamic deployment of contexts (and webapps) based on presence in directory") +public class DeploymentScanner extends ContainerLifeCycle implements Scanner.ChangeSetListener { - private static final Logger LOG = LoggerFactory.getLogger(DefaultProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(DeploymentScanner.class); - private final ContextHandlerManagement contextManagement; + private final Server server; + private final ContextHandlerDeployer contextManagement; private final FilenameFilter filenameFilter; private final List monitoredDirs = new CopyOnWriteArrayList<>(); private final DefaultContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); - private Map apps = new HashMap<>(); + private Map trackedPathsMap = new HashMap<>(); private Comparator actionComparator = new DeployActionComparator(); private Map environmentAttributesMap = new HashMap<>(); private Path environmentsDir; @@ -132,14 +133,19 @@ public class DefaultProvider extends ContainerLifeCycle implements Scanner.Chang private boolean deferInitialScan = false; private String defaultEnvironmentName; - public DefaultProvider(@Name("contextManagement") ContextHandlerManagement contextManagement) + public DeploymentScanner( + @Name("server") Server server, + @Name("contextManagement") ContextHandlerDeployer contextManagement) { - this(contextManagement, null); + this(server, contextManagement, null); } - public DefaultProvider(@Name("contextManagement") ContextHandlerManagement contextManagement, - @Name("filenameFilter") FilenameFilter filter) + public DeploymentScanner( + @Name("server") Server server, + @Name("contextManagement") ContextHandlerDeployer contextManagement, + @Name("filenameFilter") FilenameFilter filter) { + this.server = server; this.contextManagement = Objects.requireNonNull(contextManagement); this.filenameFilter = Objects.requireNonNullElse(filter, new MonitoredPathFilter(monitoredDirs)); } @@ -324,19 +330,19 @@ public void pathsChanged(Map changeSet) for (Map.Entry entry : changeSet.entrySet()) { Path path = entry.getKey(); - ScanTrackedApp.State state = switch (entry.getValue()) + TrackedPaths.State state = switch (entry.getValue()) { case ADDED -> { - yield ScanTrackedApp.State.ADDED; + yield TrackedPaths.State.ADDED; } case CHANGED -> { - yield ScanTrackedApp.State.CHANGED; + yield TrackedPaths.State.CHANGED; } case REMOVED -> { - yield ScanTrackedApp.State.REMOVED; + yield TrackedPaths.State.REMOVED; } }; @@ -353,7 +359,7 @@ public void pathsChanged(Map changeSet) { // we have a normal path entry changedBaseNames.add(basename); - ScanTrackedApp app = apps.computeIfAbsent(basename, ScanTrackedApp::new); + TrackedPaths app = trackedPathsMap.computeIfAbsent(basename, TrackedPaths::new); app.putPath(path, state); } else if (isEnvironmentConfigPath(path)) @@ -378,9 +384,9 @@ else if (isEnvironmentConfigPath(path)) // Now we want to convert this list of changes to a DeployAction list // that will perform the add/remove logic in a consistent way. - List changedApps = changedBaseNames + List changedApps = changedBaseNames .stream() - .map(name -> apps.get(name)) + .map(this::findTracked) .collect(Collectors.toList()); if (!changedEnvironments.isEmpty()) @@ -394,15 +400,15 @@ else if (isEnvironmentConfigPath(path)) for (String changedEnvName : changedEnvironments) { // Add any missing apps to changedApps list - for (ScanTrackedApp app : apps.values()) + for (TrackedPaths app : trackedPathsMap.values()) { if (changedBaseNames.contains(app.getName())) continue; // skip app that's already in the change list. if (changedEnvName.equalsIgnoreCase(app.getEnvironmentName())) { - if (app.getState() == ScanTrackedApp.State.UNCHANGED) - app.setState(ScanTrackedApp.State.CHANGED); + if (app.getState() == TrackedPaths.State.UNCHANGED) + app.setState(TrackedPaths.State.CHANGED); changedApps.add(app); changedBaseNames.add(app.getName()); } @@ -443,19 +449,32 @@ public void scan() scanner.nudge(); } + protected TrackedPaths findTracked(String name) + { + return trackedPathsMap.get(name); + } + + public void resetTrackedState(String name) + { + TrackedPaths tracked = findTracked(name); + if (tracked == null) + return; + tracked.resetStates(); + } + @Override public String toString() { return String.format("%s@%x[dirs=%s]", this.getClass(), hashCode(), monitoredDirs); } - protected List buildActionList(List changedApps) + protected List buildActionList(List changedApps) { if (LOG.isDebugEnabled()) LOG.debug("buildActionList: {}", changedApps); List actions = new ArrayList<>(); - for (ScanTrackedApp app : changedApps) + for (TrackedPaths app : changedApps) { if (LOG.isDebugEnabled()) LOG.debug("changed app: {}", app); @@ -464,16 +483,16 @@ protected List buildActionList(List changedApps) { case ADDED -> { - actions.add(new DeployAction(DeployAction.Type.ADD, app)); + actions.add(new DeployAction(DeployAction.Type.ADD, app.getName())); } case CHANGED -> { - actions.add(new DeployAction(DeployAction.Type.REMOVE, app)); - actions.add(new DeployAction(DeployAction.Type.ADD, app)); + actions.add(new DeployAction(DeployAction.Type.REMOVE, app.getName())); + actions.add(new DeployAction(DeployAction.Type.ADD, app.getName())); } case REMOVED -> { - actions.add(new DeployAction(DeployAction.Type.REMOVE, app)); + actions.add(new DeployAction(DeployAction.Type.REMOVE, app.getName())); } } } @@ -534,9 +553,11 @@ protected void doStart() throws Exception if (isDeferInitialScan()) { + if (server == null) + throw new IllegalStateException("Cannot defer initial scan with a null Server"); // Setup listener to wait for Server in STARTED state, which // triggers the first scan of the monitored directories - contextManagement.getServer().addEventListener( + server.addEventListener( new LifeCycle.Listener() { @Override @@ -609,32 +630,33 @@ protected void performActions(List actions) { for (DeployAction step : actions) { + TrackedPaths app = findTracked(step.name()); try { - switch (step.getType()) + switch (step.type()) { case REMOVE -> { - apps.remove(step.getName()); - contextManagement.removeContextHandler(step.getApp().getContextHandler(), ContextHandlerLifeCycle.UNDEPLOYED); + trackedPathsMap.remove(step.name()); + contextManagement.undeploy(app.getContextHandler()); } case ADD -> { // Load .properties into app. - step.getApp().loadProperties(); + app.loadProperties(); // Ensure Environment name is set - String appEnvironment = step.getApp().getEnvironmentName(); + String appEnvironment = app.getEnvironmentName(); if (StringUtil.isBlank(appEnvironment)) appEnvironment = getDefaultEnvironmentName(); - step.getApp().setEnvironment(Environment.get(appEnvironment)); + app.setEnvironment(Environment.get(appEnvironment)); // Create a new Attributes layer for the App deployment, which is the // combination of layered Environment Attributes with App Attributes overlaying them. Attributes envAttributes = environmentAttributesMap.get(appEnvironment); - Attributes.Layer deployAttributes = new Attributes.Layer(envAttributes, step.getApp().getAttributes()); - deployAttributes.setAttribute(Deployable.MAIN_PATH, step.getApp().getMainPath()); - deployAttributes.setAttribute(Deployable.OTHER_PATHS, step.getApp().getPaths().keySet()); + Attributes.Layer deployAttributes = new Attributes.Layer(envAttributes, app.getAttributes()); + deployAttributes.setAttribute(Deployable.MAIN_PATH, app.getMainPath()); + deployAttributes.setAttribute(Deployable.OTHER_PATHS, app.getPaths().keySet()); // Ensure that Environment configuration XMLs are listed in deployAttributes List envXmlPaths = findEnvironmentXmlPaths(deployAttributes); @@ -642,24 +664,22 @@ protected void performActions(List actions) DefaultContextHandlerFactory.setEnvironmentXmlPaths(deployAttributes, envXmlPaths); // Create the Context Handler - Server server = contextManagement.getServer(); - ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, step.getApp(), deployAttributes); - contextHandler.setID(step.getName()); // force ID to what we need (XML could have set it to something else) - step.getApp().setContextHandler(contextHandler); + ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, app, deployAttributes); + app.setContextHandler(contextHandler); // Introduce the App to the DeploymentManager - apps.put(step.getName(), step.getApp()); - contextManagement.addContextHandler(step.getApp().getContextHandler(), ContextHandlerLifeCycle.STARTED); + trackedPathsMap.put(step.name(), app); + contextManagement.deploy(app.getContextHandler()); } } } catch (Throwable t) { - LOG.warn("Failed to to perform action {} on {}", step.getType(), step.getApp(), t); + LOG.warn("Failed to to perform action {} on {}", step.type(), app, t); } finally { - step.getApp().resetStates(); + app.resetStates(); } } } @@ -807,6 +827,56 @@ private Attributes loadEnvironmentAttributes(String env) throws IOException return attributesLayer; } + public record DeployAction(DeployAction.Type type, String name) + { + public enum Type + { + REMOVE, + ADD; + } + } + + /** + *

      The List of {@link DeployAction} sort.

      + * + *
        + *
      • {@link DeployAction#type()} is sorted by all {@link DeployAction.Type#REMOVE} + * actions first, followed by all {@link DeployAction.Type#ADD} actions.
      • + *
      • {@link DeployAction.Type#REMOVE} type are in descending alphabetically order.
      • + *
      • {@link DeployAction.Type#ADD} type are in ascending alphabetically order.
      • + *
      > + */ + public static class DeployActionComparator implements Comparator + { + private final Comparator typeComparator; + private final Comparator basenameComparator; + + public DeployActionComparator() + { + typeComparator = Comparator.comparing(DeployAction::type); + basenameComparator = Comparator.comparing(DeployAction::name); + } + + @Override + public int compare(DeployAction o1, DeployAction o2) + { + int diff = typeComparator.compare(o1, o2); + if (diff != 0) + return diff; + return switch (o1.type()) + { + case REMOVE -> + { + yield basenameComparator.compare(o2, o1); + } + case ADD -> + { + yield basenameComparator.compare(o1, o2); + } + }; + } + } + /** * Builder of a deployment configuration for a specific {@link Environment}. * diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java index 1397244bef85..089e4cd153b3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java @@ -13,14 +13,14 @@ package org.eclipse.jetty.deploy.bindings; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DebugBinding implements ContextHandlerLifeCycle.Binding +public class DebugBinding implements DeploymentNodeBinding { private static final Logger LOG = LoggerFactory.getLogger(DebugBinding.class); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java index 18757ea81211..d1f76a566d5e 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java @@ -16,27 +16,27 @@ import java.util.Arrays; import java.util.LinkedList; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; /** * Provides a way of forcing the ordered execution of bindings within * a declared binding target. */ -public class OrderedGroupBinding implements ContextHandlerLifeCycle.Binding +public class OrderedGroupBinding implements DeploymentNodeBinding { private String[] _bindingTargets; - private LinkedList _orderedBindings; + private LinkedList _orderedBindings; public OrderedGroupBinding(String[] bindingTargets) { _bindingTargets = bindingTargets; } - public void addBinding(ContextHandlerLifeCycle.Binding binding) + public void addBinding(DeploymentNodeBinding binding) { if (_orderedBindings == null) { @@ -46,7 +46,7 @@ public void addBinding(ContextHandlerLifeCycle.Binding binding) _orderedBindings.add(binding); } - public void addBindings(ContextHandlerLifeCycle.Binding[] bindings) + public void addBindings(DeploymentNodeBinding[] bindings) { if (_orderedBindings == null) { @@ -65,7 +65,7 @@ public String[] getBindingTargets() @Override public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception { - for (ContextHandlerLifeCycle.Binding binding : _orderedBindings) + for (DeploymentNodeBinding binding : _orderedBindings) { binding.processBinding(deploymentManager, node, contextHandler); } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java index b74dfad55c84..decb56fe00e0 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java @@ -15,13 +15,13 @@ import java.util.Objects; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Callback; -public class StandardDeployer implements ContextHandlerLifeCycle.Binding +public class StandardDeployer implements DeploymentNodeBinding { @Override public String[] getBindingTargets() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java index fd0e7c8b18b9..b864b8dbcd55 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java @@ -13,13 +13,13 @@ package org.eclipse.jetty.deploy.bindings; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -public class StandardStarter implements ContextHandlerLifeCycle.Binding +public class StandardStarter implements DeploymentNodeBinding { @Override public String[] getBindingTargets() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java index a87eeef8157e..cf2ae406b5f0 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java @@ -13,12 +13,12 @@ package org.eclipse.jetty.deploy.bindings; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; -public class StandardStopper implements ContextHandlerLifeCycle.Binding +public class StandardStopper implements DeploymentNodeBinding { @Override public String[] getBindingTargets() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java index 60b08aa27f49..2291fc191614 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java @@ -13,14 +13,14 @@ package org.eclipse.jetty.deploy.bindings; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.Callback; -public class StandardUndeployer implements ContextHandlerLifeCycle.Binding +public class StandardUndeployer implements DeploymentNodeBinding { @Override public String[] getBindingTargets() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java similarity index 98% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultContextHandlerFactory.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java index a420e8c9393c..7d4f3e7a17a9 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy.internal; import java.nio.file.Files; import java.nio.file.Path; @@ -79,7 +79,7 @@ public static void setEnvironmentXmlPaths(Attributes attributes, List path * @return the ContextHandler * @throws Exception if unable to create the ContextHandler */ - public ContextHandler newContextHandler(Server server, ScanTrackedApp app, Attributes deployAttributes) throws Exception + public ContextHandler newContextHandler(Server server, TrackedPaths app, Attributes deployAttributes) throws Exception { Path mainPath = app.getMainPath(); if (mainPath == null) @@ -382,7 +382,7 @@ private ContextHandler getContextHandler(Object context) * @return the Context Object. * @throws Exception if unable to create Object instance. */ - private Object newContextInstance(Server server, Environment environment, ScanTrackedApp app, Attributes attributes, Path path) throws Exception + private Object newContextInstance(Server server, Environment environment, TrackedPaths app, Attributes attributes, Path path) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("newContextInstance({}, {}, {}, {})", server, environment, app, path); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycle.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java similarity index 64% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycle.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java index 923ef6e610e9..d05a821d09a7 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycle.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy; +package org.eclipse.jetty.deploy.internal; import java.util.ArrayList; import java.util.HashMap; @@ -20,45 +20,28 @@ import java.util.Map; import java.util.Set; -import org.eclipse.jetty.deploy.graph.Graph; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Graph; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The lifecycle of a {@link ContextHandler} in the {@link DeploymentManager}. + * A pre-defined graph representing the lifecycle of a {@link ContextHandler} in the {@link DeploymentManager}. *

      - * Sets up the default {@link Graph}, and manages the bindings to the life cycle via the {@link ContextHandlerLifeCycle.Binding} - * annotation. + * Sets up the default {@link Graph}, and manages the bindings of actions for each node + * via the {@link DeploymentNodeBinding} implementation. *

      * context-handler lifecycle graph */ -public class ContextHandlerLifeCycle extends Graph +public class DeploymentGraph extends Graph { - private static final Logger LOG = LoggerFactory.getLogger(ContextHandlerLifeCycle.class); + private static final Logger LOG = LoggerFactory.getLogger(DeploymentGraph.class); private static final String ALL_NODES = "*"; - public interface Binding - { - /** - * Get a list of targets that this implementation should bind to. - * - * @return the array of String node names to bind to. (use "*" to bind to all known node names) - */ - String[] getBindingTargets(); - - /** - * Event called to process a {@link ContextHandlerLifeCycle} binding. - * - * @param node the node being processed - * @param contextHandler the contextHandler being processed - * @throws Exception if any problem severe enough to halt the ContextHandlerLifeCycle processing - */ - void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception; - } - // Well known existing lifecycle Nodes public static final String UNDEPLOYED = "undeployed"; public static final String DEPLOYING = "deploying"; @@ -69,9 +52,9 @@ public interface Binding public static final String UNDEPLOYING = "undeploying"; public static final String FAILED = "failed"; - private Map> lifecyclebindings = new HashMap>(); + private Map> lifecyclebindings = new HashMap>(); - public ContextHandlerLifeCycle() + public DeploymentGraph() { // Define Default Graph @@ -95,11 +78,11 @@ public ContextHandlerLifeCycle() addNode(new Node(FAILED)); } - public void addBinding(ContextHandlerLifeCycle.Binding binding) + public void addBinding(DeploymentNodeBinding binding) { for (String nodeName : binding.getBindingTargets()) { - List bindings = lifecyclebindings.get(nodeName); + List bindings = lifecyclebindings.get(nodeName); if (bindings == null) { bindings = new ArrayList<>(); @@ -110,11 +93,11 @@ public void addBinding(ContextHandlerLifeCycle.Binding binding) } } - public void removeBinding(ContextHandlerLifeCycle.Binding binding) + public void removeBinding(DeploymentNodeBinding binding) { for (String nodeName : binding.getBindingTargets()) { - List bindings = lifecyclebindings.get(nodeName); + List bindings = lifecyclebindings.get(nodeName); if (bindings != null) bindings.remove(binding); } @@ -125,11 +108,11 @@ public void removeBinding(ContextHandlerLifeCycle.Binding binding) * * @return Set of Object(s) for all lifecycle bindings. never null. */ - public Set getBindings() + public Set getBindings() { - Set boundset = new HashSet<>(); + Set boundset = new HashSet<>(); - for (List bindings : lifecyclebindings.values()) + for (List bindings : lifecyclebindings.values()) { boundset.addAll(bindings); } @@ -143,7 +126,7 @@ public Set getBindings() * @param node the deployment graph node * @return Set of Object(s) for specific lifecycle bindings. never null. */ - public Set getBindings(Node node) + public Set getBindings(Node node) { return getBindings(node.getName()); } @@ -154,12 +137,12 @@ public Set getBindings(Node node) * @param nodeName the node name * @return Set of Object(s) for specific lifecycle bindings. never null. */ - public Set getBindings(String nodeName) + public Set getBindings(String nodeName) { - Set boundset = new HashSet(); + Set boundset = new HashSet(); // Specific node binding - List bindings = lifecyclebindings.get(nodeName); + List bindings = lifecyclebindings.get(nodeName); if (bindings != null) { boundset.addAll(bindings); @@ -177,7 +160,7 @@ public Set getBindings(String nodeName) public void runBindings(Node node, ContextHandler contextHandler, DeploymentManager deploymentManager) throws Throwable { - for (Binding binding : getBindings(node)) + for (DeploymentNodeBinding binding : getBindings(node)) { if (LOG.isDebugEnabled()) LOG.debug("Calling {} for {}", binding.getClass().getName(), contextHandler); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/ScanTrackedApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java similarity index 94% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/ScanTrackedApp.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java index 9eb66cb94b81..5c373afeddfd 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/ScanTrackedApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy.internal; import java.io.IOException; import java.io.InputStream; @@ -25,6 +25,7 @@ import java.util.Properties; import java.util.stream.Collectors; +import org.eclipse.jetty.deploy.DeploymentScanner; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; @@ -35,10 +36,10 @@ /** * A Default represents all the components that make up - * a from-file-system App deployment that the {@link DefaultProvider} + * a from-file-system App deployment that the {@link DeploymentScanner} * creates and uses. */ -public class ScanTrackedApp +public class TrackedPaths { public enum State { @@ -48,14 +49,14 @@ public enum State REMOVED } - private static final Logger LOG = LoggerFactory.getLogger(ScanTrackedApp.class); + private static final Logger LOG = LoggerFactory.getLogger(TrackedPaths.class); private final String name; private final Map paths = new HashMap<>(); private final Attributes attributes = new Attributes.Mapped(); private State state; private ContextHandler contextHandler; - public ScanTrackedApp(String name) + public TrackedPaths(String name) { this.name = name; this.state = calcState(); @@ -74,7 +75,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - ScanTrackedApp that = (ScanTrackedApp)o; + TrackedPaths that = (TrackedPaths)o; return Objects.equals(name, that.name); } @@ -98,6 +99,11 @@ public Environment getEnvironment() return (Environment)getAttributes().getAttribute(DefaultContextHandlerFactory.ENVIRONMENT); } + public void setEnvironment(Environment env) + { + getAttributes().setAttribute(DefaultContextHandlerFactory.ENVIRONMENT, env); + } + public String getEnvironmentName() { Environment env = getEnvironment(); @@ -107,16 +113,11 @@ public String getEnvironmentName() return env.getName(); } - public void setEnvironment(Environment env) - { - getAttributes().setAttribute(DefaultContextHandlerFactory.ENVIRONMENT, env); - } - /** * Get the main path used for deployment. *

      * Applies the heuristics reference in the main - * javadoc for {@link DefaultProvider} + * javadoc for {@link DeploymentScanner} *

      * * @return the main deployable path @@ -295,10 +296,10 @@ public String toString() * * @return the state of the App. */ - private ScanTrackedApp.State calcState() + private State calcState() { if (paths.isEmpty()) - return ScanTrackedApp.State.REMOVED; + return State.REMOVED; // Calculate state of unit from Path states. State ret = null; @@ -333,6 +334,6 @@ else if (ret != State.REMOVED) } } } - return ret != null ? ret : ScanTrackedApp.State.UNCHANGED; + return ret != null ? ret : State.UNCHANGED; } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Edge.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Edge.java similarity index 97% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Edge.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Edge.java index e767e10d42ab..0c20fd248093 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Edge.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Edge.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; /** * Basic Graph Edge diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Graph.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Graph.java similarity index 98% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Graph.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Graph.java index 39e2082cd6b0..cd70b6e7c4f3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Graph.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Graph.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; import java.util.HashSet; import java.util.Set; @@ -245,7 +245,7 @@ public Node getNodeByName(String name) { for (Node node : _nodes) { - if (node.getName().equals(name)) + if (node.getName().equalsIgnoreCase(name)) { return node; } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/GraphOutputDot.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/GraphOutputDot.java similarity index 99% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/GraphOutputDot.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/GraphOutputDot.java index 29be1de3850c..df78dcb8c99a 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/GraphOutputDot.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/GraphOutputDot.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; import java.io.File; import java.io.FileWriter; diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Node.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Node.java similarity index 96% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Node.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Node.java index cab2fcaf9d60..3cefa113dbba 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Node.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Node.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; /** * Basic Graph Node diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Route.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Route.java similarity index 98% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Route.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Route.java index ff357ca3d9ca..1a1955187771 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Route.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/Route.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/package-info.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/package-info.java similarity index 92% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/package-info.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/package-info.java index 0c6bd2a9aeaa..26223cc8f85f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/package-info.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/graph/package-info.java @@ -14,5 +14,5 @@ /** * Jetty Deploy : Deployment Graph */ -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java index 1e4e39a97a2d..2c4507e20ce3 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/DeploymentManagerMBean.java @@ -13,19 +13,11 @@ package org.eclipse.jetty.deploy.jmx; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; -import java.util.stream.Collectors; - import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.jmx.ObjectMBean; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; -import org.eclipse.jetty.util.annotation.Name; @SuppressWarnings("unused") @ManagedObject("MBean Wrapper for DeploymentManager") @@ -39,44 +31,16 @@ public DeploymentManagerMBean(Object managedObject) _manager = (DeploymentManager)managedObject; } - @ManagedAttribute(value = "list ContextHandlers being tracked") - public Collection getContextHandler() - { - return _manager.getContextHandlers() - .stream() - .map(Objects::toString) - .toList(); - } - @ManagedOperation(value = "list ContextHandlers that are located at specified ContextHandlerLifeCycle nodes", impact = "ACTION") - public Collection getContext(@Name("nodeName") String nodeName) - { - Node node = _manager.getLifeCycle().getNodeByName(nodeName); - if (node == null) - { - throw new IllegalArgumentException("Unable to find node [" + nodeName + "]"); - } - - return _manager.getContextHandlers(node) - .stream() - .map(Objects::toString) - .toList(); - } - - @ManagedOperation(value = "list nodes that are tracked by DeploymentManager", impact = "INFO") - public Collection getNodes() - { - return _manager.getNodes().stream().map(Node::getName).collect(Collectors.toList()); - } - - public Collection getContexts() throws Exception + public void requestContextHandlerGoal(String mbeanRef, String nodeName) { - return Collections.unmodifiableCollection(_manager.getContextHandlers()); + ContextHandler contextHandler = findContextHandlerByMBeanRef(mbeanRef); + _manager.move(contextHandler, nodeName); } - @ManagedOperation(value = "list ContextHandlers that are located at specified ContextHandlerLifeCycle nodes", impact = "ACTION") - public void requestContextHandlerGoal(String id, String nodeName) + private ContextHandler findContextHandlerByMBeanRef(String mbeanRef) { - _manager.requestContextHandlerGoal(id, nodeName); + // TODO: figure out how to do this + return null; } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployAction.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployAction.java deleted file mode 100644 index 4aa743ed7bf2..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployAction.java +++ /dev/null @@ -1,50 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.scan; - -/** - *

      Represents a single step in one update as a result of a scanner event.

      - */ -public class DeployAction -{ - public enum Type - { - REMOVE, - ADD; - } - - private final Type type; - private final ScanTrackedApp app; - - public DeployAction(Type type, ScanTrackedApp app) - { - this.type = type; - this.app = app; - } - - public String getName() - { - return app.getName(); - } - - public ScanTrackedApp getApp() - { - return app; - } - - public Type getType() - { - return type; - } -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployActionComparator.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployActionComparator.java deleted file mode 100644 index 868f4cda2a9d..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/DeployActionComparator.java +++ /dev/null @@ -1,57 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.scan; - -import java.util.Comparator; - -/** - *

      The List of {@link DeployAction} sort.

      - * - *
        - *
      • {@link DeployAction#getType()} is sorted by all {@link DeployAction.Type#REMOVE} - * actions first, followed by all {@link DeployAction.Type#ADD} actions.
      • - *
      • {@link DeployAction.Type#REMOVE} type are in descending alphabetically order.
      • - *
      • {@link DeployAction.Type#ADD} type are in ascending alphabetically order.
      • - *
      > - */ -public class DeployActionComparator implements Comparator -{ - private final Comparator typeComparator; - private final Comparator basenameComparator; - - public DeployActionComparator() - { - typeComparator = Comparator.comparing(DeployAction::getType); - basenameComparator = Comparator.comparing(DeployAction::getName); - } - - @Override - public int compare(DeployAction o1, DeployAction o2) - { - int diff = typeComparator.compare(o1, o2); - if (diff != 0) - return diff; - return switch (o1.getType()) - { - case REMOVE -> - { - yield basenameComparator.compare(o2, o1); - } - case ADD -> - { - yield basenameComparator.compare(o1, o2); - } - }; - } -} diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/package-info.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/package-info.java deleted file mode 100644 index aec10110ba2d..000000000000 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/scan/package-info.java +++ /dev/null @@ -1,18 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -/** - * Jetty Deploy : Default Hot-Deploy / Scanner Implementation - */ -package org.eclipse.jetty.deploy.scan; - diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerManagement.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java similarity index 51% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerManagement.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java index 82b05125bf9b..02b6a2b2e2e9 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerManagement.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java @@ -13,20 +13,34 @@ package org.eclipse.jetty.deploy; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; /** - * Deployment Manager specific methods that manage ContextHandler. + * Abstract ContextHandlerDeployer for testing purposes */ -public interface ContextHandlerManagement +public class AbstractContextHandlerDeployer implements ContextHandlerDeployer { - Server getServer(); + @Override + public void deploy(ContextHandler contextHandler) + { + // no default implementation + } - // TODO: document methods - void addContextHandler(ContextHandler contextHandler, String goalName); + @Override + public void undeploy(ContextHandler contextHandler) + { + // no default implementation + } - void requestContextHandlerGoal(ContextHandler contextHandler, String goalName); + @Override + public void addUndeployed(ContextHandler contextHandler) + { + // no default implementation + } - void removeContextHandler(ContextHandler contextHandler, String goalName); -} \ No newline at end of file + @Override + public void move(ContextHandler contextHandler, String goalName) + { + // no default implementation + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index b80341827dc6..3372e2596929 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -19,11 +19,18 @@ import javax.management.MBeanServerConnection; import javax.management.ObjectName; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; +import org.eclipse.jetty.deploy.internal.DeploymentLifeCyclePathCollector; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + public class DeploymentManagerLifeCycleRouteTest { @Test @@ -31,8 +38,7 @@ public void testStateTransitionNewToDeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); - PhonyContextProvider provider = new PhonyContextProvider(depman); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); depman.addLifeCycleBinding(pathtracker); depman.setContexts(new ContextHandlerCollection()); @@ -41,13 +47,21 @@ public void testStateTransitionNewToDeployed() throws Exception depman.start(); // Trigger new ContextHandler - ContextHandler foo = provider.createWebapp("foo-webapp-1.war"); - String id = foo.getID(); - provider.getContextHandlerManagement().addContextHandler(foo, ContextHandlerLifeCycle.UNDEPLOYED); + ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); + depman.addUndeployed(foo); + + // Verify the undeployed state + assertThat("Tracking.size", depman.getContextHandlers().size(), is(1)); + assertThat("ContextHandlerCollection.handlers.size", depman.getContexts().getHandlers().size(), is(0)); + + List undeployedContexts = depman.getContextHandlers(DeploymentGraph.UNDEPLOYED) + .stream() + .toList(); + List expectedContexts = List.of(foo); + assertThat(undeployedContexts, ordered(expectedContexts)); - // Request Deploy of ContextHandler - ContextHandler app = depman.findContextHandler(id); - depman.requestContextHandlerGoal(app, ContextHandlerLifeCycle.DEPLOYED); + // Move to Deployed of ContextHandler + depman.move(foo, DeploymentGraph.DEPLOYED); // Setup Expectations. List expected = new ArrayList<>(); @@ -63,16 +77,15 @@ public void testStateTransitionReceive() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); - PhonyContextProvider provider = new PhonyContextProvider(depman); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); depman.addLifeCycleBinding(pathtracker); // Start DepMan depman.start(); // Create new App - ContextHandler contextHandler = provider.createWebapp("foo-webapp-1.war"); - provider.getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.UNDEPLOYED); + ContextHandler contextHandler = Util.createContextHandler("foo-webapp-1.war"); + depman.addUndeployed(contextHandler); // Perform no goal request. @@ -83,11 +96,11 @@ public void testStateTransitionReceive() throws Exception } @Test + @Disabled("Not working yet, need to figure out how to reference the ContextHandler mbean") public void testStateTransitionDeployedToUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); - ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); - PhonyContextProvider mockProvider = new PhonyContextProvider(depman); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); // Setup JMX MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); @@ -100,13 +113,21 @@ public void testStateTransitionDeployedToUndeployed() throws Exception depman.start(); // Create new ContextHandler - ContextHandler foo = mockProvider.createWebapp("foo-webapp-1"); - String id = foo.getID(); - mockProvider.getContextHandlerManagement().addContextHandler(foo, ContextHandlerLifeCycle.UNDEPLOYED); + ContextHandler foo = Util.createContextHandler("foo-webapp-1"); + depman.addUndeployed(foo); + + // Verify the undeployed state + assertThat("Tracking.size", depman.getContextHandlers().size(), is(1)); + assertThat("ContextHandlerCollection.handlers.size", depman.getContexts().getHandlers().size(), is(0)); + + List undeployedContexts = depman.getContextHandlers(DeploymentGraph.UNDEPLOYED) + .stream() + .toList(); + List expectedContexts = List.of(foo); + assertThat(undeployedContexts, ordered(expectedContexts)); - // Request Deploy of ContextHandler - ContextHandler app = depman.findContextHandler(id); - depman.requestContextHandlerGoal(app, ContextHandlerLifeCycle.DEPLOYED); + // Move to Deployed of ContextHandler + depman.move(foo, DeploymentGraph.DEPLOYED); JmxServiceConnection jmxConnection = new JmxServiceConnection(); jmxConnection.connect(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index ab10ef26e335..f540d8d820a0 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -14,9 +14,13 @@ package org.eclipse.jetty.deploy; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Set; +import org.eclipse.jetty.deploy.internal.DeploymentLifeCyclePathCollector; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -27,21 +31,24 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(WorkDirExtension.class) public class DeploymentManagerTest extends AbstractCleanEnvironmentTest { @Test - public void testAddContext() throws Exception + public void testAddUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); - PhonyContextProvider provider = new PhonyContextProvider(depman); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); depman.addLifeCycleBinding(pathtracker); // Start DepMan @@ -50,20 +57,22 @@ public void testAddContext() throws Exception try { // Trigger new context - ContextHandler foo = provider.createWebapp("foo-webapp-1.war"); - provider.getContextHandlerManagement().addContextHandler(foo, ContextHandlerLifeCycle.UNDEPLOYED); + ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); + depman.addUndeployed(foo); + assertFalse(foo.isStarted()); // Test context tracking - Collection contexts = depman.getContextHandlers(); - assertNotNull(contexts, "Should never be null"); - assertEquals(1, contexts.size(), "Expected Context Count"); - - // Test context find - ContextHandler context = contexts.stream().findFirst().orElse(null); - assertNotNull(context); - ContextHandler actual = depman.findContextHandler(context.getID()); - assertNotNull(actual, "Should have gotten ContextHandler (by id)"); - assertThat(actual.getID(), is("foo-webapp-1")); + Collection contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(1)); + ContextHandler first = contextHandlers.iterator().next(); + assertThat("contextHandler", first, equalTo(foo)); + + // Verify that context is in expected graph node + List undeployedContexts = depman.getContextHandlers("undeployed") + .stream() + .toList(); + List expectedContexts = List.of(foo); + assertThat(undeployedContexts, ordered(expectedContexts)); } finally { @@ -71,18 +80,179 @@ public void testAddContext() throws Exception } } + @Test + public void testDeploy() throws Exception + { + DeploymentManager depman = new DeploymentManager(); + depman.setContexts(new ContextHandlerCollection()); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + depman.addLifeCycleBinding(pathtracker); + + // Start DepMan + depman.start(); + + try + { + // Trigger new context + ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); + depman.deploy(foo); + assertTrue(foo.isStarted()); + + // Test context tracking + Collection contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(1)); + ContextHandler first = contextHandlers.iterator().next(); + assertThat("contextHandler", first, equalTo(foo)); + + // Verify that context is in expected graph node + List startedContexts = depman.getContextHandlers("started") + .stream() + .toList(); + List expectedContexts = List.of(foo); + assertThat(startedContexts, ordered(expectedContexts)); + + // Verify that the graph only has one entry, and it's on started. + List state = getGraphState(depman); + List expected = List.of( + "started|/foo-webapp-1" + ); + assertThat(state, ordered(expected)); + } + finally + { + LifeCycle.stop(depman); + } + } + + @Test + public void testUndeploy() throws Exception + { + DeploymentManager depman = new DeploymentManager(); + depman.setContexts(new ContextHandlerCollection()); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + depman.addLifeCycleBinding(pathtracker); + + // Start DepMan + depman.start(); + + try + { + // Trigger deploy + ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); + depman.deploy(foo); + assertTrue(foo.isStarted()); + + // Test context tracking + Collection contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(1)); + ContextHandler first = contextHandlers.iterator().next(); + assertThat("contextHandler", first, equalTo(foo)); + + List expectedContexts = List.of(foo); + + // Verify that context is in expected graph node + List startedContexts = depman.getContextHandlers("started") + .stream() + .toList(); + assertThat(startedContexts, ordered(expectedContexts)); + + // Trigger undeploy + depman.undeploy(foo); + + // Test context tracking (the context should have been removed) + contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(0)); + + // Verify that the graph is empty now (that was the last context undeployed) + List graphState = getGraphState(depman); + assertTrue(graphState.isEmpty()); + } + finally + { + LifeCycle.stop(depman); + } + } + + @Test + public void testMove() throws Exception + { + DeploymentManager depman = new DeploymentManager(); + depman.setContexts(new ContextHandlerCollection()); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + depman.addLifeCycleBinding(pathtracker); + + // Start DepMan + depman.start(); + + try + { + // Trigger deploy + ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); + depman.deploy(foo); + assertTrue(foo.isStarted()); + + // Test context tracking + Collection contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(1)); + ContextHandler first = contextHandlers.iterator().next(); + assertThat("contextHandler", first, equalTo(foo)); + + List expectedContexts = List.of(foo); + + // Verify that context is in expected graph node + List startedContexts = depman.getContextHandlers("started") + .stream() + .toList(); + assertThat(startedContexts, ordered(expectedContexts)); + + // Trigger undeploy + depman.move(foo, "deployed"); + + // Test context tracking (the context should have been removed) + contextHandlers = depman.getContextHandlers(); + assertThat("contextHandlers.size", contextHandlers.size(), is(1)); + first = contextHandlers.iterator().next(); + assertThat("contextHandler", first, equalTo(foo)); + + // Verify that the graph only has one entry, and it's on deployed. + List state = getGraphState(depman); + List expected = List.of( + "deployed|/foo-webapp-1" + ); + assertThat(state, ordered(expected)); + } + finally + { + LifeCycle.stop(depman); + } + } + + private List getGraphState(DeploymentManager depman) + { + List state = new ArrayList<>(); + for (String nodeName : depman.getNodeNames()) + { + for (ContextHandler contextHandler : depman.getContextHandlers(nodeName)) + { + state.add("%s|%s".formatted(nodeName, contextHandler.getContextPath())); + } + } + Collections.sort(state); + return state; + } + @Test public void testBinding() { - ContextHandlerLifeCyclePathCollector pathtracker = new ContextHandlerLifeCyclePathCollector(); + DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); DeploymentManager depman = new DeploymentManager(); depman.addLifeCycleBinding(pathtracker); - Set allbindings = depman.getLifeCycle().getBindings(); + Set allbindings = depman.getBindings(); assertNotNull(allbindings, "All Bindings should never be null"); assertEquals(1, allbindings.size(), "All Bindings.size"); - Set deploybindings = depman.getLifeCycle().getBindings("deploying"); + Set deploybindings = depman.getBindings("deploying"); assertNotNull(deploybindings, "'deploying' Bindings should not be null"); assertEquals(1, deploybindings.size(), "'deploying' Bindings.size"); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java similarity index 92% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java index 660f4548900a..51988a4c9b9f 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderCoreWebappTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy; import java.io.IOException; import java.net.HttpURLConnection; @@ -23,8 +23,6 @@ import java.util.HashMap; import java.util.Map; -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -47,7 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(WorkDirExtension.class) -public class DefaultProviderCoreWebappTest extends AbstractCleanEnvironmentTest +public class DeploymentScannerCoreWebappTest extends AbstractCleanEnvironmentTest { public WorkDir workDir; private Server server; @@ -107,9 +105,9 @@ public void testExampleCoreDir() throws Exception Files.writeString(demoXml, demoXmlStr); DeploymentManager deploymentManager = new DeploymentManager(); - DefaultProvider defaultProvider = new DefaultProvider(deploymentManager); + DeploymentScanner defaultProvider = new DeploymentScanner(server, deploymentManager); defaultProvider.addMonitoredDirectory(webapps); - DefaultProvider.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); + DeploymentScanner.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); deploymentManager.addBean(defaultProvider); startServer(deploymentManager); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java similarity index 91% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderDeferredStartupTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java index 2ed8cd843e3a..51ce262efe6a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy; import java.nio.file.Files; import java.nio.file.Path; @@ -21,8 +21,6 @@ import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -44,10 +42,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Tests {@link DefaultProvider} behaviors when in Deferred Startup mode + * Tests {@link DeploymentScanner} behaviors when in Deferred Startup mode */ @ExtendWith(WorkDirExtension.class) -public class DefaultProviderDeferredStartupTest extends AbstractCleanEnvironmentTest +public class DeploymentScannerDeferredStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; @@ -98,7 +96,7 @@ public void lifeCycleStarted(LifeCycle event) { eventQueue.add("Server started"); } - if (event instanceof DefaultProvider) + if (event instanceof DeploymentScanner) { eventQueue.add("ScanningAppProvider started"); } @@ -112,8 +110,8 @@ public void lifeCycleStarted(LifeCycle event) server.addEventListener(eventCaptureListener); DeploymentManager deploymentManager = server.getBean(DeploymentManager.class); - DefaultProvider defaultProvider = deploymentManager.getBean(DefaultProvider.class); - assertNotNull(defaultProvider, "Should have found DefaultProvider"); + DeploymentScanner defaultProvider = deploymentManager.getBean(DeploymentScanner.class); + assertNotNull(defaultProvider, "Should have found DeploymentScanner"); assertTrue(defaultProvider.isDeferInitialScan(), "The DeferInitialScan configuration should be true"); defaultProvider.addEventListener(eventCaptureListener); @@ -128,13 +126,13 @@ public void beanAdded(Container parent, Object child) scanner.addListener(new Scanner.ScanCycleListener() { @Override - public void scanStarted(int cycle) throws Exception + public void scanStarted(int cycle) { eventQueue.add("Scan Started [" + cycle + "]"); } @Override - public void scanEnded(int cycle) throws Exception + public void scanEnded(int cycle) { eventQueue.add("Scan Ended [" + cycle + "]"); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java new file mode 100644 index 000000000000..99b7880bfdc4 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java @@ -0,0 +1,198 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jetty.deploy.DeploymentScanner.DeployAction; +import org.eclipse.jetty.deploy.internal.TrackedPaths; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class DeploymentScannerDeployActionComparatorTest +{ + @Test + public void testAddOnly() + { + TrackedPaths appFoo = new TrackedPaths("foo"); + appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.ADDED); + TrackedPaths appBar = new TrackedPaths("bar"); + appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.ADDED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.ADD, "bar")); + actions.add(new DeployAction(DeployAction.Type.ADD, "foo")); + + actions.sort(new DeploymentScanner.DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expected in ascending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("bar")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("foo")); + } + + @Test + public void testRemoveOnly() + { + TrackedPaths appFoo = new TrackedPaths("foo"); + appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.REMOVED); + + TrackedPaths appBar = new TrackedPaths("bar"); + appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.REMOVED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.REMOVE, "foo")); + actions.add(new DeployAction(DeployAction.Type.REMOVE, "bar")); + + actions.sort(new DeploymentScanner.DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expected in descending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("foo")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("bar")); + } + + @Test + public void testRemoveTwoAndAddTwo() + { + TrackedPaths appFoo = new TrackedPaths("foo"); + appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.REMOVED); + + TrackedPaths appBar = new TrackedPaths("bar"); + appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.REMOVED); + + List actions = new ArrayList<>(); + actions.add(new DeployAction(DeployAction.Type.REMOVE, "foo")); + actions.add(new DeployAction(DeployAction.Type.ADD, "foo")); + actions.add(new DeployAction(DeployAction.Type.ADD, "bar")); + actions.add(new DeployAction(DeployAction.Type.REMOVE, "bar")); + + // Perform sort + actions.sort(new DeploymentScanner.DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expecting REMOVE first + + // REMOVE is in descending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("foo")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("bar")); + + // expecting ADD next + + // ADD is in ascending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("bar")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("foo")); + } + + @Test + public void testRemoveFourAndAddTwo() + { + TrackedPaths appA = new TrackedPaths("app-a"); + appA.putPath(Path.of("app-a.xml"), TrackedPaths.State.REMOVED); + + TrackedPaths appB = new TrackedPaths("app-b"); + appB.putPath(Path.of("app-b.xml"), TrackedPaths.State.REMOVED); + + TrackedPaths appC = new TrackedPaths("app-c"); + appC.putPath(Path.of("app-c.xml"), TrackedPaths.State.REMOVED); + + TrackedPaths appD = new TrackedPaths("app-d"); + appD.putPath(Path.of("app-d.xml"), TrackedPaths.State.REMOVED); + + List actions = new ArrayList<>(); + // app A is going through hot-reload + actions.add(new DeployAction(DeployAction.Type.REMOVE, "app-a")); + actions.add(new DeployAction(DeployAction.Type.ADD, "app-a")); + // app B is being removed + actions.add(new DeployAction(DeployAction.Type.REMOVE, "app-b")); + // app C is being removed + actions.add(new DeployAction(DeployAction.Type.REMOVE, "app-c")); + // app D is going through hot-reload + actions.add(new DeployAction(DeployAction.Type.ADD, "app-d")); + actions.add(new DeployAction(DeployAction.Type.REMOVE, "app-d")); + + assertThat(actions.size(), is(6)); + + // Perform sort + actions.sort(new DeploymentScanner.DeployActionComparator()); + + // Verify order + Iterator iterator = actions.iterator(); + DeployAction action; + + // expecting REMOVE first + + // REMOVE is in descending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("app-d")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("app-c")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("app-b")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.REMOVE)); + assertThat(action.name(), is("app-a")); + + // expecting ADD next + + // ADD is in ascending basename order + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("app-a")); + + action = iterator.next(); + assertThat(action.type(), is(DeployAction.Type.ADD)); + assertThat(action.name(), is("app-d")); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java similarity index 94% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderRuntimeUpdatesTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java index d59af711bb73..ca42ed1791d4 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy; import java.io.IOException; import java.nio.file.Files; @@ -21,8 +21,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; @@ -45,13 +43,13 @@ import static org.junit.jupiter.api.Assertions.assertNotSame; /** - * Similar in scope to {@link DefaultProviderStartupTest}, except is concerned with the modification of existing - * deployed contexts due to incoming changes identified by the {@link DefaultProvider}. + * Similar in scope to {@link DeploymentScannerStartupTest}, except is concerned with the modification of existing + * deployed contexts due to incoming changes identified by the {@link DeploymentScanner}. */ @ExtendWith(WorkDirExtension.class) -public class DefaultProviderRuntimeUpdatesTest extends AbstractCleanEnvironmentTest +public class DeploymentScannerRuntimeUpdatesTest extends AbstractCleanEnvironmentTest { - private static final Logger LOG = LoggerFactory.getLogger(DefaultProviderRuntimeUpdatesTest.class); + private static final Logger LOG = LoggerFactory.getLogger(DeploymentScannerRuntimeUpdatesTest.class); private static XmlConfiguredJetty jetty; private final AtomicInteger _scans = new AtomicInteger(); @@ -95,8 +93,8 @@ public void startJetty() throws Exception // monitor tick DeploymentManager dm = jetty.getServer().getBean(DeploymentManager.class); - Collection defaultProviders = dm.getBeans(DefaultProvider.class); - for (DefaultProvider provider : defaultProviders) + Collection defaultProviders = dm.getBeans(DeploymentScanner.class); + for (DeploymentScanner provider : defaultProviders) { _providerCount++; provider.addScannerListener(new Scanner.ScanCycleListener() @@ -127,7 +125,7 @@ public void waitForDirectoryScan() /** * Test that if a unit (called "simple" has both a war file and xml file), will be * redeployed if the war file is touched (note: the XML is the main deployable path) - * + *

      * This addresses issue https://github.com/jetty/jetty.project/issues/12543 */ @Test diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java similarity index 97% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderStartupTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java index 919c961b9c72..3fb1f7097d44 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -20,8 +20,7 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.BarContextHandler; +import org.eclipse.jetty.deploy.internal.DefaultContextHandlerFactory; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; @@ -40,10 +39,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; /** - * Tests {@link DefaultProvider} as it starts up for the first time. + * Tests {@link DeploymentScanner} as it starts up for the first time. */ @ExtendWith(WorkDirExtension.class) -public class DefaultProviderStartupTest extends AbstractCleanEnvironmentTest +public class DeploymentScannerStartupTest extends AbstractCleanEnvironmentTest { public WorkDir testdir; private static XmlConfiguredJetty jetty; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java new file mode 100644 index 000000000000..17bda0715550 --- /dev/null +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java @@ -0,0 +1,395 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.deploy; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.eclipse.jetty.deploy.DeploymentScanner.DeployAction; +import org.eclipse.jetty.deploy.internal.TrackedPaths; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.Scanner; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +@ExtendWith(WorkDirExtension.class) +public class DeploymentScannerTest extends AbstractCleanEnvironmentTest +{ + public WorkDir workDir; + + public static class AssertActionListDeploymentScanner extends DeploymentScanner + { + Consumer> assertActionList; + + public AssertActionListDeploymentScanner() + { + super(null, new AbstractContextHandlerDeployer()); + } + + @Override + protected void performActions(List actions) + { + assertActionList.accept(actions); + + // Perform post performActions cleanup that normally happens + for (DeployAction action : actions) + { + resetTrackedState(action.name()); + } + } + + public TrackedPaths findTracked(String name) + { + return super.findTracked(name); + } + } + + @Test + public void testActionListNewXmlOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + + AssertActionListDeploymentScanner deploymentScanner = new AssertActionListDeploymentScanner(); + deploymentScanner.addMonitoredDirectory(dir); + + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths", app.getPaths().keySet(), Matchers.contains(xml)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlThenRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("foo.xml"); + Files.writeString(xml, "XML for foo", UTF_8); + + AssertActionListDeploymentScanner deploymentScanner = new AssertActionListDeploymentScanner(); + deploymentScanner.addMonitoredDirectory(dir); + + // Initial deployment. + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("foo")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths", app.getPaths().keySet(), contains(xml)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + + // Removed only deployment file. + Files.deleteIfExists(xml); + changeSet.clear(); + changeSet.put(xml, Scanner.Notification.REMOVED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("foo")); + assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths", app.getPaths().keySet(), contains(xml)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.mainPath", app.getMainPath(), is(nullValue())); + }; + + deploymentScanner.pathsChanged(changeSet); + } + + @Test + public void testActionListNewXmlAndWarOnly() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDeploymentScanner deploymentScanner = new AssertActionListDeploymentScanner(); + deploymentScanner.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlAndWarWithXmlUpdate() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDeploymentScanner deploymentScanner = new AssertActionListDeploymentScanner(); + deploymentScanner.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + + // Change/Touch war + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + } + + @Test + public void testActionListXmlAndWarWithXmlRemoved() throws IOException + { + Path dir = workDir.getEmptyPathDir(); + Path xml = dir.resolve("bar.xml"); + Files.writeString(xml, "XML for bar", UTF_8); + Path war = dir.resolve("bar.war"); + Files.writeString(war, "WAR for bar", UTF_8); + + AssertActionListDeploymentScanner deploymentScanner = new AssertActionListDeploymentScanner(); + deploymentScanner.addMonitoredDirectory(dir); + + // Initial deployment + Map changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.ADDED); + changeSet.put(war, Scanner.Notification.ADDED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + + // Change/Touch war and xml + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.CHANGED); + changeSet.put(xml, Scanner.Notification.CHANGED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(xml)); + }; + + deploymentScanner.pathsChanged(changeSet); + + // Delete XML (now only war exists) + Files.deleteIfExists(xml); + changeSet = new HashMap<>(); + changeSet.put(xml, Scanner.Notification.REMOVED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(2)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(war)); + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); + app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.mainPath", app.getMainPath(), is(war)); + }; + + deploymentScanner.pathsChanged(changeSet); + + // Delete WAR + Files.deleteIfExists(war); + changeSet = new HashMap<>(); + changeSet.put(war, Scanner.Notification.REMOVED); + + deploymentScanner.assertActionList = (actions) -> + { + assertThat("actions.size", actions.size(), is(1)); + Iterator iterator = actions.iterator(); + DeployAction action; + + action = iterator.next(); + assertThat("action.name", action.name(), is("bar")); + assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); + TrackedPaths app = deploymentScanner.findTracked(action.name()); + assertThat("action.app.state", app.getState(), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(war)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.mainPath", app.getMainPath(), is(nullValue())); + }; + + deploymentScanner.pathsChanged(changeSet); + } +} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/PhonyContextProvider.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/Util.java similarity index 56% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/PhonyContextProvider.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/Util.java index 45966653f22a..d858723d9ed1 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/PhonyContextProvider.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/Util.java @@ -16,52 +16,26 @@ import java.nio.file.Path; import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.component.AbstractLifeCycle; -import org.eclipse.jetty.util.component.Environment; -public class PhonyContextProvider extends AbstractLifeCycle +/** + * Just some utility methods for Tests + */ +public class Util { - private final ContextHandlerManagement contextHandlerManagement; - private Path webappsDir; - - public PhonyContextProvider(ContextHandlerManagement contextHandlerManagement) - { - this.contextHandlerManagement = contextHandlerManagement; - } - - public String getEnvironmentName() - { - return Environment.ensure("phony").getName(); - } - - public ContextHandlerManagement getContextHandlerManagement() - { - return contextHandlerManagement; - } - - @Override - public void doStart() - { - this.webappsDir = MavenTestingUtils.getTestResourcePathDir("webapps"); - } - - public ContextHandler createWebapp(String name) + public static ContextHandler createContextHandler(String name) { String basename = FileID.getBasename(name); ContextHandler contextHandler = new ContextHandler(); - contextHandler.setID(basename); + contextHandler.setContextPath("/" + basename); return contextHandler; } - public ContextHandler createContextHandler(String name) + public static ContextHandler createContextHandler(Path war) { ContextHandler contextHandler = new ContextHandler(); - Path war = webappsDir.resolve(name + ".war"); - - String contextPath = war.toString(); + String contextPath = war.getFileName().toString(); if (FileID.isWebArchive(war)) { @@ -82,13 +56,6 @@ public ContextHandler createContextHandler(String name) contextPath = contextPath.substring(0, contextPath.length() - 1); contextHandler.setContextPath(contextPath); - return contextHandler; } - - @Override - public String toString() - { - return String.format("%s@%x:%s", this.getClass().getSimpleName(), hashCode(), getEnvironmentName()); - } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java similarity index 86% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java index 06a35479807a..c183a7d7b15a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCyclePathCollector.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java @@ -11,12 +11,14 @@ // ======================================================================== // -package org.eclipse.jetty.deploy; +package org.eclipse.jetty.deploy.internal; import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,9 +26,9 @@ /** * Binds to all lifecycle nodes, and tracks the order of the lifecycle nodes for testing purposes. */ -public class ContextHandlerLifeCyclePathCollector implements ContextHandlerLifeCycle.Binding +public class DeploymentLifeCyclePathCollector implements DeploymentNodeBinding { - List actualOrder = new ArrayList(); + List actualOrder = new ArrayList<>(); public void clear() { diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycleTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java similarity index 91% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycleTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java index ea78377f2567..691942ceaf5b 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/ContextHandlerLifeCycleTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy; +package org.eclipse.jetty.deploy.internal; import java.io.File; import java.io.IOException; @@ -19,9 +19,9 @@ import java.util.ArrayList; import java.util.List; -import org.eclipse.jetty.deploy.graph.GraphOutputDot; -import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.graph.Route; +import org.eclipse.jetty.deploy.internal.graph.GraphOutputDot; +import org.eclipse.jetty.deploy.internal.graph.Node; +import org.eclipse.jetty.deploy.internal.graph.Route; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.junit.jupiter.api.Test; @@ -34,14 +34,14 @@ * Just an overly picky test case to validate the potential paths. */ @ExtendWith(WorkDirExtension.class) -public class ContextHandlerLifeCycleTest +public class DeploymentLifeCycleTest { private void assertNoPath(String from, String to) { assertPath(from, to, new ArrayList<>()); } - private void assertPath(ContextHandlerLifeCycle lifecycle, String from, String to, List expected) + private void assertPath(DeploymentGraph lifecycle, String from, String to, List expected) { Node fromNode = lifecycle.getNodeByName(from); Node toNode = lifecycle.getNodeByName(to); @@ -75,7 +75,7 @@ private void assertPath(ContextHandlerLifeCycle lifecycle, String from, String t private void assertPath(String from, String to, List expected) { - ContextHandlerLifeCycle lifecycle = new ContextHandlerLifeCycle(); + DeploymentGraph lifecycle = new DeploymentGraph(); assertPath(lifecycle, from, to, expected); } @@ -141,7 +141,7 @@ public void testFindPathUndeployedUnavailable() /** * Request multiple lifecycle paths with a single lifecycle instance. Just to ensure that there is no state - * maintained between {@link ContextHandlerLifeCycle#getPath(Node, Node)} requests. + * maintained between {@link DeploymentGraph#getPath(Node, Node)} requests. * * @throws IOException on test failure */ @@ -149,7 +149,7 @@ public void testFindPathUndeployedUnavailable() public void testFindPathMultiple(WorkDir workDir) throws IOException { Path tmpPath = workDir.getEmptyPathDir(); - ContextHandlerLifeCycle lifecycle = new ContextHandlerLifeCycle(); + DeploymentGraph lifecycle = new DeploymentGraph(); List expected = new ArrayList<>(); File outputDir = tmpPath.toFile(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java similarity index 53% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java index 0199ad27f5c3..2a528565c082 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/ScanTrackedAppTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.scan; +package org.eclipse.jetty.deploy.internal; import java.io.IOException; import java.nio.file.Files; @@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class ScanTrackedAppTest +public class TrackedPathsTest { public WorkDir workDir; @@ -45,8 +45,8 @@ public void testMainPathOnlyXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); - ScanTrackedApp app = new ScanTrackedApp("bar"); - app.putPath(xml, ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("bar"); + app.putPath(xml, TrackedPaths.State.UNCHANGED); Path main = app.getMainPath(); @@ -67,9 +67,9 @@ public void testMainPathXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - ScanTrackedApp app = new ScanTrackedApp("bar"); - app.putPath(xml, ScanTrackedApp.State.UNCHANGED); - app.putPath(war, ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("bar"); + app.putPath(xml, TrackedPaths.State.UNCHANGED); + app.putPath(war, TrackedPaths.State.UNCHANGED); Path main = app.getMainPath(); @@ -90,9 +90,9 @@ public void testMainPathDirAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - ScanTrackedApp app = new ScanTrackedApp("bar"); - app.putPath(appDir, ScanTrackedApp.State.UNCHANGED); - app.putPath(war, ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("bar"); + app.putPath(appDir, TrackedPaths.State.UNCHANGED); + app.putPath(war, TrackedPaths.State.UNCHANGED); Path main = app.getMainPath(); @@ -113,9 +113,9 @@ public void testMainPathDirAndXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); - ScanTrackedApp app = new ScanTrackedApp("bar"); - app.putPath(appDir, ScanTrackedApp.State.UNCHANGED); - app.putPath(xml, ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("bar"); + app.putPath(appDir, TrackedPaths.State.UNCHANGED); + app.putPath(xml, TrackedPaths.State.UNCHANGED); Path main = app.getMainPath(); @@ -138,10 +138,10 @@ public void testMainPathDirAndXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - ScanTrackedApp app = new ScanTrackedApp("bar"); - app.putPath(appDir, ScanTrackedApp.State.UNCHANGED); - app.putPath(xml, ScanTrackedApp.State.UNCHANGED); - app.putPath(war, ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("bar"); + app.putPath(appDir, TrackedPaths.State.UNCHANGED); + app.putPath(xml, TrackedPaths.State.UNCHANGED); + app.putPath(war, TrackedPaths.State.UNCHANGED); Path main = app.getMainPath(); @@ -151,107 +151,107 @@ public void testMainPathDirAndXmlAndWar() throws IOException @Test public void testStateUnchanged() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.UNCHANGED); - app.putPath(Path.of("test-b"), ScanTrackedApp.State.UNCHANGED); + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); + app.putPath(Path.of("test-b"), TrackedPaths.State.UNCHANGED); - assertThat(app.getState(), is(ScanTrackedApp.State.UNCHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.UNCHANGED)); } @Test public void testStateInitialEmpty() { - ScanTrackedApp app = new ScanTrackedApp("test"); + TrackedPaths app = new TrackedPaths("test"); // intentionally empty of Paths - assertThat(app.getState(), is(ScanTrackedApp.State.REMOVED)); + assertThat(app.getState(), is(TrackedPaths.State.REMOVED)); } @Test public void testStatePutThenRemoveAll() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.ADDED); - assertThat(app.getState(), is(ScanTrackedApp.State.ADDED)); + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.ADDED); + assertThat(app.getState(), is(TrackedPaths.State.ADDED)); // Now it gets flagged as removed. (eg: by a Scanner change) - app.putPath(Path.of("test-a"), ScanTrackedApp.State.REMOVED); + app.putPath(Path.of("test-a"), TrackedPaths.State.REMOVED); // Then it gets processed, which results in a state reset. app.resetStates(); // The resulting app should have no paths, and be flagged as removed. assertThat(app.getPaths().size(), is(0)); - assertThat(app.getState(), is(ScanTrackedApp.State.REMOVED)); + assertThat(app.getState(), is(TrackedPaths.State.REMOVED)); } @Test public void testStateAddedOnly() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.ADDED); - app.putPath(Path.of("test-b"), ScanTrackedApp.State.ADDED); + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.ADDED); + app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); - assertThat(app.getState(), is(ScanTrackedApp.State.ADDED)); + assertThat(app.getState(), is(TrackedPaths.State.ADDED)); } @Test public void testStateAddedRemoved() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.REMOVED); // existing file removed in this scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.ADDED); // new file introduced in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.REMOVED); // existing file removed in this scan + app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } @Test public void testStateAddedChanged() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.CHANGED); // existing file changed in this scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.ADDED); // new file introduced in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.CHANGED); // existing file changed in this scan + app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } @Test public void testStateUnchangedAdded() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.ADDED); // new file introduced in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } @Test public void testStateUnchangedChanged() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.CHANGED); // existing file changed in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), TrackedPaths.State.CHANGED); // existing file changed in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } @Test public void testStateUnchangedRemoved() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.REMOVED); // existing file removed in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), TrackedPaths.State.REMOVED); // existing file removed in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } @Test public void testStateUnchangedRemovedAdded() { - ScanTrackedApp app = new ScanTrackedApp("test"); - app.putPath(Path.of("test-a"), ScanTrackedApp.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), ScanTrackedApp.State.REMOVED); // existing file changed in this scan event - app.putPath(Path.of("test-c"), ScanTrackedApp.State.ADDED); // new file introduced in this scan event + TrackedPaths app = new TrackedPaths("test"); + app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), TrackedPaths.State.REMOVED); // existing file changed in this scan event + app.putPath(Path.of("test-c"), TrackedPaths.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(ScanTrackedApp.State.CHANGED)); + assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/graph/GraphTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/graph/GraphTest.java similarity index 98% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/graph/GraphTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/graph/GraphTest.java index 6f6145b66194..11c99d773eb5 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/graph/GraphTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/graph/GraphTest.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.deploy.graph; +package org.eclipse.jetty.deploy.internal.graph; import org.junit.jupiter.api.Test; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderTest.java deleted file mode 100644 index e7ff979755dc..000000000000 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DefaultProviderTest.java +++ /dev/null @@ -1,404 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.scan; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import org.eclipse.jetty.deploy.AbstractCleanEnvironmentTest; -import org.eclipse.jetty.deploy.ContextHandlerManagement; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; -import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -import org.eclipse.jetty.util.Scanner; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -@ExtendWith(WorkDirExtension.class) -public class DefaultProviderTest extends AbstractCleanEnvironmentTest -{ - public WorkDir workDir; - - public static class AssertActionListDefaultProvider extends DefaultProvider - { - Consumer> assertActionList; - - public AssertActionListDefaultProvider() - { - super(new ContextHandlerManagement() - { - @Override - public Server getServer() - { - return null; - } - - @Override - public void addContextHandler(ContextHandler contextHandler, String goalName) - { - // ignore - } - - @Override - public void requestContextHandlerGoal(ContextHandler contextHandler, String goalName) - { - // ignore - } - - @Override - public void removeContextHandler(ContextHandler contextHandler, String goalName) - { - // ignore - } - }); - } - - @Override - protected void performActions(List actions) - { - assertActionList.accept(actions); - - // Perform post performActions cleanup that normally happens - for (DeployAction action : actions) - { - action.getApp().resetStates(); - } - } - } - - @Test - public void testActionListNewXmlOnly() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - - AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); - defaultProvider.addMonitoredDirectory(dir); - - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), Matchers.contains(xml)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - } - - @Test - public void testActionListXmlThenRemoved() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("foo.xml"); - Files.writeString(xml, "XML for foo", UTF_8); - - AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); - defaultProvider.addMonitoredDirectory(dir); - - // Initial deployment. - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("foo")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), contains(xml)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - - // Removed only deployment file. - Files.deleteIfExists(xml); - changeSet.clear(); - changeSet.put(xml, Scanner.Notification.REMOVED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("foo")); - assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), contains(xml)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(nullValue())); - }; - - defaultProvider.pathsChanged(changeSet); - } - - @Test - public void testActionListNewXmlAndWarOnly() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); - defaultProvider.addMonitoredDirectory(dir); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - } - - @Test - public void testActionListXmlAndWarWithXmlUpdate() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); - defaultProvider.addMonitoredDirectory(dir); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - - // Change/Touch war - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.CHANGED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(2)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.UNCHANGED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.UNCHANGED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - } - - @Test - public void testActionListXmlAndWarWithXmlRemoved() throws IOException - { - Path dir = workDir.getEmptyPathDir(); - Path xml = dir.resolve("bar.xml"); - Files.writeString(xml, "XML for bar", UTF_8); - Path war = dir.resolve("bar.war"); - Files.writeString(war, "WAR for bar", UTF_8); - - AssertActionListDefaultProvider defaultProvider = new AssertActionListDefaultProvider(); - defaultProvider.addMonitoredDirectory(dir); - - // Initial deployment - Map changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.ADDED); - changeSet.put(war, Scanner.Notification.ADDED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.ADDED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - - // Change/Touch war and xml - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.CHANGED); - changeSet.put(xml, Scanner.Notification.CHANGED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(2)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(xml)); - }; - - defaultProvider.pathsChanged(changeSet); - - // Delete XML (now only war exists) - Files.deleteIfExists(xml); - changeSet = new HashMap<>(); - changeSet.put(xml, Scanner.Notification.REMOVED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(2)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.UNCHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(war)); - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.ADD)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.CHANGED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", action.getApp().getPaths().get(xml), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.UNCHANGED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(war)); - }; - - defaultProvider.pathsChanged(changeSet); - - // Delete WAR - Files.deleteIfExists(war); - changeSet = new HashMap<>(); - changeSet.put(war, Scanner.Notification.REMOVED); - - defaultProvider.assertActionList = (actions) -> - { - assertThat("actions.size", actions.size(), is(1)); - Iterator iterator = actions.iterator(); - DeployAction action; - - action = iterator.next(); - assertThat("action.name", action.getName(), is("bar")); - assertThat("action.type", action.getType(), is(DeployAction.Type.REMOVE)); - assertThat("action.app.state", action.getApp().getState(), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.paths", action.getApp().getPaths().keySet(), containsInAnyOrder(war)); - assertThat("action.app.paths[war].state", action.getApp().getPaths().get(war), is(ScanTrackedApp.State.REMOVED)); - assertThat("action.app.mainPath", action.getApp().getMainPath(), is(nullValue())); - }; - - defaultProvider.pathsChanged(changeSet); - } -} diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DeployActionComparatorTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DeployActionComparatorTest.java deleted file mode 100644 index 7ebc6a58035d..000000000000 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/scan/DeployActionComparatorTest.java +++ /dev/null @@ -1,196 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.deploy.scan; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -public class DeployActionComparatorTest -{ - @Test - public void testAddOnly() - { - ScanTrackedApp appFoo = new ScanTrackedApp("foo"); - appFoo.putPath(Path.of("foo.xml"), ScanTrackedApp.State.ADDED); - ScanTrackedApp appBar = new ScanTrackedApp("bar"); - appBar.putPath(Path.of("bar.xml"), ScanTrackedApp.State.ADDED); - - List actions = new ArrayList<>(); - actions.add(new DeployAction(DeployAction.Type.ADD, new ScanTrackedApp("bar"))); - actions.add(new DeployAction(DeployAction.Type.ADD, new ScanTrackedApp("foo"))); - - actions.sort(new DeployActionComparator()); - - // Verify order - Iterator iterator = actions.iterator(); - DeployAction action; - - // expected in ascending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("bar")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("foo")); - } - - @Test - public void testRemoveOnly() - { - ScanTrackedApp appFoo = new ScanTrackedApp("foo"); - appFoo.putPath(Path.of("foo.xml"), ScanTrackedApp.State.REMOVED); - - ScanTrackedApp appBar = new ScanTrackedApp("bar"); - appBar.putPath(Path.of("bar.xml"), ScanTrackedApp.State.REMOVED); - - List actions = new ArrayList<>(); - actions.add(new DeployAction(DeployAction.Type.REMOVE, appFoo)); - actions.add(new DeployAction(DeployAction.Type.REMOVE, appBar)); - - actions.sort(new DeployActionComparator()); - - // Verify order - Iterator iterator = actions.iterator(); - DeployAction action; - - // expected in descending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("foo")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("bar")); - } - - @Test - public void testRemoveTwoAndAddTwo() - { - ScanTrackedApp appFoo = new ScanTrackedApp("foo"); - appFoo.putPath(Path.of("foo.xml"), ScanTrackedApp.State.REMOVED); - - ScanTrackedApp appBar = new ScanTrackedApp("bar"); - appBar.putPath(Path.of("bar.xml"), ScanTrackedApp.State.REMOVED); - - List actions = new ArrayList<>(); - actions.add(new DeployAction(DeployAction.Type.REMOVE, appFoo)); - actions.add(new DeployAction(DeployAction.Type.ADD, appFoo)); - actions.add(new DeployAction(DeployAction.Type.ADD, appBar)); - actions.add(new DeployAction(DeployAction.Type.REMOVE, appBar)); - - // Perform sort - actions.sort(new DeployActionComparator()); - - // Verify order - Iterator iterator = actions.iterator(); - DeployAction action; - - // expecting REMOVE first - - // REMOVE is in descending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("foo")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("bar")); - - // expecting ADD next - - // ADD is in ascending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("bar")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("foo")); - } - - @Test - public void testRemoveFourAndAddTwo() - { - ScanTrackedApp appA = new ScanTrackedApp("app-a"); - appA.putPath(Path.of("app-a.xml"), ScanTrackedApp.State.REMOVED); - - ScanTrackedApp appB = new ScanTrackedApp("app-b"); - appB.putPath(Path.of("app-b.xml"), ScanTrackedApp.State.REMOVED); - - ScanTrackedApp appC = new ScanTrackedApp("app-c"); - appC.putPath(Path.of("app-c.xml"), ScanTrackedApp.State.REMOVED); - - ScanTrackedApp appD = new ScanTrackedApp("app-d"); - appD.putPath(Path.of("app-d.xml"), ScanTrackedApp.State.REMOVED); - - List actions = new ArrayList<>(); - // app A is going through hot-reload - actions.add(new DeployAction(DeployAction.Type.REMOVE, appA)); - actions.add(new DeployAction(DeployAction.Type.ADD, appA)); - // app B is being removed - actions.add(new DeployAction(DeployAction.Type.REMOVE, appB)); - // app C is being removed - actions.add(new DeployAction(DeployAction.Type.REMOVE, appC)); - // app D is going through hot-reload - actions.add(new DeployAction(DeployAction.Type.ADD, appD)); - actions.add(new DeployAction(DeployAction.Type.REMOVE, appD)); - - assertThat(actions.size(), is(6)); - - // Perform sort - actions.sort(new DeployActionComparator()); - - // Verify order - Iterator iterator = actions.iterator(); - DeployAction action; - - // expecting REMOVE first - - // REMOVE is in descending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("app-d")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("app-c")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("app-b")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.REMOVE)); - assertThat(action.getApp().getName(), is("app-a")); - - // expecting ADD next - - // ADD is in ascending basename order - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("app-a")); - - action = iterator.next(); - assertThat(action.getType(), is(DeployAction.Type.ADD)); - assertThat(action.getApp().getName(), is("app-d")); - } -} diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index c7c6e5d55e8c..736eeb3316b1 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -15,7 +15,7 @@ import java.util.Objects; -import org.eclipse.jetty.deploy.ContextHandlerManagement; +import org.eclipse.jetty.deploy.ContextHandlerDeployer; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -35,12 +35,12 @@ */ public abstract class AbstractContextProvider extends AbstractLifeCycle { - private final ContextHandlerManagement _contextHandlerManagement; + private final ContextHandlerDeployer _contextHandlerManagement; private ContextFactory _contextFactory; private String _environment; private final Attributes _attributes = new Attributes.Mapped(); - public AbstractContextProvider(ContextHandlerManagement contextHandlerManagement, String environment, ContextFactory contextFactory) + public AbstractContextProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { _contextHandlerManagement = contextHandlerManagement; _environment = Objects.requireNonNull(environment); @@ -74,7 +74,7 @@ public String getEnvironmentName() return _environment; } - public ContextHandlerManagement getContextHandlerManagement() + public ContextHandlerDeployer getContextHandlerManagement() { return _contextHandlerManagement; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index 5df7bffd4040..dd14d9310cfe 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -23,8 +23,8 @@ import java.util.List; import java.util.Map; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; -import org.eclipse.jetty.deploy.ContextHandlerManagement; +import org.eclipse.jetty.deploy.ContextHandlerDeployer; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; @@ -107,7 +107,7 @@ public void removedBundle(Bundle bundle, BundleEvent event, Object object) } } - public BundleContextProvider(ContextHandlerManagement contextHandlerManagement, String environment, ContextFactory contextFactory) + public BundleContextProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { super(contextHandlerManagement, environment, contextFactory); } @@ -195,7 +195,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception _contextHandlerMap.put(contextHandler.getID(), contextHandler); List contextHandlers = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); contextHandlers.add(contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); + getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); added = true; } @@ -220,7 +220,7 @@ public boolean bundleRemoved(Bundle bundle) { if (_contextHandlerMap.remove(context.getID()) != null) { - getContextHandlerManagement().removeContextHandler(context, ContextHandlerLifeCycle.UNDEPLOYED); + getContextHandlerManagement().removeContextHandler(context, DeploymentGraph.UNDEPLOYED); removed = true; } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index 6901ad6541ea..455585c6ec2f 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -18,8 +18,8 @@ import java.util.Hashtable; import java.util.Map; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; -import org.eclipse.jetty.deploy.ContextHandlerManagement; +import org.eclipse.jetty.deploy.ContextHandlerDeployer; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; @@ -95,7 +95,7 @@ public void removedBundle(Bundle bundle, BundleEvent event, Object object) } } - public BundleWebAppProvider(ContextHandlerManagement contextHandlerManagement, String environment, ContextFactory contextFactory) + public BundleWebAppProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { super(contextHandlerManagement, environment, contextFactory); } @@ -186,7 +186,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, staticResourcesLocation); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); + getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); return true; } @@ -197,7 +197,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); + getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); return true; } @@ -209,7 +209,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, ContextHandlerLifeCycle.STARTED); + getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); return true; } @@ -239,7 +239,7 @@ public boolean bundleRemoved(Bundle bundle) ContextHandler contextHandler = _bundleMap.remove(bundle); if (contextHandler != null) { - getContextHandlerManagement().removeContextHandler(contextHandler, ContextHandlerLifeCycle.UNDEPLOYED); + getContextHandlerManagement().removeContextHandler(contextHandler, DeploymentGraph.UNDEPLOYED); return true; } return false; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyServerFactory.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyServerFactory.java index b78c4c656ebe..729f2e699bfd 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyServerFactory.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyServerFactory.java @@ -24,8 +24,8 @@ import java.util.Objects; import java.util.StringTokenizer; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; import org.eclipse.jetty.deploy.bindings.StandardStarter; import org.eclipse.jetty.deploy.bindings.StandardStopper; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; @@ -157,7 +157,7 @@ public static Server createServer(String name, Dictionary props, //ensure DeploymentManager DeploymentManager deploymentManager = ensureDeploymentManager(server); deploymentManager.setUseStandardBindings(false); - List deploymentLifeCycleBindings = new ArrayList<>(); + List deploymentLifeCycleBindings = new ArrayList<>(); deploymentLifeCycleBindings.add(new OSGiDeployer(server)); deploymentLifeCycleBindings.add(new StandardStarter()); deploymentLifeCycleBindings.add(new StandardStopper()); diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java index 3f201eb53a9e..d66bc2c08fc8 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java @@ -15,7 +15,7 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardDeployer; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java index 77134ae8ba84..fc1ad6caed1a 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java @@ -15,7 +15,7 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardUndeployer; -import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 4b14bf4569d5..e4d70f525821 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -25,10 +25,11 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.scan.DefaultProvider; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.DeploymentScanner; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -102,8 +103,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(deploymentManager); - DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee10"); + DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee10"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee10.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); @@ -350,7 +351,7 @@ public void postConfigure(WebAppContext context) } } - public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements DeploymentNodeBinding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -366,7 +367,7 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) public String[] getBindingTargets() { return new String[]{ - ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + DeploymentGraph.STARTING, DeploymentGraph.STARTED, DeploymentGraph.FAILED }; } @@ -375,15 +376,15 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 952be27627fa..5b8ee5c0197d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index e6ddbe28e54d..a845c1f10ff7 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index 4d6abc6ac254..ba61aa902e90 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -25,10 +25,11 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.scan.DefaultProvider; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.DeploymentScanner; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -102,8 +103,8 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(deploymentManager); - DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee11"); + DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee11"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee11.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); @@ -351,7 +352,7 @@ public void postConfigure(WebAppContext context) } } - public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements DeploymentNodeBinding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -367,7 +368,7 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) public String[] getBindingTargets() { return new String[]{ - ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + DeploymentGraph.STARTING, DeploymentGraph.STARTED, DeploymentGraph.FAILED }; } @@ -376,15 +377,15 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index d48a57e20c8d..8d3d499a969f 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 3e67dd3c5829..71bdd8f44216 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index 3361196f6c3c..4010f8ddb11d 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -25,10 +25,11 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.deploy.ContextHandlerLifeCycle; import org.eclipse.jetty.deploy.DeploymentManager; -import org.eclipse.jetty.deploy.graph.Node; -import org.eclipse.jetty.deploy.scan.DefaultProvider; +import org.eclipse.jetty.deploy.DeploymentNodeBinding; +import org.eclipse.jetty.deploy.DeploymentScanner; +import org.eclipse.jetty.deploy.internal.DeploymentGraph; +import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee9.webapp.AbstractConfiguration; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -97,8 +98,8 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DefaultProvider appProvider = new DefaultProvider(deploymentManager); - DefaultProvider.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee9"); + DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee9"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee9.webapp.WebAppContext"); appProvider.setScanInterval(1); appProvider.addMonitoredDirectory(docroots); @@ -356,7 +357,7 @@ public void postConfigure(WebAppContext context) throws Exception } } - public static class AppLifeCycleTrackingBinding implements ContextHandlerLifeCycle.Binding + public static class AppLifeCycleTrackingBinding implements DeploymentNodeBinding { public final CountDownLatch startingLatch = new CountDownLatch(1); public final CountDownLatch startedLatch = new CountDownLatch(1); @@ -372,7 +373,7 @@ public AppLifeCycleTrackingBinding(String expectedContextPath) public String[] getBindingTargets() { return new String[]{ - ContextHandlerLifeCycle.STARTING, ContextHandlerLifeCycle.STARTED, ContextHandlerLifeCycle.FAILED + DeploymentGraph.STARTING, DeploymentGraph.STARTED, DeploymentGraph.FAILED }; } @@ -381,15 +382,15 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTING)) + if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) { startingLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.STARTED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) { startedLatch.countDown(); } - else if (node.getName().equalsIgnoreCase(ContextHandlerLifeCycle.FAILED)) + else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) { failedLatch.countDown(); } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 28bc3af0bd9b..3a3581a32fc2 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -80,7 +80,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 3e3eca311831..3d60fb3425ba 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + From 95a973a6f2ce54c59779ff6c1472b3a64f1a582a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 11:38:17 -0600 Subject: [PATCH 063/104] Functioning jetty-deploy (again) --- .../src/main/config/etc/jetty-deploy.xml | 4 +- .../src/main/java/module-info.java | 1 + .../jetty/deploy/DeploymentScanner.java | 86 ++++++++----- .../DefaultContextHandlerFactory.java | 4 +- .../{TrackedPaths.java => PathsApp.java} | 8 +- .../DeploymentManagerLifeCycleRouteTest.java | 10 +- .../jetty/deploy/DeploymentManagerTest.java | 117 +++++++++--------- ...mentScannerDeployActionComparatorTest.java | 42 +++---- .../DeploymentScannerRuntimeUpdatesTest.java | 14 +-- .../jetty/deploy/DeploymentScannerTest.java | 102 +++++++-------- ...=> DeploymentGraphNodeOrderCollector.java} | 2 +- ...ycleTest.java => DeploymentGraphTest.java} | 2 +- ...rackedPathsTest.java => PathsAppTest.java} | 112 ++++++++--------- .../resources/jetty-core-deploy-custom.xml | 2 +- .../src/main/config/etc/jetty-core-deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee10-deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee11-deploy.xml | 2 +- .../src/main/config/etc/jetty-ee8-deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/main/config/etc/jetty-ee9-deploy.xml | 2 +- 25 files changed, 280 insertions(+), 248 deletions(-) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/{TrackedPaths.java => PathsApp.java} (98%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/{DeploymentLifeCyclePathCollector.java => DeploymentGraphNodeOrderCollector.java} (96%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/{DeploymentLifeCycleTest.java => DeploymentGraphTest.java} (99%) rename jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/{TrackedPathsTest.java => PathsAppTest.java} (55%) diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 80d492ef7b58..23cca3bed3e5 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -8,7 +8,7 @@ - + @@ -43,7 +43,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/java/module-info.java b/jetty-core/jetty-deploy/src/main/java/module-info.java index 970cbeeedfdb..80db00fd24c6 100644 --- a/jetty-core/jetty-deploy/src/main/java/module-info.java +++ b/jetty-core/jetty-deploy/src/main/java/module-info.java @@ -15,6 +15,7 @@ { requires java.xml; requires org.eclipse.jetty.xml; + requires transitive org.eclipse.jetty.util; requires transitive org.eclipse.jetty.server; requires org.slf4j; diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index 03bd28952196..c6bbaab86232 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -37,7 +37,7 @@ import java.util.stream.Stream; import org.eclipse.jetty.deploy.internal.DefaultContextHandlerFactory; -import org.eclipse.jetty.deploy.internal.TrackedPaths; +import org.eclipse.jetty.deploy.internal.PathsApp; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -92,7 +92,7 @@ * Context Deployment properties will be initialized with: *

      *
        - *
      • The properties set on the application via embedded calls modifying {@link TrackedPaths#getAttributes()}
      • + *
      • The properties set on the application via embedded calls modifying {@link PathsApp#getAttributes()}
      • *
      • The app specific properties file {@code webapps/.properties}
      • *
      • The environment specific properties file {@code webapps/[-zzz].properties}
      • *
      • The {@link Attributes} from the {@link Environment}
      • @@ -122,10 +122,10 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Cha private final FilenameFilter filenameFilter; private final List monitoredDirs = new CopyOnWriteArrayList<>(); private final DefaultContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); + private final Map trackedApps = new HashMap<>(); + private final Map environmentAttributesMap = new HashMap<>(); - private Map trackedPathsMap = new HashMap<>(); private Comparator actionComparator = new DeployActionComparator(); - private Map environmentAttributesMap = new HashMap<>(); private Path environmentsDir; private int scanInterval = 10; private Scanner scanner; @@ -330,19 +330,19 @@ public void pathsChanged(Map changeSet) for (Map.Entry entry : changeSet.entrySet()) { Path path = entry.getKey(); - TrackedPaths.State state = switch (entry.getValue()) + PathsApp.State state = switch (entry.getValue()) { case ADDED -> { - yield TrackedPaths.State.ADDED; + yield PathsApp.State.ADDED; } case CHANGED -> { - yield TrackedPaths.State.CHANGED; + yield PathsApp.State.CHANGED; } case REMOVED -> { - yield TrackedPaths.State.REMOVED; + yield PathsApp.State.REMOVED; } }; @@ -359,7 +359,7 @@ public void pathsChanged(Map changeSet) { // we have a normal path entry changedBaseNames.add(basename); - TrackedPaths app = trackedPathsMap.computeIfAbsent(basename, TrackedPaths::new); + PathsApp app = trackedApps.computeIfAbsent(basename, PathsApp::new); app.putPath(path, state); } else if (isEnvironmentConfigPath(path)) @@ -384,15 +384,15 @@ else if (isEnvironmentConfigPath(path)) // Now we want to convert this list of changes to a DeployAction list // that will perform the add/remove logic in a consistent way. - List changedApps = changedBaseNames + List changedApps = changedBaseNames .stream() - .map(this::findTracked) + .map(this::findApp) .collect(Collectors.toList()); if (!changedEnvironments.isEmpty()) { // We have incoming environment configuration changes - // We need to add any missing ScanTrackedApp that have changed + // We need to add any missing PathsApp that have changed // due to incoming environment configuration changes, // along with loading any ${jetty.base}/environments/-*.properties // into a layer for that Environment. @@ -400,15 +400,15 @@ else if (isEnvironmentConfigPath(path)) for (String changedEnvName : changedEnvironments) { // Add any missing apps to changedApps list - for (TrackedPaths app : trackedPathsMap.values()) + for (PathsApp app : trackedApps.values()) { if (changedBaseNames.contains(app.getName())) continue; // skip app that's already in the change list. if (changedEnvName.equalsIgnoreCase(app.getEnvironmentName())) { - if (app.getState() == TrackedPaths.State.UNCHANGED) - app.setState(TrackedPaths.State.CHANGED); + if (app.getState() == PathsApp.State.UNCHANGED) + app.setState(PathsApp.State.CHANGED); changedApps.add(app); changedBaseNames.add(app.getName()); } @@ -449,17 +449,17 @@ public void scan() scanner.nudge(); } - protected TrackedPaths findTracked(String name) + protected PathsApp findApp(String name) { - return trackedPathsMap.get(name); + return trackedApps.get(name); } - public void resetTrackedState(String name) + public void resetAppState(String name) { - TrackedPaths tracked = findTracked(name); - if (tracked == null) + PathsApp app = findApp(name); + if (app == null) return; - tracked.resetStates(); + app.resetStates(); } @Override @@ -468,13 +468,13 @@ public String toString() return String.format("%s@%x[dirs=%s]", this.getClass(), hashCode(), monitoredDirs); } - protected List buildActionList(List changedApps) + protected List buildActionList(List changedApps) { if (LOG.isDebugEnabled()) LOG.debug("buildActionList: {}", changedApps); List actions = new ArrayList<>(); - for (TrackedPaths app : changedApps) + for (PathsApp app : changedApps) { if (LOG.isDebugEnabled()) LOG.debug("changed app: {}", app); @@ -483,6 +483,8 @@ protected List buildActionList(List changedApps) { case ADDED -> { + // new paths are not being tracked yet. + startTracking(app); actions.add(new DeployAction(DeployAction.Type.ADD, app.getName())); } case CHANGED -> @@ -499,6 +501,16 @@ protected List buildActionList(List changedApps) return sortActions(actions); } + private void startTracking(PathsApp app) + { + trackedApps.put(app.getName(), app); + } + + private void stopTracking(PathsApp app) + { + trackedApps.remove(app.getName()); + } + @Override protected void doStart() throws Exception { @@ -628,20 +640,32 @@ protected boolean isSameDir(Path dirA, Path dirB) protected void performActions(List actions) { + // Track apps that have been removed as a result of executing the + // full set of actions. + Set removedApps = new HashSet<>(); + + // Process each step in the actions list for (DeployAction step : actions) { - TrackedPaths app = findTracked(step.name()); + PathsApp app = findApp(step.name()); + if (app == null) + throw new IllegalStateException("Unable to find app [" + step.name() + "]"); + try { switch (step.type()) { case REMOVE -> { - trackedPathsMap.remove(step.name()); + // Track removal + removedApps.add(app); contextManagement.undeploy(app.getContextHandler()); } case ADD -> { + // Undo tracking for prior removal in this list of actions. + removedApps.remove(app); + // Load .properties into app. app.loadProperties(); @@ -667,8 +691,8 @@ protected void performActions(List actions) ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, app, deployAttributes); app.setContextHandler(contextHandler); - // Introduce the App to the DeploymentManager - trackedPathsMap.put(step.name(), app); + // Introduce the ContextHandler to the DeploymentManager + startTracking(app); contextManagement.deploy(app.getContextHandler()); } } @@ -682,6 +706,12 @@ protected void performActions(List actions) app.resetStates(); } } + + // Fully stop tracking apps that have been removed, but not re-added. + for (PathsApp removed : removedApps) + { + stopTracking(removed); + } } protected List sortActions(List actions) @@ -832,7 +862,7 @@ public record DeployAction(DeployAction.Type type, String name) public enum Type { REMOVE, - ADD; + ADD } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java index 7d4f3e7a17a9..5a1535118dd4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java @@ -79,7 +79,7 @@ public static void setEnvironmentXmlPaths(Attributes attributes, List path * @return the ContextHandler * @throws Exception if unable to create the ContextHandler */ - public ContextHandler newContextHandler(Server server, TrackedPaths app, Attributes deployAttributes) throws Exception + public ContextHandler newContextHandler(Server server, PathsApp app, Attributes deployAttributes) throws Exception { Path mainPath = app.getMainPath(); if (mainPath == null) @@ -382,7 +382,7 @@ private ContextHandler getContextHandler(Object context) * @return the Context Object. * @throws Exception if unable to create Object instance. */ - private Object newContextInstance(Server server, Environment environment, TrackedPaths app, Attributes attributes, Path path) throws Exception + private Object newContextInstance(Server server, Environment environment, PathsApp app, Attributes attributes, Path path) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("newContextInstance({}, {}, {}, {})", server, environment, app, path); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java similarity index 98% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index 5c373afeddfd..939c0ef18538 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/TrackedPaths.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -39,7 +39,7 @@ * a from-file-system App deployment that the {@link DeploymentScanner} * creates and uses. */ -public class TrackedPaths +public class PathsApp { public enum State { @@ -49,14 +49,14 @@ public enum State REMOVED } - private static final Logger LOG = LoggerFactory.getLogger(TrackedPaths.class); + private static final Logger LOG = LoggerFactory.getLogger(PathsApp.class); private final String name; private final Map paths = new HashMap<>(); private final Attributes attributes = new Attributes.Mapped(); private State state; private ContextHandler contextHandler; - public TrackedPaths(String name) + public PathsApp(String name) { this.name = name; this.state = calcState(); @@ -75,7 +75,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - TrackedPaths that = (TrackedPaths)o; + PathsApp that = (PathsApp)o; return Objects.equals(name, that.name); } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index 3372e2596929..a41e7645edf9 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -20,7 +20,7 @@ import javax.management.ObjectName; import org.eclipse.jetty.deploy.internal.DeploymentGraph; -import org.eclipse.jetty.deploy.internal.DeploymentLifeCyclePathCollector; +import org.eclipse.jetty.deploy.internal.DeploymentGraphNodeOrderCollector; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -38,7 +38,7 @@ public void testStateTransitionNewToDeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + DeploymentGraphNodeOrderCollector pathtracker = new DeploymentGraphNodeOrderCollector(); depman.addLifeCycleBinding(pathtracker); depman.setContexts(new ContextHandlerCollection()); @@ -77,7 +77,7 @@ public void testStateTransitionReceive() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + DeploymentGraphNodeOrderCollector pathtracker = new DeploymentGraphNodeOrderCollector(); depman.addLifeCycleBinding(pathtracker); // Start DepMan @@ -97,10 +97,10 @@ public void testStateTransitionReceive() throws Exception @Test @Disabled("Not working yet, need to figure out how to reference the ContextHandler mbean") - public void testStateTransitionDeployedToUndeployed() throws Exception + public void testMBeanStateTransitionToUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); + DeploymentGraphNodeOrderCollector pathtracker = new DeploymentGraphNodeOrderCollector(); // Setup JMX MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index f540d8d820a0..1284273c7af5 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -20,7 +20,7 @@ import java.util.List; import java.util.Set; -import org.eclipse.jetty.deploy.internal.DeploymentLifeCyclePathCollector; +import org.eclipse.jetty.deploy.internal.DeploymentGraphNodeOrderCollector; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -46,29 +46,29 @@ public class DeploymentManagerTest extends AbstractCleanEnvironmentTest @Test public void testAddUndeployed() throws Exception { - DeploymentManager depman = new DeploymentManager(); - depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); - depman.addLifeCycleBinding(pathtracker); + DeploymentManager deploymentManager = new DeploymentManager(); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + deploymentManager.setContexts(contextHandlerCollection); + deploymentManager.addBean(contextHandlerCollection); // Start DepMan - depman.start(); + deploymentManager.start(); try { // Trigger new context ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); - depman.addUndeployed(foo); + deploymentManager.addUndeployed(foo); assertFalse(foo.isStarted()); // Test context tracking - Collection contextHandlers = depman.getContextHandlers(); + Collection contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(1)); ContextHandler first = contextHandlers.iterator().next(); assertThat("contextHandler", first, equalTo(foo)); // Verify that context is in expected graph node - List undeployedContexts = depman.getContextHandlers("undeployed") + List undeployedContexts = deploymentManager.getContextHandlers("undeployed") .stream() .toList(); List expectedContexts = List.of(foo); @@ -76,43 +76,43 @@ public void testAddUndeployed() throws Exception } finally { - LifeCycle.stop(depman); + LifeCycle.stop(deploymentManager); } } @Test public void testDeploy() throws Exception { - DeploymentManager depman = new DeploymentManager(); - depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); - depman.addLifeCycleBinding(pathtracker); + DeploymentManager deploymentManager = new DeploymentManager(); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + deploymentManager.setContexts(contextHandlerCollection); + deploymentManager.addBean(contextHandlerCollection); // Start DepMan - depman.start(); + deploymentManager.start(); try { // Trigger new context ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); - depman.deploy(foo); + deploymentManager.deploy(foo); assertTrue(foo.isStarted()); // Test context tracking - Collection contextHandlers = depman.getContextHandlers(); + Collection contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(1)); ContextHandler first = contextHandlers.iterator().next(); assertThat("contextHandler", first, equalTo(foo)); // Verify that context is in expected graph node - List startedContexts = depman.getContextHandlers("started") + List startedContexts = deploymentManager.getContextHandlers("started") .stream() .toList(); List expectedContexts = List.of(foo); assertThat(startedContexts, ordered(expectedContexts)); // Verify that the graph only has one entry, and it's on started. - List state = getGraphState(depman); + List state = getGraphState(deploymentManager); List expected = List.of( "started|/foo-webapp-1" ); @@ -120,30 +120,30 @@ public void testDeploy() throws Exception } finally { - LifeCycle.stop(depman); + LifeCycle.stop(deploymentManager); } } @Test public void testUndeploy() throws Exception { - DeploymentManager depman = new DeploymentManager(); - depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); - depman.addLifeCycleBinding(pathtracker); + DeploymentManager deploymentManager = new DeploymentManager(); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + deploymentManager.setContexts(contextHandlerCollection); + deploymentManager.addBean(contextHandlerCollection); // Start DepMan - depman.start(); + deploymentManager.start(); try { // Trigger deploy ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); - depman.deploy(foo); + deploymentManager.deploy(foo); assertTrue(foo.isStarted()); // Test context tracking - Collection contextHandlers = depman.getContextHandlers(); + Collection contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(1)); ContextHandler first = contextHandlers.iterator().next(); assertThat("contextHandler", first, equalTo(foo)); @@ -151,48 +151,48 @@ public void testUndeploy() throws Exception List expectedContexts = List.of(foo); // Verify that context is in expected graph node - List startedContexts = depman.getContextHandlers("started") + List startedContexts = deploymentManager.getContextHandlers("started") .stream() .toList(); assertThat(startedContexts, ordered(expectedContexts)); // Trigger undeploy - depman.undeploy(foo); + deploymentManager.undeploy(foo); // Test context tracking (the context should have been removed) - contextHandlers = depman.getContextHandlers(); + contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(0)); // Verify that the graph is empty now (that was the last context undeployed) - List graphState = getGraphState(depman); + List graphState = getGraphState(deploymentManager); assertTrue(graphState.isEmpty()); } finally { - LifeCycle.stop(depman); + LifeCycle.stop(deploymentManager); } } @Test public void testMove() throws Exception { - DeploymentManager depman = new DeploymentManager(); - depman.setContexts(new ContextHandlerCollection()); - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); - depman.addLifeCycleBinding(pathtracker); + DeploymentManager deploymentManager = new DeploymentManager(); + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + deploymentManager.setContexts(contextHandlerCollection); + deploymentManager.addBean(contextHandlerCollection); // Start DepMan - depman.start(); + deploymentManager.start(); try { // Trigger deploy ContextHandler foo = Util.createContextHandler("foo-webapp-1.war"); - depman.deploy(foo); + deploymentManager.deploy(foo); assertTrue(foo.isStarted()); // Test context tracking - Collection contextHandlers = depman.getContextHandlers(); + Collection contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(1)); ContextHandler first = contextHandlers.iterator().next(); assertThat("contextHandler", first, equalTo(foo)); @@ -200,22 +200,22 @@ public void testMove() throws Exception List expectedContexts = List.of(foo); // Verify that context is in expected graph node - List startedContexts = depman.getContextHandlers("started") + List startedContexts = deploymentManager.getContextHandlers("started") .stream() .toList(); assertThat(startedContexts, ordered(expectedContexts)); // Trigger undeploy - depman.move(foo, "deployed"); + deploymentManager.move(foo, "deployed"); // Test context tracking (the context should have been removed) - contextHandlers = depman.getContextHandlers(); + contextHandlers = deploymentManager.getContextHandlers(); assertThat("contextHandlers.size", contextHandlers.size(), is(1)); first = contextHandlers.iterator().next(); assertThat("contextHandler", first, equalTo(foo)); // Verify that the graph only has one entry, and it's on deployed. - List state = getGraphState(depman); + List state = getGraphState(deploymentManager); List expected = List.of( "deployed|/foo-webapp-1" ); @@ -223,16 +223,16 @@ public void testMove() throws Exception } finally { - LifeCycle.stop(depman); + LifeCycle.stop(deploymentManager); } } - private List getGraphState(DeploymentManager depman) + private List getGraphState(DeploymentManager deploymentManager) { List state = new ArrayList<>(); - for (String nodeName : depman.getNodeNames()) + for (String nodeName : deploymentManager.getNodeNames()) { - for (ContextHandler contextHandler : depman.getContextHandlers(nodeName)) + for (ContextHandler contextHandler : deploymentManager.getContextHandlers(nodeName)) { state.add("%s|%s".formatted(nodeName, contextHandler.getContextPath())); } @@ -244,27 +244,28 @@ private List getGraphState(DeploymentManager depman) @Test public void testBinding() { - DeploymentLifeCyclePathCollector pathtracker = new DeploymentLifeCyclePathCollector(); - DeploymentManager depman = new DeploymentManager(); - depman.addLifeCycleBinding(pathtracker); + DeploymentManager deploymentManager = new DeploymentManager(); - Set allbindings = depman.getBindings(); - assertNotNull(allbindings, "All Bindings should never be null"); - assertEquals(1, allbindings.size(), "All Bindings.size"); + // Add a binding that isn't part of the standard set here. + deploymentManager.addLifeCycleBinding(new DeploymentGraphNodeOrderCollector()); - Set deploybindings = depman.getBindings("deploying"); - assertNotNull(deploybindings, "'deploying' Bindings should not be null"); - assertEquals(1, deploybindings.size(), "'deploying' Bindings.size"); + Set allBindings = deploymentManager.getBindings(); + assertNotNull(allBindings, "All Bindings should never be null"); + assertEquals(1, allBindings.size(), "All Bindings.size"); + + Set deployBindings = deploymentManager.getBindings("deploying"); + assertNotNull(deployBindings, "'deploying' Bindings should not be null"); + assertEquals(1, deployBindings.size(), "'deploying' Bindings.size"); } @Test public void testXmlConfigured(WorkDir workDir) throws Exception { - Path testdir = workDir.getEmptyPathDir(); + Path testDir = workDir.getEmptyPathDir(); XmlConfiguredJetty jetty = null; try { - jetty = new XmlConfiguredJetty(testdir); + jetty = new XmlConfiguredJetty(testDir); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java index 99b7880bfdc4..ce512e30f160 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeployActionComparatorTest.java @@ -19,7 +19,7 @@ import java.util.List; import org.eclipse.jetty.deploy.DeploymentScanner.DeployAction; -import org.eclipse.jetty.deploy.internal.TrackedPaths; +import org.eclipse.jetty.deploy.internal.PathsApp; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -30,10 +30,10 @@ public class DeploymentScannerDeployActionComparatorTest @Test public void testAddOnly() { - TrackedPaths appFoo = new TrackedPaths("foo"); - appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.ADDED); - TrackedPaths appBar = new TrackedPaths("bar"); - appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.ADDED); + PathsApp appFoo = new PathsApp("foo"); + appFoo.putPath(Path.of("foo.xml"), PathsApp.State.ADDED); + PathsApp appBar = new PathsApp("bar"); + appBar.putPath(Path.of("bar.xml"), PathsApp.State.ADDED); List actions = new ArrayList<>(); actions.add(new DeployAction(DeployAction.Type.ADD, "bar")); @@ -58,11 +58,11 @@ public void testAddOnly() @Test public void testRemoveOnly() { - TrackedPaths appFoo = new TrackedPaths("foo"); - appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.REMOVED); + PathsApp appFoo = new PathsApp("foo"); + appFoo.putPath(Path.of("foo.xml"), PathsApp.State.REMOVED); - TrackedPaths appBar = new TrackedPaths("bar"); - appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.REMOVED); + PathsApp appBar = new PathsApp("bar"); + appBar.putPath(Path.of("bar.xml"), PathsApp.State.REMOVED); List actions = new ArrayList<>(); actions.add(new DeployAction(DeployAction.Type.REMOVE, "foo")); @@ -87,11 +87,11 @@ public void testRemoveOnly() @Test public void testRemoveTwoAndAddTwo() { - TrackedPaths appFoo = new TrackedPaths("foo"); - appFoo.putPath(Path.of("foo.xml"), TrackedPaths.State.REMOVED); + PathsApp appFoo = new PathsApp("foo"); + appFoo.putPath(Path.of("foo.xml"), PathsApp.State.REMOVED); - TrackedPaths appBar = new TrackedPaths("bar"); - appBar.putPath(Path.of("bar.xml"), TrackedPaths.State.REMOVED); + PathsApp appBar = new PathsApp("bar"); + appBar.putPath(Path.of("bar.xml"), PathsApp.State.REMOVED); List actions = new ArrayList<>(); actions.add(new DeployAction(DeployAction.Type.REMOVE, "foo")); @@ -132,17 +132,17 @@ public void testRemoveTwoAndAddTwo() @Test public void testRemoveFourAndAddTwo() { - TrackedPaths appA = new TrackedPaths("app-a"); - appA.putPath(Path.of("app-a.xml"), TrackedPaths.State.REMOVED); + PathsApp appA = new PathsApp("app-a"); + appA.putPath(Path.of("app-a.xml"), PathsApp.State.REMOVED); - TrackedPaths appB = new TrackedPaths("app-b"); - appB.putPath(Path.of("app-b.xml"), TrackedPaths.State.REMOVED); + PathsApp appB = new PathsApp("app-b"); + appB.putPath(Path.of("app-b.xml"), PathsApp.State.REMOVED); - TrackedPaths appC = new TrackedPaths("app-c"); - appC.putPath(Path.of("app-c.xml"), TrackedPaths.State.REMOVED); + PathsApp appC = new PathsApp("app-c"); + appC.putPath(Path.of("app-c.xml"), PathsApp.State.REMOVED); - TrackedPaths appD = new TrackedPaths("app-d"); - appD.putPath(Path.of("app-d.xml"), TrackedPaths.State.REMOVED); + PathsApp appD = new PathsApp("app-d"); + appD.putPath(Path.of("app-d.xml"), PathsApp.State.REMOVED); List actions = new ArrayList<>(); // app A is going through hot-reload diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java index ca42ed1791d4..6a8cc686b750 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java @@ -126,13 +126,13 @@ public void waitForDirectoryScan() * Test that if a unit (called "simple" has both a war file and xml file), will be * redeployed if the war file is touched (note: the XML is the main deployable path) *

        - * This addresses issue https://github.com/jetty/jetty.project/issues/12543 + * This addresses Issue 12543 */ @Test public void testRedeployIfAnyTrackedPathChanges(WorkDir workDir) throws Exception { - Path testdir = workDir.getEmptyPathDir(); - createJettyBase(testdir); + Path baseDir = workDir.getEmptyPathDir(); + createJettyBase(baseDir); startJetty(); Path webappsDir = jetty.getJettyBasePath().resolve("webapps"); @@ -163,8 +163,8 @@ public void testRedeployIfAnyTrackedPathChanges(WorkDir workDir) throws Exceptio @Test public void testWebAppsWithAddedEnvConfig(WorkDir workDir) throws Exception { - Path testdir = workDir.getEmptyPathDir(); - createJettyBase(testdir); + Path baseDir = workDir.getEmptyPathDir(); + createJettyBase(baseDir); Path environments = jetty.getJettyBasePath().resolve("environments"); FS.ensureDirExists(environments); @@ -241,8 +241,8 @@ public void testAfterStartupThenRemoveContext(WorkDir workDir) throws Exception @Test public void testAfterStartupThenUpdateContext(WorkDir workDir) throws Exception { - Path testdir = workDir.getEmptyPathDir(); - createJettyBase(testdir); + Path baseDir = workDir.getEmptyPathDir(); + createJettyBase(baseDir); startJetty(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java index 17bda0715550..4dc02415fadc 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerTest.java @@ -23,7 +23,7 @@ import java.util.function.Consumer; import org.eclipse.jetty.deploy.DeploymentScanner.DeployAction; -import org.eclipse.jetty.deploy.internal.TrackedPaths; +import org.eclipse.jetty.deploy.internal.PathsApp; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.Scanner; @@ -60,13 +60,13 @@ protected void performActions(List actions) // Perform post performActions cleanup that normally happens for (DeployAction action : actions) { - resetTrackedState(action.name()); + resetAppState(action.name()); } } - public TrackedPaths findTracked(String name) + public PathsApp findApp(String name) { - return super.findTracked(name); + return super.findApp(name); } } @@ -92,8 +92,8 @@ public void testActionListNewXmlOnly() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.ADDED)); assertThat("action.app.paths", app.getPaths().keySet(), Matchers.contains(xml)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -124,10 +124,10 @@ public void testActionListXmlThenRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("foo")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.ADDED)); assertThat("action.app.paths", app.getPaths().keySet(), contains(xml)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.ADDED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -147,10 +147,10 @@ public void testActionListXmlThenRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("foo")); assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.REMOVED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.REMOVED)); assertThat("action.app.paths", app.getPaths().keySet(), contains(xml)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.REMOVED)); assertThat("action.app.mainPath", app.getMainPath(), is(nullValue())); }; @@ -184,11 +184,11 @@ public void testActionListNewXmlAndWarOnly() throws IOException assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.ADDED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.ADDED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -221,11 +221,11 @@ public void testActionListXmlAndWarWithXmlUpdate() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.ADDED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.ADDED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -244,21 +244,21 @@ public void testActionListXmlAndWarWithXmlUpdate() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.UNCHANGED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.UNCHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.CHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.UNCHANGED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.UNCHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.CHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -291,11 +291,11 @@ public void testActionListXmlAndWarWithXmlRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.ADDED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.ADDED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.ADDED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.ADDED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.ADDED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -315,21 +315,21 @@ public void testActionListXmlAndWarWithXmlRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.CHANGED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.CHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.CHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.CHANGED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.CHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.CHANGED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.CHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(xml)); }; @@ -349,21 +349,21 @@ public void testActionListXmlAndWarWithXmlRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.REMOVED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.UNCHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(war)); action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.ADD)); - app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.CHANGED)); + app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.CHANGED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(xml, war)); - assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(TrackedPaths.State.REMOVED)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.UNCHANGED)); + assertThat("action.app.paths[xml].state", app.getPaths().get(xml), is(PathsApp.State.REMOVED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.UNCHANGED)); assertThat("action.app.mainPath", app.getMainPath(), is(war)); }; @@ -383,10 +383,10 @@ public void testActionListXmlAndWarWithXmlRemoved() throws IOException action = iterator.next(); assertThat("action.name", action.name(), is("bar")); assertThat("action.type", action.type(), is(DeployAction.Type.REMOVE)); - TrackedPaths app = deploymentScanner.findTracked(action.name()); - assertThat("action.app.state", app.getState(), is(TrackedPaths.State.REMOVED)); + PathsApp app = deploymentScanner.findApp(action.name()); + assertThat("action.app.state", app.getState(), is(PathsApp.State.REMOVED)); assertThat("action.app.paths", app.getPaths().keySet(), containsInAnyOrder(war)); - assertThat("action.app.paths[war].state", app.getPaths().get(war), is(TrackedPaths.State.REMOVED)); + assertThat("action.app.paths[war].state", app.getPaths().get(war), is(PathsApp.State.REMOVED)); assertThat("action.app.mainPath", app.getMainPath(), is(nullValue())); }; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java similarity index 96% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java index c183a7d7b15a..7e40d48fea3b 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCyclePathCollector.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java @@ -26,7 +26,7 @@ /** * Binds to all lifecycle nodes, and tracks the order of the lifecycle nodes for testing purposes. */ -public class DeploymentLifeCyclePathCollector implements DeploymentNodeBinding +public class DeploymentGraphNodeOrderCollector implements DeploymentNodeBinding { List actualOrder = new ArrayList<>(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphTest.java similarity index 99% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphTest.java index 691942ceaf5b..d549d04edcca 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentLifeCycleTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphTest.java @@ -34,7 +34,7 @@ * Just an overly picky test case to validate the potential paths. */ @ExtendWith(WorkDirExtension.class) -public class DeploymentLifeCycleTest +public class DeploymentGraphTest { private void assertNoPath(String from, String to) { diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/PathsAppTest.java similarity index 55% rename from jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java rename to jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/PathsAppTest.java index 2a528565c082..724152b16866 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/TrackedPathsTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/PathsAppTest.java @@ -28,7 +28,7 @@ import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class TrackedPathsTest +public class PathsAppTest { public WorkDir workDir; @@ -45,8 +45,8 @@ public void testMainPathOnlyXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); - TrackedPaths app = new TrackedPaths("bar"); - app.putPath(xml, TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("bar"); + app.putPath(xml, PathsApp.State.UNCHANGED); Path main = app.getMainPath(); @@ -67,9 +67,9 @@ public void testMainPathXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - TrackedPaths app = new TrackedPaths("bar"); - app.putPath(xml, TrackedPaths.State.UNCHANGED); - app.putPath(war, TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("bar"); + app.putPath(xml, PathsApp.State.UNCHANGED); + app.putPath(war, PathsApp.State.UNCHANGED); Path main = app.getMainPath(); @@ -90,9 +90,9 @@ public void testMainPathDirAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - TrackedPaths app = new TrackedPaths("bar"); - app.putPath(appDir, TrackedPaths.State.UNCHANGED); - app.putPath(war, TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("bar"); + app.putPath(appDir, PathsApp.State.UNCHANGED); + app.putPath(war, PathsApp.State.UNCHANGED); Path main = app.getMainPath(); @@ -113,9 +113,9 @@ public void testMainPathDirAndXml() throws IOException Path xml = dir.resolve("bar.xml"); Files.writeString(xml, "XML for bar", UTF_8); - TrackedPaths app = new TrackedPaths("bar"); - app.putPath(appDir, TrackedPaths.State.UNCHANGED); - app.putPath(xml, TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("bar"); + app.putPath(appDir, PathsApp.State.UNCHANGED); + app.putPath(xml, PathsApp.State.UNCHANGED); Path main = app.getMainPath(); @@ -138,10 +138,10 @@ public void testMainPathDirAndXmlAndWar() throws IOException Path war = dir.resolve("bar.war"); Files.writeString(war, "WAR for bar", UTF_8); - TrackedPaths app = new TrackedPaths("bar"); - app.putPath(appDir, TrackedPaths.State.UNCHANGED); - app.putPath(xml, TrackedPaths.State.UNCHANGED); - app.putPath(war, TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("bar"); + app.putPath(appDir, PathsApp.State.UNCHANGED); + app.putPath(xml, PathsApp.State.UNCHANGED); + app.putPath(war, PathsApp.State.UNCHANGED); Path main = app.getMainPath(); @@ -151,107 +151,107 @@ public void testMainPathDirAndXmlAndWar() throws IOException @Test public void testStateUnchanged() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); - app.putPath(Path.of("test-b"), TrackedPaths.State.UNCHANGED); + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.UNCHANGED); + app.putPath(Path.of("test-b"), PathsApp.State.UNCHANGED); - assertThat(app.getState(), is(TrackedPaths.State.UNCHANGED)); + assertThat(app.getState(), is(PathsApp.State.UNCHANGED)); } @Test public void testStateInitialEmpty() { - TrackedPaths app = new TrackedPaths("test"); + PathsApp app = new PathsApp("test"); // intentionally empty of Paths - assertThat(app.getState(), is(TrackedPaths.State.REMOVED)); + assertThat(app.getState(), is(PathsApp.State.REMOVED)); } @Test public void testStatePutThenRemoveAll() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.ADDED); - assertThat(app.getState(), is(TrackedPaths.State.ADDED)); + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.ADDED); + assertThat(app.getState(), is(PathsApp.State.ADDED)); // Now it gets flagged as removed. (eg: by a Scanner change) - app.putPath(Path.of("test-a"), TrackedPaths.State.REMOVED); + app.putPath(Path.of("test-a"), PathsApp.State.REMOVED); // Then it gets processed, which results in a state reset. app.resetStates(); // The resulting app should have no paths, and be flagged as removed. assertThat(app.getPaths().size(), is(0)); - assertThat(app.getState(), is(TrackedPaths.State.REMOVED)); + assertThat(app.getState(), is(PathsApp.State.REMOVED)); } @Test public void testStateAddedOnly() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.ADDED); - app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.ADDED); + app.putPath(Path.of("test-b"), PathsApp.State.ADDED); - assertThat(app.getState(), is(TrackedPaths.State.ADDED)); + assertThat(app.getState(), is(PathsApp.State.ADDED)); } @Test public void testStateAddedRemoved() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.REMOVED); // existing file removed in this scan - app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.REMOVED); // existing file removed in this scan + app.putPath(Path.of("test-b"), PathsApp.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } @Test public void testStateAddedChanged() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.CHANGED); // existing file changed in this scan - app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.CHANGED); // existing file changed in this scan + app.putPath(Path.of("test-b"), PathsApp.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } @Test public void testStateUnchangedAdded() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), TrackedPaths.State.ADDED); // new file introduced in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), PathsApp.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } @Test public void testStateUnchangedChanged() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), TrackedPaths.State.CHANGED); // existing file changed in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), PathsApp.State.CHANGED); // existing file changed in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } @Test public void testStateUnchangedRemoved() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), TrackedPaths.State.REMOVED); // existing file removed in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), PathsApp.State.REMOVED); // existing file removed in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } @Test public void testStateUnchangedRemovedAdded() { - TrackedPaths app = new TrackedPaths("test"); - app.putPath(Path.of("test-a"), TrackedPaths.State.UNCHANGED); // existed in previous scan - app.putPath(Path.of("test-b"), TrackedPaths.State.REMOVED); // existing file changed in this scan event - app.putPath(Path.of("test-c"), TrackedPaths.State.ADDED); // new file introduced in this scan event + PathsApp app = new PathsApp("test"); + app.putPath(Path.of("test-a"), PathsApp.State.UNCHANGED); // existed in previous scan + app.putPath(Path.of("test-b"), PathsApp.State.REMOVED); // existing file changed in this scan event + app.putPath(Path.of("test-c"), PathsApp.State.ADDED); // new file introduced in this scan event - assertThat(app.getState(), is(TrackedPaths.State.CHANGED)); + assertThat(app.getState(), is(PathsApp.State.CHANGED)); } } diff --git a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml index 3379ce59aa5f..36dfd5ed6c3a 100644 --- a/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml +++ b/jetty-core/jetty-deploy/src/test/resources/jetty-core-deploy-custom.xml @@ -1,7 +1,7 @@ - + core diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml index 31526040bb12..91c5e715f9df 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-core-deploy.xml @@ -4,7 +4,7 @@ - + core diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 5b8ee5c0197d..e02bcae6a0ae 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index a845c1f10ff7..94a5627ef45c 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml index 142c9f6d92ad..962b157bf71b 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-deploy.xml @@ -4,7 +4,7 @@ - + ee10 diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 8d3d499a969f..01ac24c7a764 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -96,7 +96,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 71bdd8f44216..e8719d6059e7 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml index ca3dd53b9ef7..d2138157f564 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/etc/jetty-ee11-deploy.xml @@ -4,7 +4,7 @@ - + ee11 diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml index 2610aa3fbcce..9687edeafbf1 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-deploy.xml @@ -4,7 +4,7 @@ - + ee8 diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index 3a3581a32fc2..df039e342ac0 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -80,7 +80,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 3d60fb3425ba..9b6e8b164e95 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -14,7 +14,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml index bb0ccf2ac265..739ce8f8d165 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-deploy.xml @@ -4,7 +4,7 @@ - + ee9 From 266ea987e1b5082eaf8ee8c99eecae12d8440b4c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 13:06:39 -0600 Subject: [PATCH 064/104] Fixing compilation issues --- .../jetty/osgi/AbstractContextProvider.java | 7 ++++--- .../jetty/osgi/BundleContextProvider.java | 20 ++++++++----------- .../jetty/osgi/BundleWebAppProvider.java | 18 +++++++---------- .../jetty/ee10/osgi/boot/EE10Activator.java | 4 ++-- .../jetty/ee10/test/DeploymentErrorTest.java | 2 +- .../jetty/ee11/osgi/boot/EE11Activator.java | 4 ++-- .../jetty/ee11/test/DeploymentErrorTest.java | 2 +- .../jetty/ee9/osgi/boot/EE9Activator.java | 4 ++-- .../jetty/ee9/test/DeploymentErrorTest.java | 2 +- 9 files changed, 28 insertions(+), 35 deletions(-) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java index 736eeb3316b1..1ae3dc411a7b 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/AbstractContextProvider.java @@ -35,13 +35,15 @@ */ public abstract class AbstractContextProvider extends AbstractLifeCycle { + private final Server _server; private final ContextHandlerDeployer _contextHandlerManagement; private ContextFactory _contextFactory; private String _environment; private final Attributes _attributes = new Attributes.Mapped(); - public AbstractContextProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) + public AbstractContextProvider(Server server, ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { + _server = server; _contextHandlerManagement = contextHandlerManagement; _environment = Objects.requireNonNull(environment); _contextFactory = Objects.requireNonNull(contextFactory); @@ -49,7 +51,7 @@ public AbstractContextProvider(ContextHandlerDeployer contextHandlerManagement, public Server getServer() { - return _contextHandlerManagement.getServer(); + return _server; } public Attributes getAttributes() @@ -64,7 +66,6 @@ public ContextHandler createContextHandler(BundleMetadata metadata) throws Excep // Create a ContextHandler suitable to deploy in OSGi ContextHandler contextHandler = _contextFactory.createContextHandler(this, metadata); - contextHandler.setID(metadata.getID()); // force ID to what this context handler source needs (as XML might have set it) Util.setBundle(contextHandler, metadata.getBundle()); return contextHandler; } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index dd14d9310cfe..183c676fa49c 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -21,11 +21,13 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.ListIterator; import java.util.Map; import org.eclipse.jetty.deploy.ContextHandlerDeployer; import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.osgi.framework.Bundle; @@ -46,8 +48,6 @@ public class BundleContextProvider extends AbstractContextProvider implements Bu { private static final Logger LOG = LoggerFactory.getLogger(BundleContextProvider.class); - private final Map _contextHandlerMap = new HashMap<>(); - private final Map> _bundleMap = new HashMap<>(); private ServiceRegistration _serviceRegForBundles; @@ -107,9 +107,9 @@ public void removedBundle(Bundle bundle, BundleEvent event, Object object) } } - public BundleContextProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) + public BundleContextProvider(Server server, ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { - super(contextHandlerManagement, environment, contextFactory); + super(server, contextHandlerManagement, environment, contextFactory); } @Override @@ -192,10 +192,9 @@ public boolean bundleAdded(Bundle bundle) throws Exception metadata.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); } ContextHandler contextHandler = createContextHandler(metadata); - _contextHandlerMap.put(contextHandler.getID(), contextHandler); List contextHandlers = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); contextHandlers.add(contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); + getContextHandlerManagement().deploy(contextHandler); added = true; } @@ -218,12 +217,9 @@ public boolean bundleRemoved(Bundle bundle) boolean removed = false; for (ContextHandler context : contexts) { - if (_contextHandlerMap.remove(context.getID()) != null) - { - getContextHandlerManagement().removeContextHandler(context, DeploymentGraph.UNDEPLOYED); - removed = true; - } + getContextHandlerManagement().undeploy(context); + removed = true; } - return removed; // true if even 1 context was removed associated with this bundle + return removed; } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index 455585c6ec2f..4d6e276ff009 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.deploy.ContextHandlerDeployer; import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.StringUtil; import org.osgi.framework.Bundle; @@ -95,9 +96,9 @@ public void removedBundle(Bundle bundle, BundleEvent event, Object object) } } - public BundleWebAppProvider(ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) + public BundleWebAppProvider(Server server, ContextHandlerDeployer contextHandlerManagement, String environment, ContextFactory contextFactory) { - super(contextHandlerManagement, environment, contextFactory); + super(server, contextHandlerManagement, environment, contextFactory); } @Override @@ -186,7 +187,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, staticResourcesLocation); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); + getContextHandlerManagement().deploy(contextHandler); return true; } @@ -197,7 +198,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); + getContextHandlerManagement().deploy(contextHandler); return true; } @@ -209,18 +210,13 @@ public boolean bundleAdded(Bundle bundle) throws Exception BundleMetadata app = new BundleMetadata(bundle, base); ContextHandler contextHandler = createContextHandler(app); _bundleMap.put(bundle, contextHandler); - getContextHandlerManagement().addContextHandler(contextHandler, DeploymentGraph.STARTED); + getContextHandlerManagement().deploy(contextHandler); return true; } //not a webapp return false; } - catch (Throwable t) - { - t.printStackTrace(System.err); - throw t; - } finally { Thread.currentThread().setContextClassLoader(cl); @@ -239,7 +235,7 @@ public boolean bundleRemoved(Bundle bundle) ContextHandler contextHandler = _bundleMap.remove(bundle); if (contextHandler != null) { - getContextHandlerManagement().removeContextHandler(contextHandler, DeploymentGraph.UNDEPLOYED); + getContextHandlerManagement().undeploy(contextHandler); return true; } return false; diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index 766f332c1c6a..ca7ff2e88b1a 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -163,13 +163,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE10ContextFactory(_myBundle)); + contextProvider = new BundleContextProvider(server, deploymentManager, ENVIRONMENT, new EE10ContextFactory(_myBundle)); deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE10WebAppFactory(_myBundle)); + webAppProvider = new BundleWebAppProvider(server, deploymentManager, ENVIRONMENT, new EE10WebAppFactory(_myBundle)); deploymentManager.addBean(webAppProvider); } diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index e4d70f525821..37e3ed5d9fee 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -103,7 +103,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee10"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee10.webapp.WebAppContext"); appProvider.setScanInterval(1); diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index dc1aa61df42b..df91aa0a40fe 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -163,13 +163,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE11ContextFactory(_myBundle)); + contextProvider = new BundleContextProvider(server, deploymentManager, ENVIRONMENT, new EE11ContextFactory(_myBundle)); deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE11WebAppFactory(_myBundle)); + webAppProvider = new BundleWebAppProvider(server, deploymentManager, ENVIRONMENT, new EE11WebAppFactory(_myBundle)); deploymentManager.addBean(webAppProvider); } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index ba61aa902e90..b4fbbc45fc19 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -103,7 +103,7 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee11"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee11.webapp.WebAppContext"); appProvider.setScanInterval(1); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 661d788dd2ce..371fb3dcfbd0 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -164,13 +164,13 @@ public Object addingService(ServiceReference sr) if (contextProvider == null) { - contextProvider = new BundleContextProvider(deploymentManager, ENVIRONMENT, new EE9ContextFactory(_myBundle)); + contextProvider = new BundleContextProvider(server, deploymentManager, ENVIRONMENT, new EE9ContextFactory(_myBundle)); deploymentManager.addBean(contextProvider); } if (webAppProvider == null) { - webAppProvider = new BundleWebAppProvider(deploymentManager, ENVIRONMENT, new EE9WebAppFactory(_myBundle)); + webAppProvider = new BundleWebAppProvider(server, deploymentManager, ENVIRONMENT, new EE9WebAppFactory(_myBundle)); deploymentManager.addBean(webAppProvider); } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index 4010f8ddb11d..e875b92f12ad 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -98,7 +98,7 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(deploymentManager); + DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee9"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee9.webapp.WebAppContext"); appProvider.setScanInterval(1); From 810b655979c9a901f833ebe61d8906a332ac4f58 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 15:01:40 -0600 Subject: [PATCH 065/104] Better excludes of internals --- javadoc/pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/javadoc/pom.xml b/javadoc/pom.xml index 0a58683a3110..be14d7609cf9 100644 --- a/javadoc/pom.xml +++ b/javadoc/pom.xml @@ -474,7 +474,10 @@ Eclipse Jetty API Doc - v${project.version} Eclipse Jetty API Doc - v${project.version} - org.eclipse.jetty.http3.client.transport.internal; org.eclipse.jetty.http3.client.internal; org.eclipse.jetty.http3.internal; org.eclipse.jetty.http3.internal.*; org.eclipse.jetty.http3.qpack.internal; org.eclipse.jetty.http3.qpack.internal.*; org.eclipse.jetty.http3.server.internal; org.eclipse.jetty.quic.common.internal; org.eclipse.jetty.quic.quiche; org.eclipse.jetty.quic.quiche.foreign.*; org.eclipse.jetty.quic.quiche.*; org.eclipse.jetty.quic.server.internal; + + *.internal; *.internal.*; org.eclipse.jetty.quic.quiche; org.eclipse.jetty.quic.quiche.foreign.*; org.eclipse.jetty.quic.quiche.*; + + *.internal,*.internal.* From 5d2f2bb4472d95619abd815d8cdb1826683dde24 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 15:02:02 -0600 Subject: [PATCH 066/104] Fixing accidentally exposed internal package --- jetty-core/jetty-deploy/pom.xml | 7 +++++++ .../jetty/deploy/DeploymentNodeBinding.java | 5 ++--- .../jetty/deploy/bindings/DebugBinding.java | 5 ++--- .../deploy/bindings/OrderedGroupBinding.java | 5 ++--- .../jetty/deploy/bindings/StandardDeployer.java | 3 +-- .../jetty/deploy/bindings/StandardStarter.java | 3 +-- .../jetty/deploy/bindings/StandardStopper.java | 3 +-- .../jetty/deploy/bindings/StandardUndeployer.java | 3 +-- .../jetty/deploy/internal/DeploymentGraph.java | 2 +- .../DeploymentGraphNodeOrderCollector.java | 15 +++++++-------- .../eclipse/jetty/osgi/BundleContextProvider.java | 2 -- .../eclipse/jetty/osgi/BundleWebAppProvider.java | 1 - .../java/org/eclipse/jetty/osgi/OSGiDeployer.java | 11 +++++------ .../org/eclipse/jetty/osgi/OSGiUndeployer.java | 5 ++--- 14 files changed, 32 insertions(+), 38 deletions(-) diff --git a/jetty-core/jetty-deploy/pom.xml b/jetty-core/jetty-deploy/pom.xml index c9544ba40e75..9b952f880835 100644 --- a/jetty-core/jetty-deploy/pom.xml +++ b/jetty-core/jetty-deploy/pom.xml @@ -84,6 +84,13 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + + *.internal,*.internal.* + + maven-surefire-plugin diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java index 79b196bed030..ef7c0eb70a40 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentNodeBinding.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.deploy; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; /** @@ -33,9 +32,9 @@ public interface DeploymentNodeBinding * Event called to perform an action when targeted node on the Deployment graph * has a ContextHandler move through it. * - * @param node the node being processed + * @param nodeName the node being processed * @param contextHandler the contextHandler being processed * @throws Exception if any problem severe enough to halt the ContextHandler processing */ - void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception; + void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception; } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java index 089e4cd153b3..bc73dffcc1be 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/DebugBinding.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,8 +42,8 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { - LOG.info("processBinding {} {}", node, contextHandler); + LOG.info("processBinding {} {}", nodeName, contextHandler); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java index d1f76a566d5e..c3fada468dad 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/OrderedGroupBinding.java @@ -18,7 +18,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; /** @@ -63,11 +62,11 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { for (DeploymentNodeBinding binding : _orderedBindings) { - binding.processBinding(deploymentManager, node, contextHandler); + binding.processBinding(deploymentManager, nodeName, contextHandler); } } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java index decb56fe00e0..e26159d63ffa 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardDeployer.java @@ -17,7 +17,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Callback; @@ -30,7 +29,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { Objects.requireNonNull(contextHandler); Callback.Completable blocker = new Callback.Completable(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java index b864b8dbcd55..64c00b0dc262 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStarter.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -28,7 +27,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { ContextHandlerCollection contexts = deploymentManager.getContexts(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java index cf2ae406b5f0..c6f22e9bb340 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardStopper.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; public class StandardStopper implements DeploymentNodeBinding @@ -27,7 +26,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { // Before stopping, take back management from the context deploymentManager.getContexts().unmanage(contextHandler); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java index 2291fc191614..dec7ff25af67 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/StandardUndeployer.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.Callback; @@ -29,7 +28,7 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { ContextHandlerCollection contexts = deploymentManager.getContexts(); Callback.Completable blocker = new Callback.Completable(); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java index d05a821d09a7..fcfa592425d8 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DeploymentGraph.java @@ -164,7 +164,7 @@ public void runBindings(Node node, ContextHandler contextHandler, DeploymentMana { if (LOG.isDebugEnabled()) LOG.debug("Calling {} for {}", binding.getClass().getName(), contextHandler); - binding.processBinding(deploymentManager, node, contextHandler); + binding.processBinding(deploymentManager, node.getName(), contextHandler); } } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java index 7e40d48fea3b..3115f32dafc3 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/internal/DeploymentGraphNodeOrderCollector.java @@ -18,7 +18,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.DeploymentNodeBinding; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -28,14 +27,14 @@ */ public class DeploymentGraphNodeOrderCollector implements DeploymentNodeBinding { - List actualOrder = new ArrayList<>(); + List actualOrder = new ArrayList<>(); public void clear() { actualOrder.clear(); } - public List getCapturedPath() + public List getCapturedPath() { return actualOrder; } @@ -48,9 +47,9 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { - actualOrder.add(node); + actualOrder.add(nodeName); } public void assertExpected(String msg, List expectedOrder) @@ -63,9 +62,9 @@ public void assertExpected(String msg, List expectedOrder) System.out.println(path); } System.out.println("/* Actual Path */"); - for (Node path : actualOrder) + for (String name : actualOrder) { - System.out.println(path.getName()); + System.out.println(name); } assertEquals(expectedOrder.size(), actualOrder.size(), msg + " / count"); @@ -73,7 +72,7 @@ public void assertExpected(String msg, List expectedOrder) for (int i = 0, n = expectedOrder.size(); i < n; i++) { - assertEquals(expectedOrder.get(i), actualOrder.get(i).getName(), msg + "[" + i + "]"); + assertEquals(expectedOrder.get(i), actualOrder.get(i), msg + "[" + i + "]"); } } } diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index 183c676fa49c..c9da45449f61 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -21,11 +21,9 @@ import java.util.HashMap; import java.util.Hashtable; import java.util.List; -import java.util.ListIterator; import java.util.Map; import org.eclipse.jetty.deploy.ContextHandlerDeployer; -import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java index 4d6e276ff009..7cd335de6c28 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleWebAppProvider.java @@ -19,7 +19,6 @@ import java.util.Map; import org.eclipse.jetty.deploy.ContextHandlerDeployer; -import org.eclipse.jetty.deploy.internal.DeploymentGraph; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java index d66bc2c08fc8..c602e3c73f06 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiDeployer.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardDeployer; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; @@ -38,7 +37,7 @@ public OSGiDeployer(Server server) } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { //TODO how to NOT send this event if its not a webapp: //OSGi Enterprise Spec only wants an event sent if its a webapp bundle (ie not a ContextHandler) @@ -47,7 +46,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte if (bundle == null) { // Not an OSGI based ContextHandler - doProcessBinding(deploymentManager, node, contextHandler); + doProcessBinding(deploymentManager, nodeName, contextHandler); } else { @@ -56,7 +55,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte EventSender.getInstance().send(EventSender.DEPLOYING_EVENT, bundle, contextPath); try { - doProcessBinding(deploymentManager, node, contextHandler); + doProcessBinding(deploymentManager, nodeName, contextHandler); Util.registerAsOSGiService(contextHandler); EventSender.getInstance().send(EventSender.DEPLOYED_EVENT, bundle, contextPath); } @@ -68,14 +67,14 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte } } - protected void doProcessBinding(DeploymentManager deploymentManager, Node node, ContextHandler app) throws Exception + protected void doProcessBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler app) throws Exception { ClassLoader old = Thread.currentThread().getContextClassLoader(); ClassLoader cl = (ClassLoader)_server.getAttribute(OSGiServerConstants.SERVER_CLASSLOADER); Thread.currentThread().setContextClassLoader(cl); try { - super.processBinding(deploymentManager, node, app); + super.processBinding(deploymentManager, nodeName, app); } finally { diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java index fc1ad6caed1a..e0e12fb3c84d 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/OSGiUndeployer.java @@ -15,7 +15,6 @@ import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.bindings.StandardUndeployer; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.osgi.util.EventSender; import org.eclipse.jetty.osgi.util.Util; import org.eclipse.jetty.server.Server; @@ -38,7 +37,7 @@ public OSGiUndeployer(Server server) } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) throws Exception + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) throws Exception { String contextPath = contextHandler.getContextPath(); @@ -52,7 +51,7 @@ public void processBinding(DeploymentManager deploymentManager, Node node, Conte Thread.currentThread().setContextClassLoader(cl); try { - super.processBinding(deploymentManager, node, contextHandler); + super.processBinding(deploymentManager, nodeName, contextHandler); } finally { From 33259b98098825d8249d2366a262234c49b0bcd2 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 15:50:55 -0600 Subject: [PATCH 067/104] Fix compilation of DeploymentErrorTest --- .../jetty/ee10/test/DeploymentErrorTest.java | 17 +++++------------ .../jetty/ee11/test/DeploymentErrorTest.java | 17 +++++------------ .../jetty/ee9/test/DeploymentErrorTest.java | 17 +++++------------ 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index 37e3ed5d9fee..dbe68a2b5e0e 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.deploy.DeploymentNodeBinding; import org.eclipse.jetty.deploy.DeploymentScanner; import org.eclipse.jetty.deploy.internal.DeploymentGraph; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; import org.eclipse.jetty.ee10.webapp.Configuration; import org.eclipse.jetty.ee10.webapp.Configurations; @@ -372,21 +371,15 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) + switch (nodeName) { - startingLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) - { - startedLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) - { - failedLatch.countDown(); + case "starting" -> startingLatch.countDown(); + case "started" -> startedLatch.countDown(); + case "failed" -> failedLatch.countDown(); } } } diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index b4fbbc45fc19..cbab42454ca5 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.deploy.DeploymentNodeBinding; import org.eclipse.jetty.deploy.DeploymentScanner; import org.eclipse.jetty.deploy.internal.DeploymentGraph; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee11.webapp.AbstractConfiguration; import org.eclipse.jetty.ee11.webapp.Configuration; import org.eclipse.jetty.ee11.webapp.Configurations; @@ -373,21 +372,15 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) + switch (nodeName) { - startingLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) - { - startedLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) - { - failedLatch.countDown(); + case "starting" -> startingLatch.countDown(); + case "started" -> startedLatch.countDown(); + case "failed" -> failedLatch.countDown(); } } } diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index e875b92f12ad..25367e7fa5c9 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.deploy.DeploymentNodeBinding; import org.eclipse.jetty.deploy.DeploymentScanner; import org.eclipse.jetty.deploy.internal.DeploymentGraph; -import org.eclipse.jetty.deploy.internal.graph.Node; import org.eclipse.jetty.ee9.webapp.AbstractConfiguration; import org.eclipse.jetty.ee9.webapp.Configuration; import org.eclipse.jetty.ee9.webapp.Configurations; @@ -378,21 +377,15 @@ public String[] getBindingTargets() } @Override - public void processBinding(DeploymentManager deploymentManager, Node node, ContextHandler contextHandler) + public void processBinding(DeploymentManager deploymentManager, String nodeName, ContextHandler contextHandler) { if (contextHandler.getContextPath().equalsIgnoreCase(expectedContextPath)) { - if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTING)) + switch (nodeName) { - startingLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.STARTED)) - { - startedLatch.countDown(); - } - else if (node.getName().equalsIgnoreCase(DeploymentGraph.FAILED)) - { - failedLatch.countDown(); + case "starting" -> startingLatch.countDown(); + case "started" -> startedLatch.countDown(); + case "failed" -> failedLatch.countDown(); } } } From d80f118194d1e5e8c4f2e5bb6609b248abc0c2fd Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 31 Jan 2025 16:12:26 -0600 Subject: [PATCH 068/104] Correct for missing in test XML --- .../src/test/resources/RFC2616Base.xml | 3 +++ .../jetty-ee10-test-integration/src/test/resources/deploy.xml | 3 +++ .../src/test/resources/RFC2616Base.xml | 3 +++ .../jetty-ee11-test-integration/src/test/resources/deploy.xml | 3 +++ .../src/test/resources/RFC2616Base.xml | 3 +++ .../jetty-ee9-test-integration/src/test/resources/deploy.xml | 3 +++ 6 files changed, 18 insertions(+) diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index e02bcae6a0ae..002a03ba9e2d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -97,6 +97,9 @@ + + + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index 94a5627ef45c..a2bcd0ab9114 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 01ac24c7a764..557c6a0c07bc 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -97,6 +97,9 @@ + + + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index e8719d6059e7..9b65fae24630 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index df039e342ac0..c679eb8fd88c 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -81,6 +81,9 @@ + + + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 9b6e8b164e95..61f202d4f37a 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -15,6 +15,9 @@ + + + From aafcb0b7ec1149bf17ae4fafa1b6f73e604787fd Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 11:09:43 -0600 Subject: [PATCH 069/104] Changes from review --- .../src/main/config/etc/jetty-deploy.xml | 2 +- .../jetty/deploy/DeploymentScanner.java | 47 +++++++------------ .../jetty/deploy/internal/PathsApp.java | 4 +- ...y.java => PathsContextHandlerFactory.java} | 11 +++-- .../deploy/DeploymentScannerStartupTest.java | 28 +++++------ .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- .../src/test/resources/RFC2616Base.xml | 2 +- .../src/test/resources/deploy.xml | 2 +- 11 files changed, 48 insertions(+), 56 deletions(-) rename jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/{DefaultContextHandlerFactory.java => PathsContextHandlerFactory.java} (97%) diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 23cca3bed3e5..78e1596ebe56 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -12,7 +12,7 @@ - + diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index c6bbaab86232..996860d5be71 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -36,8 +36,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.eclipse.jetty.deploy.internal.DefaultContextHandlerFactory; import org.eclipse.jetty.deploy.internal.PathsApp; +import org.eclipse.jetty.deploy.internal.PathsContextHandlerFactory; import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; @@ -118,10 +118,10 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Cha private static final Logger LOG = LoggerFactory.getLogger(DeploymentScanner.class); private final Server server; - private final ContextHandlerDeployer contextManagement; + private final ContextHandlerDeployer contextHandlerDeployer; private final FilenameFilter filenameFilter; private final List monitoredDirs = new CopyOnWriteArrayList<>(); - private final DefaultContextHandlerFactory contextHandlerFactory = new DefaultContextHandlerFactory(); + private final PathsContextHandlerFactory contextHandlerFactory = new PathsContextHandlerFactory(); private final Map trackedApps = new HashMap<>(); private final Map environmentAttributesMap = new HashMap<>(); @@ -135,18 +135,18 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Cha public DeploymentScanner( @Name("server") Server server, - @Name("contextManagement") ContextHandlerDeployer contextManagement) + @Name("contextHandlerDeployer") ContextHandlerDeployer contextHandlerDeployer) { - this(server, contextManagement, null); + this(server, contextHandlerDeployer, null); } public DeploymentScanner( @Name("server") Server server, - @Name("contextManagement") ContextHandlerDeployer contextManagement, + @Name("contextHandlerDeployer") ContextHandlerDeployer contextHandlerDeployer, @Name("filenameFilter") FilenameFilter filter) { this.server = server; - this.contextManagement = Objects.requireNonNull(contextManagement); + this.contextHandlerDeployer = Objects.requireNonNull(contextHandlerDeployer); this.filenameFilter = Objects.requireNonNullElse(filter, new MonitoredPathFilter(monitoredDirs)); } @@ -332,18 +332,9 @@ public void pathsChanged(Map changeSet) Path path = entry.getKey(); PathsApp.State state = switch (entry.getValue()) { - case ADDED -> - { - yield PathsApp.State.ADDED; - } - case CHANGED -> - { - yield PathsApp.State.CHANGED; - } - case REMOVED -> - { - yield PathsApp.State.REMOVED; - } + case ADDED -> PathsApp.State.ADDED; + case CHANGED -> PathsApp.State.CHANGED; + case REMOVED -> PathsApp.State.REMOVED; }; // Using lower-case as defined by System Locale, as the files themselves from System FS. @@ -659,7 +650,7 @@ protected void performActions(List actions) { // Track removal removedApps.add(app); - contextManagement.undeploy(app.getContextHandler()); + contextHandlerDeployer.undeploy(app.getContextHandler()); } case ADD -> { @@ -679,13 +670,11 @@ protected void performActions(List actions) // combination of layered Environment Attributes with App Attributes overlaying them. Attributes envAttributes = environmentAttributesMap.get(appEnvironment); Attributes.Layer deployAttributes = new Attributes.Layer(envAttributes, app.getAttributes()); - deployAttributes.setAttribute(Deployable.MAIN_PATH, app.getMainPath()); - deployAttributes.setAttribute(Deployable.OTHER_PATHS, app.getPaths().keySet()); // Ensure that Environment configuration XMLs are listed in deployAttributes List envXmlPaths = findEnvironmentXmlPaths(deployAttributes); envXmlPaths.sort(PathCollators.byName(true)); - DefaultContextHandlerFactory.setEnvironmentXmlPaths(deployAttributes, envXmlPaths); + PathsContextHandlerFactory.setEnvironmentXmlPaths(deployAttributes, envXmlPaths); // Create the Context Handler ContextHandler contextHandler = contextHandlerFactory.newContextHandler(server, app, deployAttributes); @@ -693,7 +682,7 @@ protected void performActions(List actions) // Introduce the ContextHandler to the DeploymentManager startTracking(app); - contextManagement.deploy(app.getContextHandler()); + contextHandlerDeployer.deploy(app.getContextHandler()); } } } @@ -726,7 +715,7 @@ private List findEnvironmentXmlPaths(Attributes deployAttributes) { List rawEnvXmlPaths = deployAttributes.getAttributeNameSet() .stream() - .filter(k -> k.startsWith(DefaultContextHandlerFactory.ENVIRONMENT_XML)) + .filter(k -> k.startsWith(PathsContextHandlerFactory.ENVIRONMENT_XML)) .map(k -> Path.of((String)deployAttributes.getAttribute(k))) .toList(); @@ -1003,11 +992,11 @@ public void setContainerScanJarPattern(String pattern) *

        * * @param classname the classname for this environment's context deployable. - * @see DefaultContextHandlerFactory#CONTEXT_HANDLER_CLASS + * @see PathsContextHandlerFactory#CONTEXT_HANDLER_CLASS */ public void setContextHandlerClass(String classname) { - _environment.setAttribute(DefaultContextHandlerFactory.CONTEXT_HANDLER_CLASS, classname); + _environment.setAttribute(PathsContextHandlerFactory.CONTEXT_HANDLER_CLASS, classname); } /** @@ -1020,11 +1009,11 @@ public void setContextHandlerClass(String classname) *

        * * @param classname the default classname for this environment's context deployable. - * @see DefaultContextHandlerFactory#CONTEXT_HANDLER_CLASS_DEFAULT + * @see PathsContextHandlerFactory#CONTEXT_HANDLER_CLASS_DEFAULT */ public void setDefaultContextHandlerClass(String classname) { - _environment.setAttribute(DefaultContextHandlerFactory.CONTEXT_HANDLER_CLASS_DEFAULT, classname); + _environment.setAttribute(PathsContextHandlerFactory.CONTEXT_HANDLER_CLASS_DEFAULT, classname); } /** diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index 939c0ef18538..daeab3acd2b2 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -96,12 +96,12 @@ public void setContextHandler(ContextHandler contextHandler) public Environment getEnvironment() { - return (Environment)getAttributes().getAttribute(DefaultContextHandlerFactory.ENVIRONMENT); + return (Environment)getAttributes().getAttribute(PathsContextHandlerFactory.ENVIRONMENT); } public void setEnvironment(Environment env) { - getAttributes().setAttribute(DefaultContextHandlerFactory.ENVIRONMENT, env); + getAttributes().setAttribute(PathsContextHandlerFactory.ENVIRONMENT, env); } public String getEnvironmentName() diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java similarity index 97% rename from jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java rename to jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index 5a1535118dd4..bf563d30710a 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/DefaultContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -36,9 +36,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DefaultContextHandlerFactory +/** + * Creates a {@link ContextHandler} from a {@link PathsApp}. + */ +public class PathsContextHandlerFactory { - private static final Logger LOG = LoggerFactory.getLogger(DefaultContextHandlerFactory.class); + private static final Logger LOG = LoggerFactory.getLogger(PathsContextHandlerFactory.class); public static final String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; public static final String ENVIRONMENT = "jetty.deploy.environment"; @@ -178,8 +181,8 @@ public void initializeDefaults(Object context) } else { - DefaultContextHandlerFactory.this.initializeContextPath(contextHandler, xml); - DefaultContextHandlerFactory.this.initializeContextHandler(contextHandler, xml, attributes); + PathsContextHandlerFactory.this.initializeContextPath(contextHandler, xml); + PathsContextHandlerFactory.this.initializeContextHandler(contextHandler, xml, attributes); } } }; diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java index 3fb1f7097d44..fed1f30765f2 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java @@ -20,7 +20,7 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -import org.eclipse.jetty.deploy.internal.DefaultContextHandlerFactory; +import org.eclipse.jetty.deploy.internal.PathsContextHandlerFactory; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.FS; @@ -101,8 +101,8 @@ public void testStartupWithRelativeEnvironmentContext() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("core.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/core-context.xml", StandardOpenOption.CREATE_NEW); - Files.writeString(environments.resolve("core-other.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + ".other=etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); + Files.writeString(environments.resolve("core.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=etc/core-context.xml", StandardOpenOption.CREATE_NEW); + Files.writeString(environments.resolve("core-other.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + ".other=etc/core-context-other.xml", StandardOpenOption.CREATE_NEW); Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING); Files.copy(MavenPaths.findTestResourceFile("etc/core-context-other.xml"), jettyBase.resolve("etc/core-context-other.xml"), StandardCopyOption.REPLACE_EXISTING); @@ -130,10 +130,10 @@ public void testStartupWithAbsoluteEnvironmentContext() throws Exception FS.ensureDirExists(environments); Files.writeString(environments.resolve("core.properties"), - String.format("%s=%s%n", DefaultContextHandlerFactory.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), + String.format("%s=%s%n", PathsContextHandlerFactory.ENVIRONMENT_XML, MavenPaths.findTestResourceFile("etc/core-context.xml")), StandardOpenOption.CREATE_NEW); Files.writeString(environments.resolve("core-other.properties"), - String.format("%s=%s%n", (DefaultContextHandlerFactory.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), + String.format("%s=%s%n", (PathsContextHandlerFactory.ENVIRONMENT_XML + ".other"), MavenPaths.findTestResourceFile("etc/core-context-other.xml")), StandardOpenOption.CREATE_NEW); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); @@ -159,11 +159,11 @@ public void testNonEnvironmentPropertyFileNotApplied() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("non-env.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee8.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee9.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("ee10.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); - Files.writeString(environments.resolve("not-core.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("non-env.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee8.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee9.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("ee10.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); + Files.writeString(environments.resolve("not-core.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=some/file/that/should/be/ignored.txt"); jetty.copyWebapp("bar-core-context.properties", "bar.properties"); startJetty(); @@ -185,10 +185,10 @@ public void testPropertyOverriding() throws Exception Path environments = jettyBase.resolve("environments"); FS.ensureDirExists(environments); - Files.writeString(environments.resolve("core-a.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/a.xml"); - Files.writeString(environments.resolve("core-b.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/b.xml"); - Files.writeString(environments.resolve("core-c.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/c.xml"); - Files.writeString(environments.resolve("core-d.properties"), DefaultContextHandlerFactory.ENVIRONMENT_XML + "=etc/d.xml"); + Files.writeString(environments.resolve("core-a.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=etc/a.xml"); + Files.writeString(environments.resolve("core-b.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=etc/b.xml"); + Files.writeString(environments.resolve("core-c.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=etc/c.xml"); + Files.writeString(environments.resolve("core-d.properties"), PathsContextHandlerFactory.ENVIRONMENT_XML + "=etc/d.xml"); Path etc = jettyBase.resolve("etc"); FS.ensureDirExists(etc); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml index 002a03ba9e2d..539f6e7ae083 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/RFC2616Base.xml @@ -100,7 +100,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml index a2bcd0ab9114..3520f5b788de 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/deploy.xml @@ -18,7 +18,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml index 557c6a0c07bc..bf7d17f6e9ac 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/RFC2616Base.xml @@ -100,7 +100,7 @@ - + diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml index 9b65fae24630..391aaf9afe9f 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/resources/deploy.xml @@ -18,7 +18,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml index c679eb8fd88c..9da4e1a2f99b 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/RFC2616Base.xml @@ -84,7 +84,7 @@ - + diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml index 61f202d4f37a..e3d427f40409 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/resources/deploy.xml @@ -18,7 +18,7 @@ - + From d1d5d117ff317322e7d1abbd31a5771c6a05e155 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 11:48:01 -0600 Subject: [PATCH 070/104] Fix spotless --- javadoc/pom.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javadoc/pom.xml b/javadoc/pom.xml index be14d7609cf9..20fcf528a5ee 100644 --- a/javadoc/pom.xml +++ b/javadoc/pom.xml @@ -474,9 +474,7 @@ Eclipse Jetty API Doc - v${project.version} Eclipse Jetty API Doc - v${project.version} - - *.internal; *.internal.*; org.eclipse.jetty.quic.quiche; org.eclipse.jetty.quic.quiche.foreign.*; org.eclipse.jetty.quic.quiche.*; - + *.internal; *.internal.*; org.eclipse.jetty.quic.quiche; org.eclipse.jetty.quic.quiche.foreign.*; org.eclipse.jetty.quic.quiche.*; *.internal,*.internal.* From 5ff6708b70f1f886ae5930d08fe1883e8851082f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 11:48:23 -0600 Subject: [PATCH 071/104] Correct OSGi attribute behavior --- .../jetty/osgi/BundleContextProvider.java | 2 +- .../eclipse/jetty/osgi/BundleMetadata.java | 32 ++++++++++++++++--- .../jetty/ee10/osgi/boot/EE10Activator.java | 12 +++---- .../jetty/ee11/osgi/boot/EE11Activator.java | 14 ++++---- .../jetty/ee9/osgi/boot/EE9Activator.java | 12 +++---- 5 files changed, 48 insertions(+), 24 deletions(-) diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java index c9da45449f61..793130282545 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleContextProvider.java @@ -187,7 +187,7 @@ public boolean bundleAdded(Bundle bundle) throws Exception if (contextFilePath != null) { // set up the single context file for this deployment - metadata.getProperties().put(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); + metadata.getAttributes().setAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH, contextFilePath.toASCIIString()); } ContextHandler contextHandler = createContextHandler(metadata); List contextHandlers = _bundleMap.computeIfAbsent(bundle, b -> new ArrayList<>()); diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java index 511b3d474cbc..c4f9f46dbf16 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/BundleMetadata.java @@ -14,9 +14,12 @@ package org.eclipse.jetty.osgi; import java.nio.file.Path; -import java.util.Properties; +import java.util.Dictionary; +import java.util.Enumeration; import org.eclipse.jetty.osgi.util.Util; +import org.eclipse.jetty.server.Deployable; +import org.eclipse.jetty.util.Attributes; import org.osgi.framework.Bundle; /** @@ -27,7 +30,7 @@ public class BundleMetadata private final Bundle bundle; private final Path bundlePath; private final String contextPath; - private final Properties properties = new Properties(); + private final Attributes attributes = new Attributes.Mapped(); private final String pathToResourceBase; public BundleMetadata(Bundle bundle) throws Exception @@ -41,6 +44,27 @@ public BundleMetadata(Bundle bundle, String pathToResourceBase) throws Exception this.bundlePath = Util.getBundlePath(bundle); this.contextPath = Util.getContextPath(bundle); this.pathToResourceBase = pathToResourceBase; + + //copy selected bundle headers into the properties + Dictionary headers = bundle.getHeaders(); + Enumeration keys = headers.keys(); + while (keys.hasMoreElements()) + { + String key = keys.nextElement(); + String val = headers.get(key); + if (Deployable.DEFAULTS_DESCRIPTOR.equalsIgnoreCase(key) || OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH.equalsIgnoreCase(key)) + { + getAttributes().setAttribute(Deployable.DEFAULTS_DESCRIPTOR, val); + } + else if (OSGiWebappConstants.JETTY_WEB_XML_PATH.equalsIgnoreCase(key)) + { + getAttributes().setAttribute(key, val); + } + else if (OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH.equalsIgnoreCase(key)) + { + getAttributes().setAttribute(key, val); + } + } } public Bundle getBundle() @@ -68,8 +92,8 @@ public String getPathToResourceBase() return pathToResourceBase; } - public Properties getProperties() + public Attributes getAttributes() { - return properties; + return attributes; } } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java index ca7ff2e88b1a..5da38283045d 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/boot/EE10Activator.java @@ -290,7 +290,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -408,7 +408,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String requireTldBundles = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) @@ -418,7 +418,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Set up configuration from manifest headers //extra classpath - String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -458,7 +458,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -556,7 +556,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } //web.xml - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); @@ -565,7 +565,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } // webdefault-ee10.xml - tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); diff --git a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java index df91aa0a40fe..aec4d85b4202 100644 --- a/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java +++ b/jetty-ee11/jetty-ee11-osgi/jetty-ee11-osgi-boot/src/main/java/org/eclipse/jetty/ee11/osgi/boot/EE11Activator.java @@ -290,7 +290,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -399,7 +399,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } webApp.setConfigurations(Configurations.getKnown().stream() - .filter(c -> c.isEnabledByDefault()) + .filter(Configuration::isEnabledByDefault) .toArray(Configuration[]::new)); //Make a webapp classloader @@ -408,7 +408,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String requireTldBundles = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) @@ -418,7 +418,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Set up configuration from manifest headers //extra classpath - String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -458,7 +458,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -556,7 +556,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } //web.xml - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); @@ -565,7 +565,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } // webdefault-ee11.xml - tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java index 371fb3dcfbd0..105155f3cf2f 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/boot/EE9Activator.java @@ -290,7 +290,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun contextHandler.setClassLoader(classLoader); //Apply any context xml file - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); final URI contextXmlURI = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); if (contextXmlURI != null) @@ -408,7 +408,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Handle Require-TldBundle //This is a comma separated list of names of bundles that contain tlds that this webapp uses. //We add them to the webapp classloader. - String requireTldBundles = (String)metadata.getProperties().get(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); + String requireTldBundles = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.REQUIRE_TLD_BUNDLE); List pathsToTldBundles = Util.getPathsToBundlesBySymbolicNames(requireTldBundles, metadata.getBundle().getBundleContext()); for (Path p : pathsToTldBundles) { @@ -417,7 +417,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Set up configuration from manifest headers //extra classpath - String extraClasspath = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); + String extraClasspath = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_EXTRA_CLASSPATH); if (extraClasspath != null) webApp.setExtraClasspath(extraClasspath); @@ -457,7 +457,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun //Then look in the property OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH and apply the first one if (contextXmlURL == null) { - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_CONTEXT_FILE_PATH); if (tmp != null) { String[] filenames = tmp.split("[,;]"); @@ -555,7 +555,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } //web.xml - String tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_WEB_XML_PATH); + String tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_WEB_XML_PATH); if (!StringUtil.isBlank(tmp)) { URI webXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); @@ -564,7 +564,7 @@ public ContextHandler createContextHandler(AbstractContextProvider provider, Bun } // webdefault-ee9.xml - tmp = metadata.getProperties().getProperty(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); + tmp = (String)metadata.getAttributes().getAttribute(OSGiWebappConstants.JETTY_DEFAULT_WEB_XML_PATH); if (tmp != null) { URI defaultWebXml = Util.resolvePathAsLocalizedURI(tmp, metadata.getBundle(), jettyHomePath); From ef981793322466a3b10e1731daa46116cafab038 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 12:22:12 -0600 Subject: [PATCH 072/104] Updating names of variable in test --- .../eclipse/jetty/ee10/test/DeploymentErrorTest.java | 9 +++++---- .../src/test/resources/jetty-logging.properties | 1 + .../eclipse/jetty/ee11/test/DeploymentErrorTest.java | 10 +++++----- .../eclipse/jetty/ee9/test/DeploymentErrorTest.java | 9 +++++---- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java index dbe68a2b5e0e..60587a1efd7d 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/java/org/eclipse/jetty/ee10/test/DeploymentErrorTest.java @@ -102,11 +102,12 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); - DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee10"); + DeploymentScanner deploymentScanner = new DeploymentScanner(server, deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = deploymentScanner.configureEnvironment("ee10"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee10.webapp.WebAppContext"); - appProvider.setScanInterval(1); - appProvider.addMonitoredDirectory(docroots); + deploymentScanner.setScanInterval(1); + deploymentScanner.addMonitoredDirectory(docroots); + deploymentManager.addBean(deploymentScanner); server.addBean(deploymentManager); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/jetty-logging.properties b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/jetty-logging.properties index 0003cced627a..bdcb9912b520 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/jetty-logging.properties +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/jetty-logging.properties @@ -1,4 +1,5 @@ # Jetty Logging using jetty-slf4j-impl #org.eclipse.jetty.LEVEL=DEBUG +org.eclipse.jetty.deploy.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG #org.eclipse.jetty.util.ssl.KeyStoreScanner.LEVEL=DEBUG diff --git a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java index cbab42454ca5..eec23b03e8b9 100644 --- a/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java +++ b/jetty-ee11/jetty-ee11-tests/jetty-ee11-test-integration/src/test/java/org/eclipse/jetty/ee11/test/DeploymentErrorTest.java @@ -102,12 +102,12 @@ public Path startServer(Consumer docrootSetupConsumer) throws Exception } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); - DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee11"); + DeploymentScanner deploymentScanner = new DeploymentScanner(server, deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = deploymentScanner.configureEnvironment("ee11"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee11.webapp.WebAppContext"); - appProvider.setScanInterval(1); - appProvider.addMonitoredDirectory(docroots); - deploymentManager.addBean(appProvider); + deploymentScanner.setScanInterval(1); + deploymentScanner.addMonitoredDirectory(docroots); + deploymentManager.addBean(deploymentScanner); server.addBean(deploymentManager); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java index 25367e7fa5c9..f204141bd8ed 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-integration/src/test/java/org/eclipse/jetty/ee9/test/DeploymentErrorTest.java @@ -97,11 +97,12 @@ public Path startServer(Consumer docrootSetupConsumer, Path docroots) thro } System.setProperty("test.docroots", docroots.toAbsolutePath().toString()); - DeploymentScanner appProvider = new DeploymentScanner(server, deploymentManager); - DeploymentScanner.EnvironmentConfig envConfig = appProvider.configureEnvironment("ee9"); + DeploymentScanner deploymentScanner = new DeploymentScanner(server, deploymentManager); + DeploymentScanner.EnvironmentConfig envConfig = deploymentScanner.configureEnvironment("ee9"); envConfig.setContextHandlerClass("org.eclipse.jetty.ee9.webapp.WebAppContext"); - appProvider.setScanInterval(1); - appProvider.addMonitoredDirectory(docroots); + deploymentScanner.setScanInterval(1); + deploymentScanner.addMonitoredDirectory(docroots); + deploymentManager.addBean(deploymentScanner); server.addBean(deploymentManager); // Server handlers From 20d2e5f3065e29b5af8691621cd7dc608fc446fc Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:28:27 -0600 Subject: [PATCH 073/104] Add [tags] to deployment modules --- jetty-core/jetty-deploy/src/main/config/modules/deploy.mod | 3 +++ .../src/main/config/modules/deployment-manager.mod | 3 +++ .../jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod | 3 +++ .../jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod | 3 +++ .../jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod | 3 +++ .../jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod | 3 +++ 6 files changed, 18 insertions(+) diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod index 1dff1db7ef9e..d0b2d6b5f912 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod +++ b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod @@ -1,6 +1,9 @@ [description] This module enables web application context deployment from the `$JETTY_BASE/webapps` directory. +[tags] +deployment + [depend] server deployment-manager diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod b/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod index d9e73c73b41e..e2d6148e9685 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod +++ b/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod @@ -1,6 +1,9 @@ [description] This module enables the DeploymentManager +[tags] +deployment + [depend] server diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod index 30f04501fb69..91b3bf7ac279 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-deploy.mod @@ -3,6 +3,9 @@ This module enables webapp deployment from the `$JETTY_BASE/webapps` directory. # end::description[] +[tags] +deployment + [environment] ee10 diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod b/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod index a1637c403cb6..d87e50c21c57 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod +++ b/jetty-ee11/jetty-ee11-webapp/src/main/config/modules/ee11-deploy.mod @@ -3,6 +3,9 @@ This module enables webapp deployment from the `$JETTY_BASE/webapps` directory. # end::description[] +[tags] +deployment + [environment] ee11 diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod index 6897cd9484a2..5e5e39156988 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-deploy.mod @@ -3,6 +3,9 @@ This module enables webapp deployment from the `$JETTY_BASE/webapps` directory. # end::description[] +[tags] +deployment + [environment] ee8 diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod index 75b121460f2d..29c683741617 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-deploy.mod @@ -3,6 +3,9 @@ This module enables webapp deployment from the `$JETTY_BASE/webapps` directory. # end::description[] +[tags] +deployment + [environment] ee9 From 56fdc2964ae58dc5bd9b41844bcc67a6659760f3 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:30:29 -0600 Subject: [PATCH 074/104] Fix sibling check in WebInfConfiguration.unpack --- .../ee10/webapp/WebInfConfiguration.java | 21 ++++++++++++------- .../ee11/webapp/WebInfConfiguration.java | 21 ++++++++++++------- .../jetty/ee9/webapp/WebInfConfiguration.java | 21 ++++++++++++------- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java index 41a7ce0d0a92..d582c0156a52 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java @@ -208,16 +208,23 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use it's Path + // cannot be a CombinedResource, therefore safe to use its Path Path warPath = webApp.getPath(); - if (warPath != null) + + if (Files.isDirectory(warPath)) + { + // only working with a directory, nothing else to check. + extractedWebAppDir = warPath; + } + else if (FileID.isWebArchive(warPath)) { - // look for a sibling like "foo/" to a "foo.war" - if (FileID.isWebArchive(warPath) && Files.exists(warPath)) + // we are working with a war file, look for a directory with the same basename + // in the same directory as the war, that would be our extracted webapp directory. + String basename = FileID.getBasename(warPath); + Path sibling = warPath.getParent().resolve(basename); + if (Files.isDirectory(sibling) && Files.isWritable(sibling)) { - Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); - if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) - extractedWebAppDir = sibling; + extractedWebAppDir = sibling; } } } diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java index e090455ae8e7..aa0402a4f7e3 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java @@ -208,16 +208,23 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use it's Path + // cannot be a CombinedResource, therefore safe to use its Path Path warPath = webApp.getPath(); - if (warPath != null) + + if (Files.isDirectory(warPath)) + { + // only working with a directory, nothing else to check. + extractedWebAppDir = warPath; + } + else if (FileID.isWebArchive(warPath)) { - // look for a sibling like "foo/" to a "foo.war" - if (FileID.isWebArchive(warPath) && Files.exists(warPath)) + // we are working with a war file, look for a directory with the same basename + // in the same directory as the war, that would be our extracted webapp directory. + String basename = FileID.getBasename(warPath); + Path sibling = warPath.getParent().resolve(basename); + if (Files.isDirectory(sibling) && Files.isWritable(sibling)) { - Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); - if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) - extractedWebAppDir = sibling; + extractedWebAppDir = sibling; } } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java index 62f367b68ba6..302981e664b4 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java @@ -211,16 +211,23 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use it's Path + // cannot be a CombinedResource, therefore safe to use its Path Path warPath = webApp.getPath(); - if (warPath != null) + + if (Files.isDirectory(warPath)) + { + // only working with a directory, nothing else to check. + extractedWebAppDir = warPath; + } + else if (FileID.isWebArchive(warPath)) { - // look for a sibling like "foo/" to a "foo.war" - if (FileID.isWebArchive(warPath) && Files.exists(warPath)) + // we are working with a war file, look for a directory with the same basename + // in the same directory as the war, that would be our extracted webapp directory. + String basename = FileID.getBasename(warPath); + Path sibling = warPath.getParent().resolve(basename); + if (Files.isDirectory(sibling) && Files.isWritable(sibling)) { - Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); - if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) - extractedWebAppDir = sibling; + extractedWebAppDir = sibling; } } } From a07aafcbe3ab21c9caef0a553811fdd23c9e5e65 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:32:04 -0600 Subject: [PATCH 075/104] Module names in --list-modules should be left justified. --- .../src/main/java/org/eclipse/jetty/start/Modules.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java index bba1d015f22c..619d564f26af 100644 --- a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java +++ b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java @@ -200,7 +200,7 @@ public void listModules(PrintStream out, List tags) Optional max = _modules.stream().filter(filter).map(Module::getName).map(String::length).max(Integer::compareTo); if (max.isEmpty()) return; - String format = "%" + max.get() + "s - %s%s%n"; + String format = " %-" + max.get() + "s - %s%s%n"; Comparator comparator = wild ? Comparator.comparing(Module::getName) : Module::compareTo; AtomicReference tag = new AtomicReference<>(); From 9f34adc5a281cdb2689d97f8a353c705f5e9a0e4 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:32:37 -0600 Subject: [PATCH 076/104] Doing some debugging on RedispatchPlansTests behaviors --- .../jetty/tests/redispatch/AbstractRedispatchTest.java | 8 ++++++++ .../jetty/tests/redispatch/RedispatchPlansTests.java | 1 + 2 files changed, 9 insertions(+) diff --git a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/AbstractRedispatchTest.java b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/AbstractRedispatchTest.java index 6dc609184930..3efe5dd9a25b 100644 --- a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/AbstractRedispatchTest.java +++ b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/AbstractRedispatchTest.java @@ -88,6 +88,14 @@ public InitializedJettyBase(TestInfo testInfo) throws Exception Path startDir = jettyBase.resolve("start.d"); FS.ensureDirExists(startDir); + Path resourcesDir = jettyBase.resolve("resources"); + FS.ensureDirExists(resourcesDir); + String loggingText = """ + #org.eclipse.jetty.LEVEL=DEBUG + #org.eclipse.jetty.deploy.LEVEL=DEBUG + """; + Files.writeString(resourcesDir.resolve("jetty-logging.properties"), loggingText, StandardCharsets.UTF_8); + // Configure the DispatchPlanHandler Path ccdJar = distribution.resolveArtifact("org.eclipse.jetty.tests.ccd:ccd-common:jar:" + jettyVersion); Files.copy(ccdJar, libDir.resolve(ccdJar.getFileName())); diff --git a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java index ae3e5bba0d90..4a53b8b49327 100644 --- a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java +++ b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java @@ -76,6 +76,7 @@ public static Stream dispatchPlans() throws IOException List plans = new ArrayList<>(); List disabledTests = new ArrayList<>(); + disabledTests.add("disabled"); Path testPlansDir = MavenPaths.findTestResourceDir("plans"); try (Stream plansStream = Files.list(testPlansDir)) From f980a291835a5f8f6d07ee5d160b2226ffe0abfa Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:33:07 -0600 Subject: [PATCH 077/104] Correcting jetty.webapps property creation from PathsContextHandlerFactory --- .../jetty/deploy/internal/PathsContextHandlerFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index bf563d30710a..9bae9f707619 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -188,7 +188,7 @@ public void initializeDefaults(Object context) }; xmlConfiguration.getIdMap().put("Environment", environment.getName()); - xmlConfiguration.setJettyStandardIdsAndProperties(server, xml.getParent()); + xmlConfiguration.setJettyStandardIdsAndProperties(server, xml); // Put all Environment attributes into XmlConfiguration as properties that can be used. attributes.getAttributeNameSet() From 0daac6133c4730dfbe3a56658177e90e2e50dfac Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:33:20 -0600 Subject: [PATCH 078/104] Dump mainPath in PathsApp.toString --- .../main/java/org/eclipse/jetty/deploy/internal/PathsApp.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index daeab3acd2b2..eac209443e8e 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -265,6 +265,7 @@ public String toString() str.append("[").append(name); str.append("|").append(getState()); str.append(", env=").append(getEnvironmentName()); + str.append(", mainPath=").append(getMainPath()); str.append(", paths="); str.append(paths.entrySet().stream() .map((e) -> String.format("%s|%s", e.getKey(), e.getValue())) From 315f0748c33d9d7d53cb2623bdf34a274ddb9acd Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 14:33:31 -0600 Subject: [PATCH 079/104] core deploy should also have [tags] --- .../jetty-server/src/main/config/modules/core-deploy.mod | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod index 48470e95e611..4287df23d653 100644 --- a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod +++ b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod @@ -1,6 +1,9 @@ [description] Enables application based on core handlers deployed from the $JETTY_BASE/webapps/ directory. +[tags] +deployment + [environment] core From a1233bc3219db6c5d75a326ab9bd258f4bc8216f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 15:00:16 -0600 Subject: [PATCH 080/104] Revert "Fix sibling check in WebInfConfiguration.unpack" This reverts commit 56fdc2964ae58dc5bd9b41844bcc67a6659760f3. --- .../ee10/webapp/WebInfConfiguration.java | 21 +++++++------------ .../ee11/webapp/WebInfConfiguration.java | 21 +++++++------------ .../jetty/ee9/webapp/WebInfConfiguration.java | 21 +++++++------------ 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java index d582c0156a52..41a7ce0d0a92 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebInfConfiguration.java @@ -208,23 +208,16 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use its Path + // cannot be a CombinedResource, therefore safe to use it's Path Path warPath = webApp.getPath(); - - if (Files.isDirectory(warPath)) - { - // only working with a directory, nothing else to check. - extractedWebAppDir = warPath; - } - else if (FileID.isWebArchive(warPath)) + if (warPath != null) { - // we are working with a war file, look for a directory with the same basename - // in the same directory as the war, that would be our extracted webapp directory. - String basename = FileID.getBasename(warPath); - Path sibling = warPath.getParent().resolve(basename); - if (Files.isDirectory(sibling) && Files.isWritable(sibling)) + // look for a sibling like "foo/" to a "foo.war" + if (FileID.isWebArchive(warPath) && Files.exists(warPath)) { - extractedWebAppDir = sibling; + Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); + if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) + extractedWebAppDir = sibling; } } } diff --git a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java index aa0402a4f7e3..e090455ae8e7 100644 --- a/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java +++ b/jetty-ee11/jetty-ee11-webapp/src/main/java/org/eclipse/jetty/ee11/webapp/WebInfConfiguration.java @@ -208,23 +208,16 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use its Path + // cannot be a CombinedResource, therefore safe to use it's Path Path warPath = webApp.getPath(); - - if (Files.isDirectory(warPath)) - { - // only working with a directory, nothing else to check. - extractedWebAppDir = warPath; - } - else if (FileID.isWebArchive(warPath)) + if (warPath != null) { - // we are working with a war file, look for a directory with the same basename - // in the same directory as the war, that would be our extracted webapp directory. - String basename = FileID.getBasename(warPath); - Path sibling = warPath.getParent().resolve(basename); - if (Files.isDirectory(sibling) && Files.isWritable(sibling)) + // look for a sibling like "foo/" to a "foo.war" + if (FileID.isWebArchive(warPath) && Files.exists(warPath)) { - extractedWebAppDir = sibling; + Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); + if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) + extractedWebAppDir = sibling; } } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java index 302981e664b4..62f367b68ba6 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebInfConfiguration.java @@ -211,23 +211,16 @@ public void unpack(WebAppContext context) throws IOException if (war != null) { // We have obtained the webApp from the war string, so it - // cannot be a CombinedResource, therefore safe to use its Path + // cannot be a CombinedResource, therefore safe to use it's Path Path warPath = webApp.getPath(); - - if (Files.isDirectory(warPath)) - { - // only working with a directory, nothing else to check. - extractedWebAppDir = warPath; - } - else if (FileID.isWebArchive(warPath)) + if (warPath != null) { - // we are working with a war file, look for a directory with the same basename - // in the same directory as the war, that would be our extracted webapp directory. - String basename = FileID.getBasename(warPath); - Path sibling = warPath.getParent().resolve(basename); - if (Files.isDirectory(sibling) && Files.isWritable(sibling)) + // look for a sibling like "foo/" to a "foo.war" + if (FileID.isWebArchive(warPath) && Files.exists(warPath)) { - extractedWebAppDir = sibling; + Path sibling = warPath.getParent().resolve(FileID.getBasename(warPath)); + if (Files.exists(sibling) && Files.isDirectory(sibling) && Files.isWritable(sibling)) + extractedWebAppDir = sibling; } } } From b0edf97a2d72a99f1f12ca0245fb6441ca5f93ff Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 16:58:06 -0600 Subject: [PATCH 081/104] Sort test args coming from disk, for repeatable results. --- .../eclipse/jetty/tests/redispatch/RedispatchPlansTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java index 4a53b8b49327..b13b7a4a7225 100644 --- a/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java +++ b/tests/test-cross-context-dispatch/ccd-tests/src/test/java/org/eclipse/jetty/tests/redispatch/RedispatchPlansTests.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.tests.ccd.common.Property; import org.eclipse.jetty.tests.testers.JettyHomeTester; import org.eclipse.jetty.toolchain.test.MavenPaths; +import org.eclipse.jetty.util.resource.PathCollators; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestInfo; @@ -85,6 +86,7 @@ public static Stream dispatchPlans() throws IOException .filter(Files::isRegularFile) .filter((file) -> file.getFileName().toString().endsWith(".txt")) .filter((file) -> !disabledTests.contains(file.getFileName().toString())) + .sorted(PathCollators.byName(true)) .toList(); for (Path plansText : testPlans) From 0790cc5869274471c4431385209e9a6a9a25dfe2 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 3 Feb 2025 16:58:30 -0600 Subject: [PATCH 082/104] Move ATTRIBUTE_PREFIX from Deployable to PathsContextHandlerFactory --- .../jetty/deploy/internal/PathsApp.java | 8 +++++++ .../internal/PathsContextHandlerFactory.java | 24 +++++++++++++------ .../org/eclipse/jetty/server/Deployable.java | 2 -- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index eac209443e8e..f83ecd619b21 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.FileID; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.component.Environment; import org.eclipse.jetty.util.resource.PathCollators; import org.slf4j.Logger; @@ -234,6 +235,13 @@ public void loadProperties() LOG.warn("Unable to read properties file: {}", propFile, e); } } + + // Look for simple old school environment name. + String environmentName = (String)getAttributes().getAttribute(PathsContextHandlerFactory.ENVIRONMENT); + if (StringUtil.isNotBlank(environmentName)) + { + setEnvironment(Environment.get(environmentName)); + } } public void putPath(Path path, State state) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index 9bae9f707619..f195abeb7854 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -44,9 +44,10 @@ public class PathsContextHandlerFactory private static final Logger LOG = LoggerFactory.getLogger(PathsContextHandlerFactory.class); public static final String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; - public static final String ENVIRONMENT = "jetty.deploy.environment"; + public static final String ENVIRONMENT = "environment"; public static final String ENV_XML_PATHS = "jetty.deploy.defaultApp.envXMLs"; public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; + private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; private static Map asProperties(Attributes attributes) { @@ -54,8 +55,11 @@ private static Map asProperties(Attributes attributes) attributes.getAttributeNameSet().forEach((name) -> { Object value = attributes.getAttribute(name); - String key = name.startsWith(Deployable.ATTRIBUTE_PREFIX) - ? name.substring(Deployable.ATTRIBUTE_PREFIX.length()) + // Strip old "jetty.deploy.attribute." prefix if found. + // We no longer limit the properties to only those + // prefixed keys, we allow all keys through now. + String key = name.startsWith(ATTRIBUTE_PREFIX) + ? name.substring(ATTRIBUTE_PREFIX.length()) : name; props.put(key, Objects.toString(value)); }); @@ -256,11 +260,17 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path // pass through properties as attributes directly attributes.getAttributeNameSet().stream() - .filter((name) -> name.startsWith(Deployable.ATTRIBUTE_PREFIX)) - .forEach((name) -> + .map(key -> { - Object value = attributes.getAttribute(name); - String key = name.substring(Deployable.ATTRIBUTE_PREFIX.length()); + // strip older attribute prefix from key names. + if (key.startsWith(ATTRIBUTE_PREFIX)) + return key.substring(ATTRIBUTE_PREFIX.length()); + else + return key; + }) + .forEach((key) -> + { + Object value = attributes.getAttribute(key); if (LOG.isDebugEnabled()) LOG.debug("Setting attribute [{}] to [{}] in context {}", key, value, contextHandler); contextHandler.setAttribute(key, value); diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java index df4475c3a162..fee0b3536a97 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Deployable.java @@ -55,8 +55,6 @@ public interface Deployable return e1.compareTo(e2); }; - String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; - /** *

        Attribute key name: Temp Directory for context.

        * From 3d0fc382d780609a39d3e3ce3c28fecf9c52f4bf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 09:19:03 -0600 Subject: [PATCH 083/104] Cleanup attribute key names & value --- .../internal/PathsContextHandlerFactory.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index f195abeb7854..90fed2fbafba 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -45,8 +45,8 @@ public class PathsContextHandlerFactory public static final String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; public static final String ENVIRONMENT = "environment"; - public static final String ENV_XML_PATHS = "jetty.deploy.defaultApp.envXMLs"; public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; + public static final String ENVIRONMENT_XML_PATHS = ENVIRONMENT_XML + ".paths"; private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; private static Map asProperties(Attributes attributes) @@ -69,12 +69,12 @@ private static Map asProperties(Attributes attributes) public static List getEnvironmentXmlPaths(Attributes attributes) { //noinspection unchecked - return (List)attributes.getAttribute(ENV_XML_PATHS); + return (List)attributes.getAttribute(ENVIRONMENT_XML_PATHS); } public static void setEnvironmentXmlPaths(Attributes attributes, List paths) { - attributes.setAttribute(ENV_XML_PATHS, paths); + attributes.setAttribute(ENVIRONMENT_XML_PATHS, paths); } /** @@ -206,7 +206,7 @@ public void initializeDefaults(Object context) if (v == null) xmlConfiguration.getProperties().remove(k); else - xmlConfiguration.getProperties().put(k, Objects.toString(v)); + xmlConfiguration.getProperties().put(k, asPropertyValue(v)); }); // Run configure against appropriate classloader. @@ -229,6 +229,23 @@ public void initializeDefaults(Object context) } } + /** + * Convert an Object into a String suitable for use as a Properties value. + * + * @param obj the object to convert + * @return the String representing the Object, or null if {@code obj} is null. + */ + private static String asPropertyValue(Object obj) + { + if (obj == null) + return null; + if (obj instanceof Enum en) + return en.name(); + if (obj instanceof Environment env) + return env.getName(); + return Objects.toString(obj); + } + private ClassLoader getClassLoader(Object context, Environment environment) { ContextHandler contextHandler = getContextHandler(context); From c3fd737246032273c9867dbf6ec43be0f7e1fddb Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 13:35:46 -0600 Subject: [PATCH 084/104] Adding more Listener testing to Scanner --- .../java/org/eclipse/jetty/util/Scanner.java | 3 +- .../org/eclipse/jetty/util/ScannerTest.java | 874 ++++++++++++------ .../test/resources/jetty-logging.properties | 1 + 3 files changed, 613 insertions(+), 265 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java index 86cdc727c30e..e163433f09ec 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java @@ -46,7 +46,7 @@ /** * Scanner - * + *

        * Utility for scanning a directory for added, removed and changed * files and reporting these events via registered Listeners. * The scanner operates on the {@link Path#toRealPath(LinkOption...)} of the files scanned and @@ -269,7 +269,6 @@ public interface DiscreteListener extends Listener */ default void pathChanged(Path path) throws Exception { - path.toString(); fileChanged(path.toString()); } diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java index 01c6809846bf..769bd7846f0d 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ScannerTest.java @@ -23,9 +23,8 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; -import java.util.Objects; +import java.util.Map; import java.util.Set; -import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -36,12 +35,15 @@ import org.eclipse.jetty.util.component.LifeCycle; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -50,50 +52,9 @@ @ExtendWith(WorkDirExtension.class) public class ScannerTest { + private static final Logger LOG = LoggerFactory.getLogger(ScannerTest.class); public WorkDir workDir; - - public Path _directory; private Scanner _scanner; - private final BlockingQueue _queue = new LinkedBlockingQueue<>(); - private final BlockingQueue> _bulk = new LinkedBlockingQueue<>(); - - @BeforeEach - public void setupScanner() throws Exception - { - _directory = workDir.getEmptyPathDir(); - _scanner = new Scanner(); - _scanner.addDirectory(_directory); - _scanner.setScanInterval(0); - _scanner.setReportDirs(false); - _scanner.setReportExistingFilesOnStartup(false); - _scanner.addListener(new Scanner.DiscreteListener() - { - @Override - public void fileRemoved(String filename) - { - _queue.add(new Event(filename, Notification.REMOVED)); - } - - @Override - public void fileChanged(String filename) - { - _queue.add(new Event(filename, Notification.CHANGED)); - } - - @Override - public void fileAdded(String filename) - { - _queue.add(new Event(filename, Notification.ADDED)); - } - }); - _scanner.addListener((Scanner.BulkListener)_bulk::add); - - _scanner.start(); - _scanner.scan(); - - assertTrue(_queue.isEmpty()); - assertTrue(_bulk.isEmpty()); - } @AfterEach public void cleanup() throws Exception @@ -101,371 +62,758 @@ public void cleanup() throws Exception LifeCycle.stop(_scanner); } - static class Event + record Event(String filename, Scanner.Notification notification) { - String _filename; - Scanner.Notification _notification; - - public Event(String filename, Notification notification) - { - _filename = filename; - _notification = notification; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null || getClass() != obj.getClass()) - return false; - Event that = (Event)obj; - return _filename.equals(that._filename) && _notification == that._notification; - } - - @Override - public int hashCode() - { - return Objects.hash(_filename, _notification); - } - - @Override - public String toString() - { - return ("File: " + _filename + ":" + _notification); - } } @Test - public void testDepth() throws Exception + public void testDiscreteDepth() throws Exception { - Path root = _directory.resolve("root"); + Path root = workDir.getEmptyPathDir().resolve("root"); FS.ensureDirExists(root); - FS.touch(root.resolve("foo.foo")); - FS.touch(root.resolve("foo2.foo")); + touch(root.resolve("foo.foo")); + touch(root.resolve("foo2.foo")); Path dir = root.resolve("xxx"); FS.ensureDirExists(dir); - FS.touch(dir.resolve("xxx.foo")); - FS.touch(dir.resolve("xxx2.foo")); + touch(dir.resolve("xxx.foo")); + touch(dir.resolve("xxx2.foo")); Path dir2 = dir.resolve("yyy"); FS.ensureDirExists(dir2); - FS.touch(dir2.resolve("yyy.foo")); - FS.touch(dir2.resolve("yyy2.foo")); - - BlockingQueue queue = new LinkedBlockingQueue<>(); - Scanner scanner = new Scanner(); - scanner.setScanInterval(0); - scanner.setScanDepth(0); - scanner.setReportDirs(true); - scanner.setReportExistingFilesOnStartup(true); - scanner.addDirectory(root); - scanner.addListener(new Scanner.DiscreteListener() - { - @Override - public void fileRemoved(String filename) - { - queue.add(new Event(filename, Notification.REMOVED)); - } - - @Override - public void fileChanged(String filename) - { - queue.add(new Event(filename, Notification.CHANGED)); - } - - @Override - public void fileAdded(String filename) - { - queue.add(new Event(filename, Notification.ADDED)); - } - }); - - scanner.start(); + touch(dir2.resolve("yyy.foo")); + touch(dir2.resolve("yyy2.foo")); + + DiscreteQueueListener queue = new DiscreteQueueListener(); + _scanner = new Scanner(); + _scanner.setScanInterval(0); + _scanner.setScanDepth(0); + _scanner.setReportDirs(true); + _scanner.setReportExistingFilesOnStartup(true); + _scanner.addDirectory(root); + _scanner.addListener(queue); + + _scanner.start(); Event e = queue.take(); assertNotNull(e); - assertEquals(Notification.ADDED, e._notification); - assertTrue(e._filename.endsWith(root.getFileName().toString())); + assertEquals(Notification.ADDED, e.notification); + assertTrue(e.filename.endsWith(root.getFileName().toString())); queue.clear(); - scanner.stop(); - scanner.reset(); + _scanner.stop(); + _scanner.reset(); //Depth one should report the dir itself and its file and dir direct children - scanner.setScanDepth(1); - scanner.addDirectory(root); - scanner.start(); + _scanner.setScanDepth(1); + _scanner.addDirectory(root); + _scanner.start(); assertEquals(4, queue.size()); queue.clear(); - scanner.stop(); - scanner.reset(); + _scanner.stop(); + _scanner.reset(); //Depth 2 should report the dir itself, all file children, xxx and xxx's children - scanner.setScanDepth(2); - scanner.addDirectory(root); - scanner.start(); + _scanner.setScanDepth(2); + _scanner.addDirectory(root); + _scanner.start(); assertEquals(7, queue.size()); - scanner.stop(); + _scanner.stop(); } @Test - public void testPatterns() throws Exception + public void testDiscretePatterns() throws Exception { // test include and exclude patterns - Path root = _directory.resolve("proot"); + Path root = workDir.getEmptyPathDir().resolve("proot"); FS.ensureDirExists(root); Path ttt = root.resolve("ttt.txt"); - FS.touch(ttt); - FS.touch(root.resolve("ttt.foo")); + touch(ttt); + touch(root.resolve("ttt.foo")); Path dir = root.resolve("xxx"); FS.ensureDirExists(dir); Path x1 = dir.resolve("ttt.xxx"); - FS.touch(x1); + touch(x1); Path x2 = dir.resolve("xxx.txt"); - FS.touch(x2); + touch(x2); Path dir2 = dir.resolve("yyy"); FS.ensureDirExists(dir2); Path y1 = dir2.resolve("ttt.yyy"); - FS.touch(y1); + touch(y1); Path y2 = dir2.resolve("yyy.txt"); - FS.touch(y2); + touch(y2); - BlockingQueue queue = new LinkedBlockingQueue<>(); + DiscreteQueueListener queue = new DiscreteQueueListener(); //only scan the *.txt files for changes - Scanner scanner = new Scanner(); - IncludeExcludeSet pattern = scanner.addDirectory(root); + _scanner = new Scanner(); + IncludeExcludeSet pattern = _scanner.addDirectory(root); pattern.exclude(root.getFileSystem().getPathMatcher("glob:**/*.foo")); pattern.exclude(root.getFileSystem().getPathMatcher("glob:**/ttt.xxx")); - scanner.setScanInterval(0); - scanner.setScanDepth(2); //should never see any files from subdir yyy - scanner.setReportDirs(false); - scanner.setReportExistingFilesOnStartup(false); - scanner.addListener(new Scanner.DiscreteListener() - { - @Override - public void fileRemoved(String filename) - { - queue.add(new Event(filename, Notification.REMOVED)); - } - - @Override - public void fileChanged(String filename) - { - queue.add(new Event(filename, Notification.CHANGED)); - } - - @Override - public void fileAdded(String filename) - { - queue.add(new Event(filename, Notification.ADDED)); - } - }); - - scanner.start(); + _scanner.setScanInterval(0); + _scanner.setScanDepth(2); //should never see any files from subdir yyy + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(queue); + + _scanner.start(); assertTrue(queue.isEmpty()); Thread.sleep(1100); // make sure time in seconds changes - FS.touch(ttt); - FS.touch(x2); - FS.touch(x1); - FS.touch(y2); - scanner.scan(); - scanner.scan(); //2 scans for file to be considered settled + touch(ttt); + touch(x2); + touch(x1); + touch(y2); + _scanner.scan(); + _scanner.scan(); //2 scans for file to be considered settled List results = new ArrayList<>(); queue.drainTo(results); assertThat(results.size(), Matchers.equalTo(2)); for (Event e : results) { - assertTrue(e._filename.endsWith("ttt.txt") || e._filename.endsWith("xxx.txt")); + assertTrue(e.filename.endsWith("ttt.txt") || e.filename.endsWith("xxx.txt")); } } @Test - @Tag("Slow") - public void testAddedChangeRemove() throws Exception + public void testDiscreteAddedChangeRemove() throws Exception { - touch("a0"); + Path directory = workDir.getEmptyPathDir(); + DiscreteQueueListener discreteQueue = new DiscreteQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(discreteQueue); + _scanner.start(); + _scanner.scan(); + Path fileA0 = directory.resolve("a0"); + touch(fileA0); - // takes 2 scans to notice a0 and check that it is stable + // -- takes 2 scans to notice a0 and check that it is stable _scanner.scan(); _scanner.scan(); - Event event = _queue.poll(5, TimeUnit.SECONDS); + Event event = discreteQueue.poll(5, TimeUnit.SECONDS); assertNotNull(event, "Event should not be null"); - assertEquals(_directory.resolve("a0").toString(), event._filename); - assertEquals(Notification.ADDED, event._notification); + assertEquals(fileA0.toString(), event.filename); + assertEquals(Notification.ADDED, event.notification); - // add 3 more files + // -- add 3 more files Thread.sleep(1100); // make sure time in seconds changes - touch("a1"); - touch("a2"); - touch("a3"); - - // not stable after 1 scan so should not be seen yet. + Path fileA1 = directory.resolve("a1"); + Path fileA2 = directory.resolve("a2"); + Path fileA3 = directory.resolve("a3"); + touch(fileA1); + touch(fileA2); + touch(fileA3); + + // -- not stable after 1 scan so should not be seen yet _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); - // Keep a2 unstable and remove a3 before it stabilized + // -- Keep a2 unstable and remove a3 before it stabilized Thread.sleep(1100); // make sure time in seconds changes - touch("a2"); - delete("a3"); + touch(fileA2); + delete(fileA3); - // only a1 is stable so it should be seen, a3 is deleted + // -- only a1 is stable so it should be seen, a3 is deleted _scanner.scan(); List actualEvents = new ArrayList<>(); - _queue.drainTo(actualEvents); + discreteQueue.drainTo(actualEvents); assertEquals(2, actualEvents.size()); - Event a1 = new Event(_directory.resolve("a1").toString(), Notification.ADDED); - Event a3 = new Event(_directory.resolve("a3").toString(), Notification.REMOVED); - assertThat(actualEvents, Matchers.containsInAnyOrder(a1, a3)); - assertTrue(_queue.isEmpty()); + Event a1 = new Event(fileA1.toString(), Notification.ADDED); + Event a3 = new Event(fileA3.toString(), Notification.REMOVED); + assertThat(actualEvents, containsInAnyOrder(a1, a3)); + assertTrue(discreteQueue.isEmpty()); - // Now a2 is stable + // -- Now a2 is stable _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("a2").toString(), event._filename); - assertEquals(Notification.ADDED, event._notification); - assertTrue(_queue.isEmpty()); + assertEquals(fileA2.toString(), event.filename); + assertEquals(Notification.ADDED, event.notification); + assertTrue(discreteQueue.isEmpty()); - // touch a1 and a2 + // -- touch a1 and a2 Thread.sleep(1100); // make sure time in seconds changes - touch("a1"); - touch("a2"); - // not stable after 1scan so nothing should not be seen yet. + touch(fileA1); + touch(fileA2); + // -- not stable after 1scan so nothing should not be seen yet _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); - // Keep a2 unstable + // -- Keep a2 unstable Thread.sleep(1100); // make sure time in seconds changes - touch("a2"); + touch(fileA2); - // only a1 is stable so it should be seen. + // -- only a1 is stable so it should be seen _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("a1").toString(), event._filename); - assertEquals(Notification.CHANGED, event._notification); - assertTrue(_queue.isEmpty()); + assertEquals(fileA1.toString(), event.filename); + assertEquals(Notification.CHANGED, event.notification); + assertTrue(discreteQueue.isEmpty()); - // Now a2 is stable + // -- Now a2 is stable _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("a2").toString(), event._filename); - assertEquals(Notification.CHANGED, event._notification); - assertTrue(_queue.isEmpty()); + assertEquals(fileA2.toString(), event.filename); + assertEquals(Notification.CHANGED, event.notification); + assertTrue(discreteQueue.isEmpty()); - // delete a1 and a2 - delete("a1"); - delete("a2"); + // -- delete a1 and a2 + delete(fileA1); + delete(fileA2); - //Immediate notification of deletes. + // -- Immediate notification of deletes _scanner.scan(); - a1 = new Event(_directory.resolve("a1").toString(), Notification.REMOVED); - Event a2 = new Event(_directory.resolve("a2").toString(), Notification.REMOVED); + a1 = new Event(fileA1.toString(), Notification.REMOVED); + Event a2 = new Event(fileA2.toString(), Notification.REMOVED); actualEvents = new ArrayList<>(); - _queue.drainTo(actualEvents); + discreteQueue.drainTo(actualEvents); assertEquals(2, actualEvents.size()); - assertThat(actualEvents, Matchers.containsInAnyOrder(a1, a2)); - assertTrue(_queue.isEmpty()); + assertThat(actualEvents, containsInAnyOrder(a1, a2)); + assertTrue(discreteQueue.isEmpty()); - // recreate a2 - touch("a2"); + // -- recreate a2 + touch(fileA2); - // a2 not stable yet, shouldn't be seen + // -- a2 not stable yet, shouldn't be seen _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); - assertTrue(_queue.isEmpty()); + assertTrue(discreteQueue.isEmpty()); - //Now a2 is reported as ADDED. + // -- Now a2 is reported as ADDED _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("a2").toString(), event._filename); - assertEquals(Notification.ADDED, event._notification); - assertTrue(_queue.isEmpty()); + assertEquals(fileA2.toString(), event.filename); + assertEquals(Notification.ADDED, event.notification); + assertTrue(discreteQueue.isEmpty()); } @Test - public void testSizeChange() throws Exception + public void testDiscreteSizeChange() throws Exception { - touch("tsc0"); + Path directory = workDir.getEmptyPathDir(); + DiscreteQueueListener discreteQueue = new DiscreteQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(discreteQueue); + _scanner.start(); + _scanner.scan(); + Path fileTsc0 = directory.resolve("tsc0"); + touch(fileTsc0); + _scanner.scan(); _scanner.scan(); // takes 2 scans to notice tsc0 and check that it is stable. - Event event = _queue.poll(); + Event event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("tsc0").toString(), event._filename); - assertEquals(Notification.ADDED, event._notification); + assertEquals(fileTsc0.toString(), event.filename); + assertEquals(Notification.ADDED, event.notification); // Create a new file by writing to it. FileTime now = FileTime.from(Instant.now()); - Path file = _directory.resolve("st"); - try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) + Path fileSt = directory.resolve("st"); + try (OutputStream out = Files.newOutputStream(fileSt, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { out.write('x'); out.flush(); - FS.touch(file); - Files.setLastModifiedTime(file, now); + touch(fileSt); + Files.setLastModifiedTime(fileSt, now); // Not stable yet so no notification. _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); // Modify size only out.write('x'); out.flush(); - Files.setLastModifiedTime(file, now); + Files.setLastModifiedTime(fileSt, now); // Still not stable yet so no notification. _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); // now stable so finally see the ADDED _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("st").toString(), event._filename); - assertEquals(Notification.ADDED, event._notification); + assertEquals(directory.resolve("st").toString(), event.filename); + assertEquals(Notification.ADDED, event.notification); // Modify size only out.write('x'); out.flush(); - Files.setLastModifiedTime(file, now); + Files.setLastModifiedTime(fileSt, now); // Still not stable yet so no notification. _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNull(event); // now stable so finally see the ADDED _scanner.scan(); - event = _queue.poll(); + event = discreteQueue.poll(); assertNotNull(event); - assertEquals(_directory.resolve("st").toString(), event._filename); - assertEquals(Notification.CHANGED, event._notification); + assertEquals(fileSt.toString(), event.filename); + assertEquals(Notification.CHANGED, event.notification); + } + } + + @Test + public void testBulkPathsAdd() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkPathsQueueListener bulkQueue = new BulkPathsQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set paths = bulkQueue.poll(); + assertThat(paths, containsInAnyOrder(fileA0, fileA1)); + } + + @Test + public void testBulkPathsAddRemove() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkPathsQueueListener bulkQueue = new BulkPathsQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set paths = bulkQueue.poll(); + assertThat(paths, containsInAnyOrder(fileA0, fileA1)); + + // -- delete A0 + delete(fileA0); + _scanner.scan(); + + paths = bulkQueue.poll(); + assertThat(paths, containsInAnyOrder(fileA0)); + } + + @Test + public void testBulkPathsAddChange() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkPathsQueueListener bulkQueue = new BulkPathsQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set paths = bulkQueue.poll(); + assertThat(paths, containsInAnyOrder(fileA0, fileA1)); + + // -- touch A0 + touch(fileA0); + _scanner.scan(); + + paths = bulkQueue.poll(); + assertThat("The changes to A0 are not stable yet", paths, nullValue()); + + _scanner.scan(); + paths = bulkQueue.poll(); + assertThat(paths, containsInAnyOrder(fileA0)); + } + + @Test + public void testBulkFilesAdd() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkFilesQueueListener bulkQueue = new BulkFilesQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set files = bulkQueue.poll(); + assertThat(files, containsInAnyOrder(fileA0.toString(), fileA1.toString())); + } + + @Test + public void testBulkFilesAddRemove() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkFilesQueueListener bulkQueue = new BulkFilesQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set files = bulkQueue.poll(); + assertThat(files, containsInAnyOrder(fileA0.toString(), fileA1.toString())); + + // -- delete A0 + delete(fileA0); + _scanner.scan(); + + files = bulkQueue.poll(); + assertThat(files, containsInAnyOrder(fileA0.toString())); + } + + @Test + public void testBulkFilesAddChange() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + BulkFilesQueueListener bulkQueue = new BulkFilesQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(bulkQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + Set files = bulkQueue.poll(); + assertThat(files, containsInAnyOrder(fileA0.toString(), fileA1.toString())); + + // -- touch A0 + touch(fileA0); + _scanner.scan(); + + files = bulkQueue.poll(); + assertThat("The changes to A0 are not stable yet", files, nullValue()); + + _scanner.scan(); + files = bulkQueue.poll(); + assertThat(files, containsInAnyOrder(fileA0.toString())); + } + + @Test + public void testChangeSetAdd() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + ChangeSetQueueListener changeSetQueue = new ChangeSetQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(changeSetQueue); + _scanner.start(); + _scanner.scan(); + + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + List actualChanges = changeSetQueue.pollOrderedChanges(); + List expected = List.of( + "ADDED|" + fileA0, + "ADDED|" + fileA1 + ); + assertThat(actualChanges, ordered(expected)); + } + + @Test + public void testChangeSetAddRemove() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + ChangeSetQueueListener changeSetQueue = new ChangeSetQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(changeSetQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + List actualChanges = changeSetQueue.pollOrderedChanges(); + List expected = List.of( + "ADDED|" + fileA0, + "ADDED|" + fileA1 + ); + assertThat(actualChanges, ordered(expected)); + + // -- delete A0 + delete(fileA0); + _scanner.scan(); + + actualChanges = changeSetQueue.pollOrderedChanges(); + expected = List.of( + "REMOVED|" + fileA0 + ); + assertThat(actualChanges, ordered(expected)); + } + + @Test + public void testChangeSetAddChange() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + ChangeSetQueueListener changeSetQueue = new ChangeSetQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(changeSetQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + List actualChanges = changeSetQueue.pollOrderedChanges(); + List expected = List.of( + "ADDED|" + fileA0, + "ADDED|" + fileA1 + ); + assertThat(actualChanges, ordered(expected)); + + // -- touch A0 + touch(fileA0); + _scanner.scan(); + + actualChanges = changeSetQueue.pollOrderedChanges(); + assertThat("The changes to A0 are not stable yet", actualChanges, nullValue()); + + _scanner.scan(); + actualChanges = changeSetQueue.pollOrderedChanges(); + expected = List.of( + "CHANGED|" + fileA0 + ); + assertThat(actualChanges, ordered(expected)); + } + + @Test + public void testChangeSetAddChangeRemove() throws Exception + { + Path directory = workDir.getEmptyPathDir(); + ChangeSetQueueListener changeSetQueue = new ChangeSetQueueListener(); + _scanner = new Scanner(); + _scanner.addDirectory(directory); + _scanner.setScanInterval(0); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.addListener(changeSetQueue); + _scanner.start(); + _scanner.scan(); + + // -- create A0 and A1 + Path fileA0 = directory.resolve("A0"); + Path fileA1 = directory.resolve("A1"); + touch(fileA0); + touch(fileA1); + + _scanner.scan(); + _scanner.scan(); + List actualChanges = changeSetQueue.pollOrderedChanges(); + List expected = List.of( + "ADDED|" + fileA0, + "ADDED|" + fileA1 + ); + assertThat(actualChanges, ordered(expected)); + + // -- touch A0, and add B2 + touch(fileA0); + Path fileB2 = directory.resolve("B2"); + touch(fileB2); + _scanner.scan(); + + actualChanges = changeSetQueue.pollOrderedChanges(); + assertThat("The changes to A0 and B2 are not stable yet", actualChanges, nullValue()); + + // -- remove A1 + delete(fileA1); + + _scanner.scan(); + actualChanges = changeSetQueue.pollOrderedChanges(); + expected = List.of( + "ADDED|" + fileB2, + "CHANGED|" + fileA0, + "REMOVED|" + fileA1 + ); + assertThat(actualChanges, ordered(expected)); + } + + private static void delete(Path path) + { + FS.deleteFile(path); + } + + private static void touch(Path path) throws IOException + { + FS.touch(path); + } + + private static class DiscreteQueueListener extends LinkedBlockingQueue implements Scanner.DiscreteListener + { + private static final Logger LOG = LoggerFactory.getLogger(ScannerTest.LOG.getName() + "." + DiscreteQueueListener.class.getSimpleName()); + + @Override + public void fileRemoved(String filename) + { + LOG.debug("fileRemoved: {}", filename); + this.add(new Event(filename, Notification.REMOVED)); + } + + @Override + public void fileChanged(String filename) + { + LOG.debug("fileChanged: {}", filename); + this.add(new Event(filename, Notification.CHANGED)); + } + + @Override + public void fileAdded(String filename) + { + LOG.debug("fileAdded: {}", filename); + this.add(new Event(filename, Notification.ADDED)); } } - private void delete(String string) throws IOException + private static class BulkPathsQueueListener extends LinkedBlockingQueue> implements Scanner.BulkListener { - Path file = _directory.resolve(string); - Files.deleteIfExists(file); + private static final Logger LOG = LoggerFactory.getLogger(ScannerTest.LOG.getName() + "." + BulkPathsQueueListener.class.getSimpleName()); + + @Override + public void pathsChanged(Set paths) + { + LOG.debug("pathsChanged: {}", paths); + add(paths); + } + + @Override + public void filesChanged(Set filenames) + { + // not interested about this method + } + } + + private static class BulkFilesQueueListener extends LinkedBlockingQueue> implements Scanner.BulkListener + { + private static final Logger LOG = LoggerFactory.getLogger(ScannerTest.LOG.getName() + "." + BulkFilesQueueListener.class.getSimpleName()); + + @Override + public void filesChanged(Set filenames) + { + LOG.debug("filesChanged: {}", filenames); + add(filenames); + } } - private void touch(String string) throws IOException + private static class ChangeSetQueueListener extends LinkedBlockingQueue> implements Scanner.ChangeSetListener { - Path file = _directory.resolve(string); - FS.touch(file); + private static final Logger LOG = LoggerFactory.getLogger(ScannerTest.LOG.getName() + "." + ChangeSetQueueListener.class.getSimpleName()); + + @Override + public void pathsChanged(Map changeSet) + { + LOG.atDebug().addArgument(changeSet).log("pathsChanged: {}"); + add(changeSet); + } + + public List pollOrderedChanges() + { + Map changeSet = poll(); + if (changeSet == null) + return null; + return changeSet.entrySet().stream() + .map(e -> String.format("%s|%s", e.getValue(), e.getKey())) + .sorted() + .toList(); + } } } diff --git a/jetty-core/jetty-util/src/test/resources/jetty-logging.properties b/jetty-core/jetty-util/src/test/resources/jetty-logging.properties index 91055dc78f03..9b90bde3a110 100644 --- a/jetty-core/jetty-util/src/test/resources/jetty-logging.properties +++ b/jetty-core/jetty-util/src/test/resources/jetty-logging.properties @@ -1,6 +1,7 @@ #org.eclipse.jetty.util.LEVEL=DEBUG #org.eclipse.jetty.util.Utf8Appendable.LEVEL=DEBUG #org.eclipse.jetty.util.PathWatcher.LEVEL=DEBUG +#org.eclipse.jetty.util.ScannerTest.LEVEL=DEBUG #org.eclipse.jetty.util.resource.PathResource.LEVEL=DEBUG #org.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=DEBUG #org.eclipse.jetty.util.thread.ReservedThreadExecutor.LEVEL=DEBUG From b38aa974cefa7338027497c97d184d011eb1a4cd Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 14:10:55 -0600 Subject: [PATCH 085/104] Scanner Listener failures propagate --- .../java/org/eclipse/jetty/util/Scanner.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java index e163433f09ec..9efd60d68f34 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java @@ -669,7 +669,7 @@ public void startScanning() } else { - //just register the list of existing files and only report changes + // just register the list of existing files and only report changes _prevScan = scanFiles(); } @@ -904,6 +904,7 @@ private void reportAddition(Path path) if (path == null) return; + Throwable failure = null; for (Listener listener : _listeners) { try @@ -913,9 +914,11 @@ private void reportAddition(Path path) } catch (Throwable e) { + failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } + ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -928,6 +931,7 @@ private void reportRemoval(Path path) if (path == null) return; + Throwable failure = null; for (Listener listener : _listeners) { try @@ -937,9 +941,11 @@ private void reportRemoval(Path path) } catch (Throwable e) { + failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } + ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -952,6 +958,7 @@ private void reportChange(Path path) if (path == null) return; + Throwable failure = null; for (Listener listener : _listeners) { try @@ -961,9 +968,11 @@ private void reportChange(Path path) } catch (Throwable e) { + failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } + ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -976,6 +985,7 @@ private void reportChangeSet(Map changes) if (changes == null || changes.isEmpty()) return; + Throwable failure = null; for (Listener l : _listeners) { try @@ -985,9 +995,11 @@ private void reportChangeSet(Map changes) } catch (Throwable e) { + failure = ExceptionUtil.combine(failure, e); LOG.warn("{} failed on '{}'", l, changes, e); } } + ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -1000,6 +1012,7 @@ private void reportBulkChanges(Set paths) if (paths == null || paths.isEmpty()) return; + Throwable failure = null; for (Listener listener : _listeners) { try @@ -1009,9 +1022,11 @@ private void reportBulkChanges(Set paths) } catch (Throwable e) { + failure = ExceptionUtil.combine(failure, e); LOG.warn("{} failed on '{}'", listener, paths, e); } } + ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** From 77a5429220cce4a1131e6e48753e5051523b3010 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 14:11:39 -0600 Subject: [PATCH 086/104] Deployment fixes for throw on start behaviors --- .../jetty/deploy/DeploymentManager.java | 63 +++++++++---------- .../jetty/deploy/DeploymentScanner.java | 2 + .../jetty/tests/distribution/BadAppTests.java | 8 +++ 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 9ecb4feca1b3..97295f04974b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.Iterator; +import java.util.Locale; import java.util.Objects; import java.util.Queue; import java.util.Set; @@ -64,7 +65,6 @@ public class DeploymentManager extends ContainerLifeCycle implements ContextHand private final AutoLock _lock = new AutoLock(); private final DeploymentGraph _lifecycle = new DeploymentGraph(); private final Queue _tracked = new ConcurrentLinkedQueue<>(); - private Throwable _onStartupErrors; private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; @@ -103,7 +103,17 @@ public void deploy(ContextHandler contextHandler) if (LOG.isDebugEnabled()) LOG.debug("deploy: {}", contextHandler); TrackedContext trackedContext = startTracking(contextHandler); - requestContextHandlerGoal(trackedContext, DeploymentGraph.STARTED); + + if (getContexts().isRunning()) + { + // Only move to STARTED state if the ContextHandlerCollection itself is started. + requestContextHandlerGoal(trackedContext, DeploymentGraph.STARTED); + } + else + { + // Otherwise, just make sure it reaches the deployed state. + requestContextHandlerGoal(trackedContext, DeploymentGraph.DEPLOYED); + } } /** @@ -281,22 +291,9 @@ protected void doStart() throws Exception addLifeCycleBinding(new StandardUndeployer()); } - try (AutoLock l = _lock.lock()) - { - ExceptionUtil.ifExceptionThrow(_onStartupErrors); - } - super.doStart(); } - private void addOnStartupError(Throwable cause) - { - try (AutoLock l = _lock.lock()) - { - _onStartupErrors = ExceptionUtil.combine(_onStartupErrors, cause); - } - } - private TrackedContext findTrackedContext(ContextHandler contextHandler) { return _tracked.stream() @@ -363,25 +360,25 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) } catch (Throwable t) { - LOG.warn("Unable to reach node goal: {}", nodeName, t); - - // migrate to FAILED node - Node failed = _lifecycle.getNodeByName(DeploymentGraph.FAILED); - tracked.setLifeCycleNode(failed); - try - { - _lifecycle.runBindings(failed, tracked.contextHandler, this); - } - catch (Throwable cause) - { - // The runBindings failed for 'failed' node - LOG.trace("IGNORED", cause); - } + String message = nodeName.toUpperCase(Locale.ENGLISH) + " Deployment failed for " + tracked.contextHandler; + LOG.warn(message, t); + fail(tracked); + ExceptionUtil.ifExceptionThrowUnchecked(t); + } + } - if (isStarting()) - { - addOnStartupError(t); - } + private void fail(TrackedContext tracked) + { + Node failed = _lifecycle.getNodeByName(DeploymentGraph.FAILED); + tracked.setLifeCycleNode(failed); + try + { + _lifecycle.runBindings(failed, tracked.contextHandler, this); + } + catch (Throwable cause) + { + // The runBindings failed for 'failed' node + LOG.trace("IGNORED", cause); } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index 996860d5be71..7f004a86d8ba 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -42,6 +42,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.StringUtil; @@ -689,6 +690,7 @@ protected void performActions(List actions) catch (Throwable t) { LOG.warn("Failed to to perform action {} on {}", step.type(), app, t); + ExceptionUtil.ifExceptionThrowUnchecked(t); } finally { diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java index 086312d51fb3..8448c3d3a737 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java @@ -49,8 +49,10 @@ public class BadAppTests extends AbstractJettyHomeTest @CsvSource({"ee9", "ee10", "ee11"}) public void testXmlThrowOnUnavailableTrue(String env) throws Exception { + Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyBase(jettyBase) .jettyVersion(jettyVersion) .build(); @@ -88,8 +90,10 @@ public void testXmlThrowOnUnavailableTrue(String env) throws Exception @CsvSource({"ee9", "ee10", "ee11"}) public void testXmlThrowOnUnavailableFalse(String env) throws Exception { + Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyBase(jettyBase) .jettyVersion(jettyVersion) .build(); @@ -130,8 +134,10 @@ public void testXmlThrowOnUnavailableFalse(String env) throws Exception @CsvSource({"ee9", "ee10", "ee11"}) public void testNoXmlThrowOnUnavailableDefault(String env) throws Exception { + Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyBase(jettyBase) .jettyVersion(jettyVersion) .build(); @@ -161,8 +167,10 @@ public void testNoXmlThrowOnUnavailableDefault(String env) throws Exception @CsvSource({"ee9", "ee10", "ee11"}) public void testBadWebSocketWebapp(String env) throws Exception { + Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); JettyHomeTester distribution = JettyHomeTester.Builder.newInstance() + .jettyBase(jettyBase) .jettyVersion(jettyVersion) .build(); String mods = "resources,server,http," + From 5c930623c99ca7fb235d0a09d1c6dd2b985c166f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 16:27:48 -0600 Subject: [PATCH 087/104] Revert Scanner Listener rethrowing --- .../main/java/org/eclipse/jetty/util/Scanner.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java index 9efd60d68f34..420a516c7cc9 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/Scanner.java @@ -904,7 +904,6 @@ private void reportAddition(Path path) if (path == null) return; - Throwable failure = null; for (Listener listener : _listeners) { try @@ -914,11 +913,9 @@ private void reportAddition(Path path) } catch (Throwable e) { - failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } - ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -931,7 +928,6 @@ private void reportRemoval(Path path) if (path == null) return; - Throwable failure = null; for (Listener listener : _listeners) { try @@ -941,11 +937,9 @@ private void reportRemoval(Path path) } catch (Throwable e) { - failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } - ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -958,7 +952,6 @@ private void reportChange(Path path) if (path == null) return; - Throwable failure = null; for (Listener listener : _listeners) { try @@ -968,11 +961,9 @@ private void reportChange(Path path) } catch (Throwable e) { - failure = ExceptionUtil.combine(failure, e); warn(listener, path, e); } } - ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -985,7 +976,6 @@ private void reportChangeSet(Map changes) if (changes == null || changes.isEmpty()) return; - Throwable failure = null; for (Listener l : _listeners) { try @@ -995,11 +985,9 @@ private void reportChangeSet(Map changes) } catch (Throwable e) { - failure = ExceptionUtil.combine(failure, e); LOG.warn("{} failed on '{}'", l, changes, e); } } - ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** @@ -1012,7 +1000,6 @@ private void reportBulkChanges(Set paths) if (paths == null || paths.isEmpty()) return; - Throwable failure = null; for (Listener listener : _listeners) { try @@ -1022,11 +1009,9 @@ private void reportBulkChanges(Set paths) } catch (Throwable e) { - failure = ExceptionUtil.combine(failure, e); LOG.warn("{} failed on '{}'", listener, paths, e); } } - ExceptionUtil.ifExceptionThrowUnchecked(failure); } /** From b2ed72034195c12926e3463b0dd080b1f6162b70 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 4 Feb 2025 17:12:04 -0600 Subject: [PATCH 088/104] Adding startup failure tests for jetty-deploy --- .../src/main/config/etc/jetty-deploy.xml | 10 +- .../jetty/deploy/ContextHandlerDeployer.java | 17 +- .../jetty/deploy/DeploymentManager.java | 17 +- .../jetty/deploy/DeploymentScanner.java | 12 +- .../internal/PathsContextHandlerFactory.java | 2 +- .../AbstractContextHandlerDeployer.java | 12 -- .../DeploymentScannerCoreWebappTest.java | 180 ++++++++++++++++++ .../deploy/DeploymentScannerStartupTest.java | 4 +- .../example/ExampleBadSetServerHandler.java | 38 ++++ .../org/example/ExampleBadStartHandler.java | 37 ++++ 10 files changed, 298 insertions(+), 31 deletions(-) create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadSetServerHandler.java create mode 100644 jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadStartHandler.java diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml index 78e1596ebe56..abc03084441a 100644 --- a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -12,7 +12,7 @@ - + @@ -39,12 +39,4 @@ - - - - - - - - diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java index 03017fe16ad6..5d974df002c1 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ContextHandlerDeployer.java @@ -39,7 +39,9 @@ public interface ContextHandlerDeployer * * @param contextHandler the ContextHandler to add to the graph. */ - void addUndeployed(ContextHandler contextHandler); + default void addUndeployed(ContextHandler contextHandler) + { + } /** * Advanced usage, move a ContextHandler through the DeploymentManager graph by name. @@ -47,5 +49,16 @@ public interface ContextHandlerDeployer * @param contextHandler the ContextHandler to move * @param goalName the goal graph node by name */ - void move(ContextHandler contextHandler, String goalName); + default void move(ContextHandler contextHandler, String goalName) + { + } + + /** + * Report a failure to the ContextHandlerDeployer to fail startup. + * + * @param cause the cause of the failure + */ + default void reportStartupFailure(Throwable cause) + { + } } \ No newline at end of file diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 97295f04974b..0811210dd635 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -40,7 +40,6 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; -import org.eclipse.jetty.util.thread.AutoLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,11 +61,11 @@ public class DeploymentManager extends ContainerLifeCycle implements ContextHandlerDeployer { private static final Logger LOG = LoggerFactory.getLogger(DeploymentManager.class); - private final AutoLock _lock = new AutoLock(); private final DeploymentGraph _lifecycle = new DeploymentGraph(); private final Queue _tracked = new ConcurrentLinkedQueue<>(); private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; + private Throwable _onStartupErrors; /** * Add a DeploymentNodeBinding to the graph. @@ -292,6 +291,8 @@ protected void doStart() throws Exception } super.doStart(); + + ExceptionUtil.ifExceptionThrow(_onStartupErrors); } private TrackedContext findTrackedContext(ContextHandler contextHandler) @@ -363,10 +364,20 @@ private void requestContextHandlerGoal(TrackedContext tracked, String nodeName) String message = nodeName.toUpperCase(Locale.ENGLISH) + " Deployment failed for " + tracked.contextHandler; LOG.warn(message, t); fail(tracked); - ExceptionUtil.ifExceptionThrowUnchecked(t); + + if (isStarting()) + { + reportStartupFailure(t); + } } } + @Override + public void reportStartupFailure(Throwable cause) + { + _onStartupErrors = ExceptionUtil.combine(_onStartupErrors, cause); + } + private void fail(TrackedContext tracked) { Node failed = _lifecycle.getNodeByName(DeploymentGraph.FAILED); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index 7f004a86d8ba..2888a3631e01 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -42,7 +42,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; -import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.StringUtil; @@ -134,6 +133,14 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Cha private boolean deferInitialScan = false; private String defaultEnvironmentName; + public DeploymentScanner( + @Name("server") Server server, + @Name("deploymentManager") DeploymentManager deploymentManager) + { + this(server, deploymentManager, null); + deploymentManager.addBean(this); + } + public DeploymentScanner( @Name("server") Server server, @Name("contextHandlerDeployer") ContextHandlerDeployer contextHandlerDeployer) @@ -690,7 +697,8 @@ protected void performActions(List actions) catch (Throwable t) { LOG.warn("Failed to to perform action {} on {}", step.type(), app, t); - ExceptionUtil.ifExceptionThrowUnchecked(t); + if (isStarting()) + contextHandlerDeployer.reportStartupFailure(t); } finally { diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index 90fed2fbafba..7e57adb3809b 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -46,7 +46,7 @@ public class PathsContextHandlerFactory public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; public static final String ENVIRONMENT = "environment"; public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; - public static final String ENVIRONMENT_XML_PATHS = ENVIRONMENT_XML + ".paths"; + public static final String ENVIRONMENT_XML_PATHS = "jetty.deploy.paths.environmentXmls"; private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; private static Map asProperties(Attributes attributes) diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java index 02b6a2b2e2e9..270035d926c1 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AbstractContextHandlerDeployer.java @@ -31,16 +31,4 @@ public void undeploy(ContextHandler contextHandler) { // no default implementation } - - @Override - public void addUndeployed(ContextHandler contextHandler) - { - // no default implementation - } - - @Override - public void move(ContextHandler contextHandler, String goalName) - { - // no default implementation - } } diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java index 51988a4c9b9f..224c675471fb 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerCoreWebappTest.java @@ -22,7 +22,9 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -41,6 +43,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -121,6 +124,183 @@ public void testExampleCoreDir() throws Exception assertThat(responseBody, containsString("it all looks so easy.")); } + /** + * Test of a core deployment that will fail the DeploymentManager startup due to an exception triggered from the XML. + * This is at a point in time before the Core app is even added to the ContextHandlerCollection + */ + @Test + public void testFailureXml() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path webapps = baseDir.resolve("webapps"); + FS.ensureDirExists(webapps); + + Path demoDir = webapps.resolve("demo.d"); + FS.ensureDirExists(demoDir); + + Path srcZip = MavenPaths.targetDir().resolve("core-webapps/jetty-test-core-example-webapp.zip"); + Assertions.assertTrue(Files.exists(srcZip), "Src Zip should exist: " + srcZip); + unpack(srcZip, demoDir); + + // ensure that demo jar isn't in our test/server classpath. + // it should only exist in the jar file on disk. + assertThrows(ClassNotFoundException.class, () -> Class.forName("org.example.ExampleHandler")); + + Path demoXml = webapps.resolve("demo.xml"); + String demoXmlStr = """ + + + + /demo + + + + + """; + Files.writeString(demoXml, demoXmlStr); + + DeploymentManager deploymentManager = new DeploymentManager(); + DeploymentScanner defaultProvider = new DeploymentScanner(server, deploymentManager); + defaultProvider.addMonitoredDirectory(webapps); + DeploymentScanner.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); + coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); + + try (StacklessLogging ignore = new StacklessLogging(DeploymentScanner.class)) + { + Throwable throwable = assertThrows(Throwable.class, () -> startServer(deploymentManager)); + + // unwrap any ExecutionExceptions + while (throwable instanceof ExecutionException ee) + { + throwable = ee.getCause(); + } + + // Verify that we saw the message + assertThat(throwable, instanceOf(ClassNotFoundException.class)); + assertThat(throwable.getMessage(), is("org.example.BogusHandler")); + } + } + + /** + * Test of a core deployment that will fail DeploymentManager startup due to an exception during the + * ContextHandlerCollection.deployHandler() step of the core app. + */ + @Test + public void testFailureDeploy() throws IOException + { + Path baseDir = workDir.getEmptyPathDir(); + + Path webapps = baseDir.resolve("webapps"); + FS.ensureDirExists(webapps); + + Path demoDir = webapps.resolve("demo.d"); + FS.ensureDirExists(demoDir); + + Path srcZip = MavenPaths.targetDir().resolve("core-webapps/jetty-test-core-example-webapp.zip"); + Assertions.assertTrue(Files.exists(srcZip), "Src Zip should exist: " + srcZip); + unpack(srcZip, demoDir); + + // ensure that demo jar isn't in our test/server classpath. + // it should only exist in the jar file on disk. + assertThrows(ClassNotFoundException.class, () -> Class.forName("org.example.ExampleHandler")); + + Path demoXml = webapps.resolve("demo.xml"); + String demoXmlStr = """ + + + + /demo + + + + + """; + Files.writeString(demoXml, demoXmlStr); + + DeploymentManager deploymentManager = new DeploymentManager(); + DeploymentScanner defaultProvider = new DeploymentScanner(server, deploymentManager); + defaultProvider.addMonitoredDirectory(webapps); + DeploymentScanner.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); + coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); + + try (StacklessLogging ignore = new StacklessLogging( + // screwy name courtesy of SerializedInvoker.onError() logic + "org.eclipse.jetty.server.handler.ContextHandlerCollection$1", + DeploymentManager.class.getName())) + { + Throwable throwable = assertThrows(Throwable.class, () -> startServer(deploymentManager)); + + // unwrap any ExecutionExceptions + while (throwable instanceof ExecutionException ee) + { + throwable = ee.getCause(); + } + + // Verify that we saw the message + assertThat(throwable, instanceOf(RuntimeException.class)); + assertThat(throwable.getMessage(), is("Example of failing startup")); + } + } + + /** + * Test of a core deployment that will fail DeploymentManager startup due to an exception during the + * ContextHandler.doStart() step of the core app. + */ + @Test + public void testFailureDoStart() throws IOException + { + Path baseDir = workDir.getEmptyPathDir(); + + Path webapps = baseDir.resolve("webapps"); + FS.ensureDirExists(webapps); + + Path demoDir = webapps.resolve("demo.d"); + FS.ensureDirExists(demoDir); + + Path srcZip = MavenPaths.targetDir().resolve("core-webapps/jetty-test-core-example-webapp.zip"); + Assertions.assertTrue(Files.exists(srcZip), "Src Zip should exist: " + srcZip); + unpack(srcZip, demoDir); + + // ensure that demo jar isn't in our test/server classpath. + // it should only exist in the jar file on disk. + assertThrows(ClassNotFoundException.class, () -> Class.forName("org.example.ExampleHandler")); + + Path demoXml = webapps.resolve("demo.xml"); + String demoXmlStr = """ + + + + /demo + + + + + """; + Files.writeString(demoXml, demoXmlStr); + + DeploymentManager deploymentManager = new DeploymentManager(); + DeploymentScanner defaultProvider = new DeploymentScanner(server, deploymentManager); + defaultProvider.addMonitoredDirectory(webapps); + DeploymentScanner.EnvironmentConfig coreConfig = defaultProvider.configureEnvironment("core"); + coreConfig.setContextHandlerClass(CoreContextHandler.class.getName()); + + try (StacklessLogging ignore = new StacklessLogging(DeploymentManager.class)) + { + Throwable throwable = assertThrows(Throwable.class, () -> startServer(deploymentManager)); + + // unwrap any ExecutionExceptions + while (throwable instanceof ExecutionException ee) + { + throwable = ee.getCause(); + } + + // Verify that we saw the message + assertThat(throwable, instanceOf(RuntimeException.class)); + assertThat(throwable.getMessage(), is("Example of failing startup")); + } + } + private void unpack(Path srcPath, Path destPath) throws IOException { Map env = new HashMap<>(); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java index fed1f30765f2..318da2581c4a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java @@ -79,7 +79,7 @@ public void startJetty() throws Exception } @AfterEach - public void teardownEnvironment() throws Exception + public void stopJetty() throws Exception { jetty.stop(); } @@ -243,7 +243,7 @@ public void testPropertySubstitution() throws Exception private static void writeXmlDisplayName(Path filePath, String displayName) throws IOException { String content = """ - + @NAME@ diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadSetServerHandler.java b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadSetServerHandler.java new file mode 100644 index 000000000000..b2adf7ccc997 --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadSetServerHandler.java @@ -0,0 +1,38 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Callback; + +/** + * A Handler that will always fail to be added to the ContextHandlerCollection. + */ +public class ExampleBadSetServerHandler extends Handler.Abstract +{ + @Override + public boolean handle(Request request, Response response, Callback callback) + { + throw new IllegalStateException("This code should never have run, this Handler should have never started."); + } + + @Override + public void setServer(Server server) + { + throw new RuntimeException("Example of failing startup"); + } +} diff --git a/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadStartHandler.java b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadStartHandler.java new file mode 100644 index 000000000000..6c908dc1ea3f --- /dev/null +++ b/jetty-core/jetty-tests/jetty-test-core-example-webapp/src/main/java/org/example/ExampleBadStartHandler.java @@ -0,0 +1,37 @@ +// +// ======================================================================== +// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.example; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * A Handler that will always fail to startup. + */ +public class ExampleBadStartHandler extends Handler.Abstract +{ + @Override + public boolean handle(Request request, Response response, Callback callback) + { + throw new IllegalStateException("This code should never have run, this Handler should have never started."); + } + + @Override + protected void doStart() + { + throw new RuntimeException("Example of failing startup"); + } +} From 88b688bb4fe1946a23d6f25d17bb7b335045d4bf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 5 Feb 2025 07:52:58 -0600 Subject: [PATCH 089/104] Use renamed classname in testcase --- .../org/eclipse/jetty/tests/distribution/DistributionTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index fbbbee49a460..81c5e4d59759 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -1967,7 +1967,7 @@ public void testHTTP2ClientInCoreWebAppProvidedByServer() throws Exception Files.writeString(webapps.resolve(name + ".xml"), """ - + /test From 0587aac669b170173d3b6c86bf0e1a3db9ab4451 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 5 Feb 2025 09:43:19 -0600 Subject: [PATCH 090/104] Fixing attribute prefix stripping bug --- .../jetty/deploy/DeploymentScanner.java | 54 ++++++++++++++++++- .../jetty/deploy/internal/PathsApp.java | 7 ++- .../internal/PathsContextHandlerFactory.java | 22 ++------ .../main/config/etc/jetty-ee10-quickstart.xml | 6 +-- .../main/config/etc/jetty-ee11-quickstart.xml | 6 +-- .../main/config/etc/jetty-ee8-quickstart.xml | 8 +-- .../main/config/etc/jetty-ee9-quickstart.xml | 6 +-- 7 files changed, 75 insertions(+), 34 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index 2888a3631e01..6a6430d9fc32 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -116,6 +116,8 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.ChangeSetListener { private static final Logger LOG = LoggerFactory.getLogger(DeploymentScanner.class); + // old attributes prefix, now stripped. + private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; private final Server server; private final ContextHandlerDeployer contextHandlerDeployer; @@ -133,6 +135,15 @@ public class DeploymentScanner extends ContainerLifeCycle implements Scanner.Cha private boolean deferInitialScan = false; private String defaultEnvironmentName; + /** + *

        + * Convenience constructor to have DeploymentScanner participate in DeploymentManager startup behaviors, + * allowing failures in DeploymentScanner.start() to trigger a failure to start DeploymentManager. + *

        + * + * @param server the server reference to use for any XML based deployments. + * @param deploymentManager the DeploymentManager instance that is being used by this DeploymentScanner. + */ public DeploymentScanner( @Name("server") Server server, @Name("deploymentManager") DeploymentManager deploymentManager) @@ -141,6 +152,16 @@ public DeploymentScanner( deploymentManager.addBean(this); } + /** + *

        + * Construct a raw DeploymentScanner that will (periodically) scan specific directories for paths that can be + * used to construct webapps that will be submitted to the DeploymentManager for eventual deployment to + * it's configured destination. + *

        + * + * @param server the server reference to use for any XML based deployments. + * @param contextHandlerDeployer the ContextHandlerDeployer to use for deploying the created ContextHandlers. + */ public DeploymentScanner( @Name("server") Server server, @Name("contextHandlerDeployer") ContextHandlerDeployer contextHandlerDeployer) @@ -148,6 +169,17 @@ public DeploymentScanner( this(server, contextHandlerDeployer, null); } + /** + *

        + * Construct a raw DeploymentScanner that will (periodically) scan specific directories for paths that can be + * used to construct webapps that will be submitted to the DeploymentManager for eventual deployment to + * it's configured destination. + *

        + * + * @param server the server reference to use for any XML based deployments. + * @param contextHandlerDeployer the ContextHandlerDeployer to use for deploying the created ContextHandlers. + * @param filter A custom FilenameFilter to control what files the {@link Scanner} monitors for changes. + */ public DeploymentScanner( @Name("server") Server server, @Name("contextHandlerDeployer") ContextHandlerDeployer contextHandlerDeployer, @@ -158,6 +190,21 @@ public DeploymentScanner( this.filenameFilter = Objects.requireNonNullElse(filter, new MonitoredPathFilter(monitoredDirs)); } + /** + * Strip old {@code jetty.deploy.attribute.} prefix if found. + * We no longer limit the properties to only those prefixed keys, we allow all keys through now. + * + * @param key the key to possibly strip + * @return the stripped key + */ + public static String stripOldAttributePrefix(String key) + { + if (key.startsWith(ATTRIBUTE_PREFIX)) + return key.substring(ATTRIBUTE_PREFIX.length()); + else + return key; + } + /** * @param dir Directory to scan for deployable artifacts */ @@ -848,7 +895,12 @@ private Attributes loadEnvironmentAttributes(String env) throws IOException Attributes.Layer layer = new Attributes.Layer(attributesLayer); //put each property into our substitution pool - tmp.stringPropertyNames().forEach(k -> layer.setAttribute(k, tmp.getProperty(k))); + tmp.stringPropertyNames().forEach(name -> + { + String value = tmp.getProperty(name); + String key = stripOldAttributePrefix(name); + layer.setAttribute(key, value); + }); attributesLayer = layer; } } diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index f83ecd619b21..36980047afa4 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -228,7 +228,12 @@ public void loadProperties() Properties props = new Properties(); props.load(inputStream); props.stringPropertyNames().forEach( - (name) -> getAttributes().setAttribute(name, props.getProperty(name))); + (name) -> + { + String value = props.getProperty(name); + String key = DeploymentScanner.stripOldAttributePrefix(name); + getAttributes().setAttribute(key, value); + }); } catch (IOException e) { diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index 7e57adb3809b..b491e750289f 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -47,7 +47,6 @@ public class PathsContextHandlerFactory public static final String ENVIRONMENT = "environment"; public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; public static final String ENVIRONMENT_XML_PATHS = "jetty.deploy.paths.environmentXmls"; - private static final String ATTRIBUTE_PREFIX = "jetty.deploy.attribute."; private static Map asProperties(Attributes attributes) { @@ -55,13 +54,7 @@ private static Map asProperties(Attributes attributes) attributes.getAttributeNameSet().forEach((name) -> { Object value = attributes.getAttribute(name); - // Strip old "jetty.deploy.attribute." prefix if found. - // We no longer limit the properties to only those - // prefixed keys, we allow all keys through now. - String key = name.startsWith(ATTRIBUTE_PREFIX) - ? name.substring(ATTRIBUTE_PREFIX.length()) - : name; - props.put(key, Objects.toString(value)); + props.put(name, Objects.toString(value)); }); return props; } @@ -152,8 +145,7 @@ public ContextHandler newContextHandler(Server server, PathsApp app, Attributes ContextHandler contextHandler = getContextHandler(context); // Copy non-deploy attributes into ContextHandler attributes for context use - deployAttributes.getAttributeNameSet().stream() - .filter(k -> !k.startsWith("jetty.deploy.")) + deployAttributes.getAttributeNameSet() .forEach(k -> contextHandler.setAttribute(k, deployAttributes.getAttribute(k))); return contextHandler; @@ -276,15 +268,7 @@ protected void initializeContextHandler(ContextHandler contextHandler, Path path } // pass through properties as attributes directly - attributes.getAttributeNameSet().stream() - .map(key -> - { - // strip older attribute prefix from key names. - if (key.startsWith(ATTRIBUTE_PREFIX)) - return key.substring(ATTRIBUTE_PREFIX.length()); - else - return key; - }) + attributes.getAttributeNameSet() .forEach((key) -> { Object value = attributes.getAttribute(key); diff --git a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml index c92702f7a51c..19375a5d4ada 100644 --- a/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml +++ b/jetty-ee10/jetty-ee10-quickstart/src/main/config/etc/jetty-ee10-quickstart.xml @@ -10,19 +10,19 @@ ee10 - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + org.eclipse.jetty.quickstart.mode - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + org.eclipse.jetty.quickstart.origin - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + org.eclipse.jetty.quickstart.xml diff --git a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml index 555a385fc059..c13c723e27c1 100644 --- a/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml +++ b/jetty-ee11/jetty-ee11-quickstart/src/main/config/etc/jetty-ee11-quickstart.xml @@ -10,19 +10,19 @@ ee11 - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + org.eclipse.jetty.quickstart.mode - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + org.eclipse.jetty.quickstart.origin - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + org.eclipse.jetty.quickstart.xml diff --git a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml index 8eacb3b00bce..19576b3f6b4c 100644 --- a/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml +++ b/jetty-ee8/jetty-ee8-quickstart/src/main/config/etc/jetty-ee8-quickstart.xml @@ -8,21 +8,21 @@ - ee9 + ee8 - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + org.eclipse.jetty.quickstart.mode - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + org.eclipse.jetty.quickstart.origin - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + org.eclipse.jetty.quickstart.xml diff --git a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml index 3215a9ca1241..a90c845fdecf 100644 --- a/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml +++ b/jetty-ee9/jetty-ee9-quickstart/src/main/config/etc/jetty-ee9-quickstart.xml @@ -10,19 +10,19 @@ ee9 - jetty.deploy.attribute.org.eclipse.jetty.quickstart.mode + org.eclipse.jetty.quickstart.mode - jetty.deploy.attribute.org.eclipse.jetty.quickstart.origin + org.eclipse.jetty.quickstart.origin - jetty.deploy.attribute.org.eclipse.jetty.quickstart.xml + org.eclipse.jetty.quickstart.xml From 0f6665ca28f533712f2ba212d54778b8a077f6a8 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 5 Feb 2025 09:44:03 -0600 Subject: [PATCH 091/104] Cleanup java source --- .../jetty/deploy/DeploymentScanner.java | 46 ++++---- .../internal/PathsContextHandlerFactory.java | 104 +++++++++--------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java index 6a6430d9fc32..4c7a78bb1d30 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentScanner.java @@ -483,6 +483,14 @@ else if (isEnvironmentConfigPath(path)) performActions(actions); } + public void resetAppState(String name) + { + PathsApp app = findApp(name); + if (app == null) + return; + app.resetStates(); + } + @ManagedOperation(value = "Scan the monitored directories", impact = "ACTION") public void scan() { @@ -495,19 +503,6 @@ public void scan() scanner.nudge(); } - protected PathsApp findApp(String name) - { - return trackedApps.get(name); - } - - public void resetAppState(String name) - { - PathsApp app = findApp(name); - if (app == null) - return; - app.resetStates(); - } - @Override public String toString() { @@ -547,16 +542,6 @@ protected List buildActionList(List changedApps) return sortActions(actions); } - private void startTracking(PathsApp app) - { - trackedApps.put(app.getName(), app); - } - - private void stopTracking(PathsApp app) - { - trackedApps.remove(app.getName()); - } - @Override protected void doStart() throws Exception { @@ -651,6 +636,11 @@ protected boolean exists(String path) return scanner.exists(path); } + protected PathsApp findApp(String name) + { + return trackedApps.get(name); + } + protected boolean isEnvironmentConfigPath(Path path) { if (environmentsDir == null) @@ -908,6 +898,16 @@ private Attributes loadEnvironmentAttributes(String env) throws IOException return attributesLayer; } + private void startTracking(PathsApp app) + { + trackedApps.put(app.getName(), app); + } + + private void stopTracking(PathsApp app) + { + trackedApps.remove(app.getName()); + } + public record DeployAction(DeployAction.Type type, String name) { public enum Type diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index b491e750289f..8c5444a23069 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -41,12 +41,12 @@ */ public class PathsContextHandlerFactory { - private static final Logger LOG = LoggerFactory.getLogger(PathsContextHandlerFactory.class); public static final String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass"; public static final String CONTEXT_HANDLER_CLASS_DEFAULT = "jetty.deploy.default.contextHandlerClass"; public static final String ENVIRONMENT = "environment"; public static final String ENVIRONMENT_XML = "jetty.deploy.environmentXml"; public static final String ENVIRONMENT_XML_PATHS = "jetty.deploy.paths.environmentXmls"; + private static final Logger LOG = LoggerFactory.getLogger(PathsContextHandlerFactory.class); private static Map asProperties(Attributes attributes) { @@ -59,6 +59,23 @@ private static Map asProperties(Attributes attributes) return props; } + /** + * Convert an Object into a String suitable for use as a Properties value. + * + * @param obj the object to convert + * @return the String representing the Object, or null if {@code obj} is null. + */ + private static String asPropertyValue(Object obj) + { + if (obj == null) + return null; + if (obj instanceof Enum en) + return en.name(); + if (obj instanceof Environment env) + return env.getName(); + return Objects.toString(obj); + } + public static List getEnvironmentXmlPaths(Attributes attributes) { //noinspection unchecked @@ -221,36 +238,6 @@ public void initializeDefaults(Object context) } } - /** - * Convert an Object into a String suitable for use as a Properties value. - * - * @param obj the object to convert - * @return the String representing the Object, or null if {@code obj} is null. - */ - private static String asPropertyValue(Object obj) - { - if (obj == null) - return null; - if (obj instanceof Enum en) - return en.name(); - if (obj instanceof Environment env) - return env.getName(); - return Objects.toString(obj); - } - - private ClassLoader getClassLoader(Object context, Environment environment) - { - ContextHandler contextHandler = getContextHandler(context); - if (context != null) - { - ClassLoader classLoader = contextHandler.getClassLoader(); - if (classLoader != null) - return classLoader; - } - - return environment.getClassLoader(); - } - protected void initializeContextHandler(ContextHandler contextHandler, Path path, Attributes attributes) { if (LOG.isDebugEnabled()) @@ -351,6 +338,19 @@ private boolean applyEnvironmentXml(Server server, Object context, Environment e return true; } + private ClassLoader getClassLoader(Object context, Environment environment) + { + ContextHandler contextHandler = getContextHandler(context); + if (context != null) + { + ClassLoader classLoader = contextHandler.getClassLoader(); + if (classLoader != null) + return classLoader; + } + + return environment.getClassLoader(); + } + /** * Find the {@link ContextHandler} for the provided {@link Object} * @@ -377,6 +377,27 @@ private ContextHandler getContextHandler(Object context) return null; } + private void initializeDeployable(Object context, Attributes attributes) + { + // Ensure that WAR fallback String (that WebInfConfiguration needs) is + // only created once. + if (attributes.getAttribute(Deployable.WAR) == null) + { + Path mainPath = (Path)attributes.getAttribute(Deployable.MAIN_PATH); + if (FileID.isWebArchive(mainPath)) + { + // Set a backup value for the path to the war in case it hasn't already been set + // via a different means. This is especially important for a deployable App + // that is only a .war file (no XML). The eventual WebInfConfiguration + // will use this attribute. + attributes.setAttribute(Deployable.WAR, mainPath.toString()); + } + } + + if (context instanceof Deployable deployable) + deployable.initializeDefaults(attributes); + } + /** * Initialize a new Context object instance. * @@ -451,27 +472,6 @@ private Object newContextInstance(Server server, Environment environment, PathsA return null; } - private void initializeDeployable(Object context, Attributes attributes) - { - // Ensure that WAR fallback String (that WebInfConfiguration needs) is - // only created once. - if (attributes.getAttribute(Deployable.WAR) == null) - { - Path mainPath = (Path)attributes.getAttribute(Deployable.MAIN_PATH); - if (FileID.isWebArchive(mainPath)) - { - // Set a backup value for the path to the war in case it hasn't already been set - // via a different means. This is especially important for a deployable App - // that is only a .war file (no XML). The eventual WebInfConfiguration - // will use this attribute. - attributes.setAttribute(Deployable.WAR, mainPath.toString()); - } - } - - if (context instanceof Deployable deployable) - deployable.initializeDefaults(attributes); - } - private Object newInstance(String className) throws Exception { if (StringUtil.isBlank(className)) From 3aed6495aba1943196cf7348a99611991b4e2087 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 6 Feb 2025 09:32:02 -0600 Subject: [PATCH 092/104] Cleaning up module metadata --- ...eploy.xml => jetty-deployment-scanner.xml} | 0 .../src/main/config/modules/deploy.mod | 34 +++++-------------- .../{ => deployment}/deployment-manager.mod | 0 .../modules/deployment/deployment-scanner.mod | 31 +++++++++++++++++ .../jetty/deploy/DeploymentManagerTest.java | 2 +- .../DeploymentScannerDeferredStartupTest.java | 2 +- .../DeploymentScannerRuntimeUpdatesTest.java | 2 +- .../deploy/DeploymentScannerStartupTest.java | 2 +- .../src/test/resources/etc/webdefault.xml | 2 +- .../jetty/osgi/JettyBootstrapActivator.java | 2 +- .../src/main/config/modules/core-deploy.mod | 6 ++-- .../main/resources/demo/webdefault-ee11.xml | 2 +- .../src/test/config/etc/webdefault-ee10.xml | 2 +- .../jetty/ee10/osgi/test/TestOSGiUtil.java | 2 +- .../src/test/resources/webdefault-ee10.xml | 2 +- .../src/main/config/modules/ee10-deploy.mod | 4 +-- .../src/test/config/etc/webdefault-ee11.xml | 2 +- .../jetty/ee11/osgi/test/TestOSGiUtil.java | 2 +- .../src/test/resources/webdefault-ee11.xml | 2 +- .../src/main/config/modules/ee11-deploy.mod | 4 +-- .../src/test/config/etc/webdefault-ee8.xml | 2 +- .../src/main/config/modules/ee8-deploy.mod | 4 +-- .../src/test/config/etc/webdefault-ee9.xml | 2 +- .../jetty/ee9/osgi/test/TestOSGiUtil.java | 2 +- .../src/test/resources/webdefault-ee9.xml | 2 +- .../src/main/config/modules/ee9-deploy.mod | 4 +-- 26 files changed, 68 insertions(+), 53 deletions(-) rename jetty-core/jetty-deploy/src/main/config/etc/{jetty-deploy.xml => jetty-deployment-scanner.xml} (100%) rename jetty-core/jetty-deploy/src/main/config/modules/{ => deployment}/deployment-manager.mod (100%) create mode 100644 jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-scanner.mod diff --git a/jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-scanner.xml similarity index 100% rename from jetty-core/jetty-deploy/src/main/config/etc/jetty-deploy.xml rename to jetty-core/jetty-deploy/src/main/config/etc/jetty-deployment-scanner.xml diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod index d0b2d6b5f912..e411d68d48ff 100644 --- a/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod +++ b/jetty-core/jetty-deploy/src/main/config/modules/deploy.mod @@ -1,31 +1,13 @@ [description] -This module enables web application context deployment from the `$JETTY_BASE/webapps` directory. +Deprecated `deploy` module (does nothing on its own) + +[deprecated] +Module 'deprecated' is deprecated and does nothing +Use one of the environment specific modules '-deploy' instead. [tags] deployment +deprecated -[depend] -server -deployment-manager - -[lib] -lib/jetty-deploy-${jetty.version}.jar - -[files] -webapps/ - -[xml] -etc/jetty-deploy.xml - -[ini-template] -## Monitored directory name (relative to $jetty.base) -# jetty.deploy.monitoredDir=webapps - -# Defer Initial Scan -# true to have the initial scan deferred until the Server component is started. -# Note: deploy failures do not fail server startup in a deferred initial scan mode. -# false (default) to have initial scan occur as normal. -# jetty.deploy.deferInitialScan=false - -## Monitored directory scan period (seconds) -# jetty.deploy.scanInterval=0 +[depends] +deployment/deployment-scanner diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod b/jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-manager.mod similarity index 100% rename from jetty-core/jetty-deploy/src/main/config/modules/deployment-manager.mod rename to jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-manager.mod diff --git a/jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-scanner.mod b/jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-scanner.mod new file mode 100644 index 000000000000..efefdb4cbdd5 --- /dev/null +++ b/jetty-core/jetty-deploy/src/main/config/modules/deployment/deployment-scanner.mod @@ -0,0 +1,31 @@ +[description] +This module enables web application context scanning of the `$JETTY_BASE/webapps` directory. + +[tags] +deployment + +[depend] +server +deployment/deployment-manager + +[lib] +lib/jetty-deploy-${jetty.version}.jar + +[files] +webapps/ + +[xml] +etc/jetty-deployment-scanner.xml + +[ini-template] +## Monitored directory name (relative to $jetty.base) +# jetty.deploy.monitoredDir=webapps + +# Defer Initial Scan +# true to have the initial scan deferred until the Server component is started. +# Note: deploy failures do not fail server startup in a deferred initial scan mode. +# false (default) to have initial scan occur as normal. +# jetty.deploy.deferInitialScan=false + +## Monitored directory scan period (seconds) +# jetty.deploy.scanInterval=0 diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java index 1284273c7af5..afa0f0abb5b2 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerTest.java @@ -269,7 +269,7 @@ public void testXmlConfigured(WorkDir workDir) throws Exception jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); - jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-scanner.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Should not throw an Exception diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java index 51ce262efe6a..9ba5c5a9c05a 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java @@ -66,7 +66,7 @@ public void testDelayedDeploy() throws Exception jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); - jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-scanner.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Put a context into the base diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java index 6a8cc686b750..15b695a75b2f 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerRuntimeUpdatesTest.java @@ -82,7 +82,7 @@ public void startJetty() throws Exception jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); - jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-scanner.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Should not throw an Exception diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java index 318da2581c4a..1c3325dedc7e 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerStartupTest.java @@ -62,7 +62,7 @@ public void setupEnvironment() throws Exception jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-http.xml")); jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-manager.xml")); - jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deploy.xml")); + jetty.addConfiguration(MavenPaths.projectBase().resolve("src/main/config/etc/jetty-deployment-scanner.xml")); jetty.addConfiguration(MavenPaths.findTestResourceFile("jetty-core-deploy-custom.xml")); // Setup initial context diff --git a/jetty-core/jetty-deploy/src/test/resources/etc/webdefault.xml b/jetty-core/jetty-deploy/src/test/resources/etc/webdefault.xml index d1ff445e8b79..b420667d2c24 100644 --- a/jetty-core/jetty-deploy/src/test/resources/etc/webdefault.xml +++ b/jetty-core/jetty-deploy/src/test/resources/etc/webdefault.xml @@ -21,7 +21,7 @@ - + diff --git a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyBootstrapActivator.java b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyBootstrapActivator.java index 8e3037bc03a4..39f54435bf85 100644 --- a/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyBootstrapActivator.java +++ b/jetty-core/jetty-osgi/src/main/java/org/eclipse/jetty/osgi/JettyBootstrapActivator.java @@ -57,7 +57,7 @@ public class JettyBootstrapActivator implements BundleActivator /** * Set of config files to apply to a jetty Server instance if none are supplied by SYS_PROP_JETTY_ETC_FILES */ - public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deploy.xml"; + public static final String DEFAULT_JETTY_ETC_FILES = "etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployment-scanner.xml"; /** * Default location within bundle of a jetty home dir. diff --git a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod index 4287df23d653..97fd631fb4fd 100644 --- a/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod +++ b/jetty-core/jetty-server/src/main/config/modules/core-deploy.mod @@ -1,5 +1,7 @@ [description] -Enables application based on core handlers deployed from the $JETTY_BASE/webapps/ directory. +# tag::description[] +Scans and deploys `core` environment contexts from `$JETTY_BASE/webapps` directory. +# end::description[] [tags] deployment @@ -8,7 +10,7 @@ deployment core [depend] -deploy +deployment/deployment-scanner [xml] etc/jetty-core-deploy.xml diff --git a/jetty-demos/jetty-servlet6-demos/jetty-servlet6-demo-embedded/src/main/resources/demo/webdefault-ee11.xml b/jetty-demos/jetty-servlet6-demos/jetty-servlet6-demo-embedded/src/main/resources/demo/webdefault-ee11.xml index 9c57d4e0028e..7eb585e8a169 100644 --- a/jetty-demos/jetty-servlet6-demos/jetty-servlet6-demo-embedded/src/main/resources/demo/webdefault-ee11.xml +++ b/jetty-demos/jetty-servlet6-demos/jetty-servlet6-demo-embedded/src/main/resources/demo/webdefault-ee11.xml @@ -21,7 +21,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault-ee10.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault-ee10.xml index 644949f802ea..a7857f3de196 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault-ee10.xml +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/webdefault-ee10.xml @@ -21,7 +21,7 @@ - + diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java index fd34475cabe6..c9e6d55a008a 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java @@ -86,7 +86,7 @@ public static List
      * - * @param defer true to defer initial scan, false to have initial scan occur on startup of ScanningAppProvider. + * @param defer true to defer initial scan, false to have initial scan occur on startup of {@code DeploymentScanner}. */ public void setDeferInitialScan(boolean defer) { @@ -425,7 +425,7 @@ else if (isEnvironmentConfigPath(path)) } } - // Now we know the ScanTrackedApp instances that are changed by processing + // Now we know the PathsApp instances that are changed by processing // the incoming Scanner changes. // Now we want to convert this list of changes to a DeployAction list // that will perform the add/remove logic in a consistent way. @@ -711,8 +711,8 @@ protected void performActions(List actions) appEnvironment = getDefaultEnvironmentName(); app.setEnvironment(Environment.get(appEnvironment)); - // Create a new Attributes layer for the App deployment, which is the - // combination of layered Environment Attributes with App Attributes overlaying them. + // Create a new Attributes layer for the app deployment, which is the + // combination of layered Environment Attributes with app Attributes overlaying them. Attributes envAttributes = environmentAttributesMap.get(appEnvironment); Attributes.Layer deployAttributes = new Attributes.Layer(envAttributes, app.getAttributes()); diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java index 36980047afa4..402ccf1f4d97 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsApp.java @@ -36,9 +36,8 @@ import org.slf4j.LoggerFactory; /** - * A Default represents all the components that make up - * a from-file-system App deployment that the {@link DeploymentScanner} - * creates and uses. + * A representation of all the filesystem components that are used to + * create a {@link ContextHandler} and push it through the {@link org.eclipse.jetty.deploy.DeploymentManager}. */ public class PathsApp { @@ -194,8 +193,8 @@ public int hashCode() } /** - * Load all {@code properties} files belonging to this ScanTrackedApp - * into the {@link Attributes} for this App. + * Load all {@code properties} files belonging to this PathsApp + * into its {@link Attributes}. * * @see #getAttributes() */ @@ -308,7 +307,7 @@ public String toString() *
      All Path states are in REMOVED state, or there are no Paths being tracked
      * * - * @return the state of the App. + * @return the state. */ private State calcState() { diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java index 946c31e08cdf..c9426edd440d 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/internal/PathsContextHandlerFactory.java @@ -110,7 +110,7 @@ public ContextHandler newContextHandler(Server server, PathsApp app, Attributes // Can happen if the file existed when notified by scanner (as either an ADD or CHANGE), // and then the file was deleted before reaching this code. if (!Files.exists(mainPath)) - throw new IllegalStateException("App path does not exist " + mainPath); + throw new IllegalStateException("Main path does not exist " + mainPath); deployAttributes.setAttribute(Deployable.MAIN_PATH, mainPath); deployAttributes.setAttribute(Deployable.OTHER_PATHS, app.getPaths().keySet()); @@ -385,7 +385,7 @@ private void initializeDeployable(Object context, Attributes attributes) if (FileID.isWebArchive(mainPath)) { // Set a backup value for the path to the war in case it hasn't already been set - // via a different means. This is especially important for a deployable App + // via a different means. This is especially important for a deployable app // that is only a .war file (no XML). The eventual WebInfConfiguration // will use this attribute. attributes.setAttribute(Deployable.WAR, mainPath.toString()); @@ -404,13 +404,13 @@ private void initializeDeployable(Object context, Attributes attributes) *

      *
        *
      1. If app attribute {@link #CONTEXT_HANDLER_CLASS} is specified, use it, and initialize context
      2. - *
      3. If App deployable path is XML, apply XML {@code }
      4. + *
      5. If app deployable path is XML, apply XML {@code }
      6. *
      7. Fallback to environment attribute {@link #CONTEXT_HANDLER_CLASS_DEFAULT}, and initialize context.
      8. *
      * * @param environment the environment context applies to - * @param app the App for the context - * @param attributes the Attributes used to deploy the App + * @param app the app for the context + * @param attributes the Attributes used to deploy the app * @param path the path of the deployable * @return the Context Object. * @throws Exception if unable to create Object instance. diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java index a41e7645edf9..f0c392be8ff4 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentManagerLifeCycleRouteTest.java @@ -83,7 +83,7 @@ public void testStateTransitionReceive() throws Exception // Start DepMan depman.start(); - // Create new App + // Create new ContextHandler ContextHandler contextHandler = Util.createContextHandler("foo-webapp-1.war"); depman.addUndeployed(contextHandler); diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java index 9ba5c5a9c05a..64e2e4119590 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/DeploymentScannerDeferredStartupTest.java @@ -98,7 +98,7 @@ public void lifeCycleStarted(LifeCycle event) } if (event instanceof DeploymentScanner) { - eventQueue.add("ScanningAppProvider started"); + eventQueue.add("DeploymentScanner started"); } if (event instanceof Scanner) { @@ -164,7 +164,7 @@ public void beanRemoved(Container parent, Object child) String[] expectedOrderedEvents = { // The deepest component starts first "Scanner started", - "ScanningAppProvider started", + "DeploymentScanner started", "Server started", // We should see scan events after the server has started "Scan Started [1]",