Jetty 12.0.x deployment (#8208)

Deploy webapps for different environments from the same webapps directory.
The maximal environment known to the AppProviders is used as the default environment.
An explicit environment can be set in a properties file for an application, which is also used for property substitution in any context xml file.
This commit is contained in:
Greg Wilkins 2022-06-30 09:51:19 +10:00 committed by GitHub
parent 5304233b15
commit ea5bedf072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 879 additions and 822 deletions

View File

@ -90,6 +90,11 @@
<artifactId>jetty-deploy</artifactId>
<version>12.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
<version>12.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-client</artifactId>

View File

@ -56,5 +56,9 @@
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -15,11 +15,6 @@
<Set name="contexts">
<Ref refid="Contexts" />
</Set>
<!-- TODO move this to an environment -->
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jetty-jakarta-servlet-api-[^/]*\.jar$|.*/.*jakarta.servlet.jsp.jstl-.*\.jar$</Arg>
</Call>
<!-- Add a customize step to the deployment lifecycle -->
<!-- uncomment and replace DebugBinding with your extended AppLifeCycle.Binding class

View File

@ -10,18 +10,3 @@ lib/jetty-deploy-${jetty.version}.jar
[xml]
etc/jetty-deploy.xml
[ini-template]
# Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps
# - OR -
# Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps
# Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault.xml
# Monitored directory scan period (seconds)
# jetty.deploy.scanInterval=1
# Whether to extract *.war files
# jetty.deploy.extractWars=true

View File

@ -14,6 +14,7 @@
module org.eclipse.jetty.deploy
{
requires java.xml;
requires org.eclipse.jetty.ee;
requires org.eclipse.jetty.xml;
requires org.eclipse.jetty.server;
requires org.slf4j;

View File

@ -13,36 +13,59 @@
package org.eclipse.jetty.deploy;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.eclipse.jetty.deploy.util.FileID;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Attributes;
/**
* The information about an App that is managed by the {@link DeploymentManager}
* The information about an App that is managed by the {@link DeploymentManager}.
*/
public class App
{
private final DeploymentManager _manager;
private final AppProvider _provider;
private final String _environment;
private final String _filename;
private final Map<String, String> _properties = new HashMap<>();
private ContextHandler _context;
/**
* Create an App with specified Origin ID and archivePath
*
* <p>
* Any properties file that exists with the same {@link FileID#getDot3Basename(String)} 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 environment the name of the environment or null for the server environment.
* @param filename the filename of the base resource of the application
* @see App#getFilename()
* @see App#getContextPath()
*/
public App(DeploymentManager manager, AppProvider provider, String environment, String filename)
public App(DeploymentManager manager, AppProvider provider, String filename)
{
_manager = manager;
_provider = provider;
_environment = environment;
_filename = filename;
try
{
String basename = FileID.getDot3Basename(filename);
File properties = new File(basename + ".properties");
if (properties.exists())
{
Properties p = new Properties();
p.load(new FileInputStream(properties));
p.keySet().stream().map(Object::toString).forEach(k -> _properties.put(k, p.getProperty(k)));
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
/**
@ -61,6 +84,11 @@ public class App
return _provider;
}
public Map<String, String> getProperties()
{
return _properties;
}
/**
* Get ContextHandler for the App.
*
@ -74,19 +102,7 @@ public class App
public ContextHandler getContextHandler() throws Exception
{
if (_context == null)
{
_context = getAppProvider().createContextHandler(this);
Attributes.Mapped attributes = _manager.getContextAttributes();
if (attributes != null && attributes.size() > 0)
{
// Merge the manager attributes under the existing attributes
for (String name : attributes.getAttributeNameSet())
{
_context.setAttribute(name, attributes.getAttribute(name));
}
}
}
return _context;
}
@ -117,9 +133,16 @@ public class App
return _context == null ? null : _context.getContextPath();
}
public String getEnvironment()
/**
* Get the environment name.
* If the property "environment" exists, then that is returned as the environment, otherwise
* the {@link DeploymentManager#getDefaultEnvironmentName()} is returned.
* @return The {@link org.eclipse.jetty.util.component.Environment} name for the application.
*/
public String getEnvironmentName()
{
return _environment;
String name = getProperties().get(Deployable.ENVIRONMENT);
return name == null ? _manager.getDefaultEnvironmentName() : name;
}
/**
@ -135,6 +158,6 @@ public class App
@Override
public String toString()
{
return "App[" + _context + "," + _filename + "]";
return "App@%x[%s,%s,%s]".formatted(hashCode(), getEnvironmentName(), _context, _filename);
}
}

View File

@ -40,4 +40,10 @@ public interface AppProvider extends LifeCycle
* @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.
*/
String getEnvironmentName();
}

View File

@ -13,7 +13,6 @@
package org.eclipse.jetty.deploy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -32,18 +31,17 @@ 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.Path;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.MultiException;
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.resource.Resource;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.thread.AutoLock;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -125,11 +123,26 @@ public class DeploymentManager extends ContainerLifeCycle
private final List<AppProvider> _providers = new ArrayList<AppProvider>();
private final AppLifeCycle _lifecycle = new AppLifeCycle();
private final Queue<AppEntry> _apps = new ConcurrentLinkedQueue<AppEntry>();
private Attributes.Mapped _contextAttributes = new Attributes.Mapped();
private ContextHandlerCollection _contexts;
private boolean _useStandardBindings = true;
private String _defaultLifeCycleGoal = AppLifeCycle.STARTED;
/**
* Get the default {@link Environment} name for deployed applications.
* @return The name of environment known to the {@link AppProvider}s returned from
* {@link #getAppProviders()} that matches {@link Deployable#EE_ENVIRONMENT_NAME}.
* If multiple names match, then the maximal name, according to {@link Deployable#EE_ENVIRONMENT_COMPARATOR}
* is returned.
*/
public String getDefaultEnvironmentName()
{
return _providers.stream()
.map(AppProvider::getEnvironmentName)
.filter(Deployable.EE_ENVIRONMENT_NAME)
.max(Deployable.EE_ENVIRONMENT_COMPARATOR)
.orElse(null);
}
/**
* Receive an app for processing.
*
@ -139,7 +152,7 @@ public class DeploymentManager extends ContainerLifeCycle
*/
public void addApp(App app)
{
LOG.debug("Deployable added: {}", app.getFilename());
LOG.info("addApp: {}", app);
AppEntry entry = new AppEntry();
entry.app = app;
entry.setLifeCycleNode(_lifecycle.getNodeByName("undeployed"));
@ -373,22 +386,6 @@ public class DeploymentManager extends ContainerLifeCycle
return ret;
}
/**
* Get a contextAttribute that will be set for every Context deployed by this provider.
*
* @param name context attribute name
* @return the context attribute value
*/
public Object getContextAttribute(String name)
{
return _contextAttributes.getAttribute(name);
}
public Attributes.Mapped getContextAttributes()
{
return _contextAttributes;
}
@ManagedAttribute("Deployed Contexts")
public ContextHandlerCollection getContexts()
{
@ -421,6 +418,7 @@ public class DeploymentManager extends ContainerLifeCycle
*/
public void removeApp(App app)
{
LOG.info("removeApp: {}", app);
Iterator<AppEntry> it = _apps.iterator();
while (it.hasNext())
{
@ -430,7 +428,6 @@ public class DeploymentManager extends ContainerLifeCycle
if (!AppLifeCycle.UNDEPLOYED.equals(entry.lifecyleNode.getName()))
requestAppGoal(entry.app, AppLifeCycle.UNDEPLOYED);
it.remove();
LOG.debug("Deployable removed: {}", entry.app);
}
}
}
@ -450,16 +447,6 @@ public class DeploymentManager extends ContainerLifeCycle
}
}
/**
* Remove a contextAttribute that will be set for every Context deployed by this provider.
*
* @param name the context attribute name
*/
public void removeContextAttribute(String name)
{
_contextAttributes.removeAttribute(name);
}
/**
* 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.
@ -571,23 +558,6 @@ public class DeploymentManager extends ContainerLifeCycle
requestAppGoal(appentry, nodeName);
}
/**
* Set a contextAttribute that will be set for every Context deployed by this provider.
*
* @param name the context attribute name
* @param value the context attribute value
*/
public void setContextAttribute(String name, Object value)
{
_contextAttributes.setAttribute(name, value);
}
public void setContextAttributes(Attributes contextAttributes)
{
this._contextAttributes.clearAttributes();
this._contextAttributes.addAll(contextAttributes);
}
public void setContexts(ContextHandlerCollection contexts)
{
this._contexts = contexts;
@ -634,10 +604,4 @@ public class DeploymentManager extends ContainerLifeCycle
{
return _lifecycle.getNodes();
}
public void scope(XmlConfiguration xmlc, Resource webapp)
throws IOException
{
xmlc.setJettyStandardIdsAndProperties(getServer(), webapp);
}
}

View File

@ -1,114 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2022 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.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
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.Dumpable;
import org.eclipse.jetty.util.resource.Resource;
/**
* FileConfigurationManager
*
* Supplies properties defined in a file.
*/
@ManagedObject("Configure deployed webapps via properties")
public class PropertiesConfigurationManager implements ConfigurationManager, Dumpable
{
private String _properties;
private final Map<String, String> _map = new HashMap<>();
public PropertiesConfigurationManager(String properties)
{
if (properties != null)
{
try
{
setFile(properties);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
public PropertiesConfigurationManager()
{
this(null);
}
public void setFile(String resource) throws IOException
{
_properties = resource;
_map.clear();
loadProperties(_properties);
}
@ManagedAttribute("A file or URL of properties")
public String getFile()
{
return _properties;
}
@ManagedOperation("Set a property")
public void put(@Name("name") String name, @Name("value") String value)
{
_map.put(name, value);
}
@Override
public Map<String, String> getProperties()
{
return _map;
}
private void loadProperties(String resource) throws FileNotFoundException, IOException
{
Resource file = Resource.newResource(resource);
if (file != null && file.exists())
{
Properties properties = new Properties();
properties.load(file.getInputStream());
for (Map.Entry<Object, Object> entry : properties.entrySet())
_map.put(entry.getKey().toString(), String.valueOf(entry.getValue()));
}
}
@Override
public String toString()
{
return String.format("%s@%x{%s}", this.getClass(), hashCode(), _properties);
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, toString(), _map);
}
}

View File

@ -15,27 +15,23 @@ package org.eclipse.jetty.deploy.providers;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.ConfigurationManager;
import org.eclipse.jetty.deploy.util.FileID;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Loader;
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;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.slf4j.LoggerFactory;
@ -55,6 +51,7 @@ import org.slf4j.LoggerFactory;
* implements some heuristics to ignore some files found in the scans: <ul>
* <li>Hidden files (starting with ".") are ignored</li>
* <li>Directories with names ending in ".d" are ignored</li>
* <li>Property files with names ending in ".properties" are not deployed.</li>
* <li>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)</li>
* <li>If a directory and a matching XML file exist ( eg foo/ and foo.xml) then the directory is assumed to be
@ -62,20 +59,23 @@ import org.slf4j.LoggerFactory;
* <li>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.
* </ul>
* <p>
* Only {@link App}s discovered that report {@link App#getEnvironmentName()} matching this providers
* {@link #getEnvironmentName()} will be deployed.
* </p>
* <p>For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and
* properties for the webapp file as "jetty.webapp" and directory as "jetty.webapps".
* properties for the webapp file such as "jetty.webapp" and directory as "jetty.webapps".
* The properties will be initialized with:<ul>
* <li>The properties set on the application via {@link App#getProperties()}; otherwise:</li>
* <li>The properties set on this provider via {@link #getProperties()}</li>
* </ul>
*/
@ManagedObject("Provider for start-up deployement of webapps based on presence in directory")
@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 boolean _extract = false;
private boolean _parentLoaderPriority = false;
private ConfigurationManager _configurationManager;
private String _defaultsDescriptor;
private File _tempDirectory;
private String[] _configurationClasses;
private final Map<String, String> _properties = new HashMap<>();
public class Filter implements FilenameFilter
{
@ -87,32 +87,37 @@ public class ContextProvider extends ScanningAppProvider
String lowerName = name.toLowerCase(Locale.ENGLISH);
// TODO close resource!
Resource resource = Resource.newResource(new File(dir, name));
if (getMonitoredResources().stream().anyMatch(resource::isSame))
return false;
// ignore hidden files
if (lowerName.startsWith("."))
return false;
// Ignore some directories
if (resource.isDirectory())
try (Resource resource = Resource.newResource(new File(dir, name)))
{
// is it a nominated config directory
if (lowerName.endsWith(".d"))
if (getMonitoredResources().stream().anyMatch(resource::isSame))
return false;
// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
// ignore hidden files
if (lowerName.startsWith("."))
return false;
// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
// ignore property files
if (lowerName.endsWith(".properties"))
return false;
//is it a sccs dir?
return !"cvs".equals(lowerName) && !"cvsroot".equals(lowerName); // OK to deploy it then
// Ignore some directories
if (resource.isDirectory())
{
// is it a nominated config directory
if (lowerName.endsWith(".d"))
return false;
// is it an unpacked directory for an existing war file?
if (exists(name + ".war") || exists(name + ".WAR"))
return false;
// is it a directory for an existing xml file?
if (exists(name + ".xml") || exists(name + ".XML"))
return false;
//is it a sccs dir?
return !"cvs".equals(lowerName) && !"cvsroot".equals(lowerName); // OK to deploy it then
}
}
// else is it a war file
@ -131,161 +136,186 @@ public class ContextProvider extends ScanningAppProvider
setScanInterval(0);
}
/**
* Get the extractWars.
*
* @return the extractWars
* @deprecated use {@link #isExtract()}
*/
@Deprecated
public boolean isExtractWars()
public Map<String, String> getProperties()
{
return isExtract();
return _properties;
}
/**
* @return True if WAR and JAR are extraced on deploy.
* Get the extractWars.
* This is equivalent to getting the {@link Deployable#EXTRACT_WARS} property.
*
* @return the extractWars
*/
@ManagedAttribute("extract WAR and JAR files")
public boolean isExtract()
@ManagedAttribute("extract war files")
public boolean isExtractWars()
{
return _extract;
return Boolean.parseBoolean(_properties.get(Deployable.EXTRACT_WARS));
}
/**
* Set the extractWars.
* This is equivalent to setting the {@link Deployable#EXTRACT_WARS} property.
*
* @param extract the extractWars to set
* @deprecated use {@link #setExtract(boolean)}
* @param extractWars the extractWars to set
*/
@Deprecated
public void setExtractWars(boolean extract)
public void setExtractWars(boolean extractWars)
{
setExtract(extract);
}
/**
* Set to extract WAR and JAR files.
*
* @param extract the extractWars to set
*/
public void setExtract(boolean extract)
{
_extract = extract;
_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 _parentLoaderPriority;
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)
{
_parentLoaderPriority = 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()
{
return _defaultsDescriptor;
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)
{
_defaultsDescriptor = defaultsDescriptor;
}
public ConfigurationManager getConfigurationManager()
{
return _configurationManager;
_properties.put(Deployable.DEFAULTS_DESCRIPTOR, defaultsDescriptor);
}
/**
* Set the configurationManager.
*
* @param configurationManager the configurationManager to set
* This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property.
* @param configurations The configuration class names as a comma separated list
*/
public void setConfigurationManager(ConfigurationManager configurationManager)
public void setConfigurationClasses(String configurations)
{
updateBean(_configurationManager, configurationManager);
_configurationManager = configurationManager;
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)
{
_configurationClasses = configurations == null ? null : configurations.clone();
}
@ManagedAttribute("configuration classes for webapps to be processed through")
public String[] getConfigurationClasses()
{
return _configurationClasses;
_properties.put(Deployable.CONFIGURATION_CLASSES, (configurations == null)
? null
: String.join(",", configurations));
}
/**
* Set the Work directory where unpacked WAR files are managed from.
*
* 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()
{
String cc = _properties.get(Deployable.CONFIGURATION_CLASSES);
return cc == null ? new String[0] : cc.split(",");
}
/**
* Set the temporary directory for deployment.
* <p>
* Default is the same as the <code>java.io.tmpdir</code> System Property.
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(String directory)
{
_properties.put(Deployable.BASE_TEMP_DIR, directory);
}
/**
* Set the temporary directory for deployment.
* <p>
* This is equivalent to setting the {@link Deployable#BASE_TEMP_DIR} property.
* If not set, then the <code>java.io.tmpdir</code> System Property is used.
*
* @param directory the new work directory
*/
public void setTempDir(File directory)
{
_tempDirectory = directory;
_properties.put(Deployable.BASE_TEMP_DIR, directory.getAbsolutePath());
}
/**
* Get the user supplied Work Directory.
* Get the temporary directory for deployment.
* <p>
* This is equivalent to getting the {@link Deployable#BASE_TEMP_DIR} property.
*
* @return the user supplied work directory (null if user has not set Temp Directory yet)
*/
@ManagedAttribute("temp directory for use, null if no user set temp directory")
public File getTempDir()
{
return _tempDirectory;
String tmpDir = _properties.get(Deployable.BASE_TEMP_DIR);
return tmpDir == null ? null : new File(tmpDir);
}
protected void initializeWebAppContextDefaults(ContextHandler webapp)
protected ContextHandler initializeContextHandler(Object context, File file, Map<String, String> properties)
{
if (_defaultsDescriptor != null)
webapp.setAttribute("defaultsDescriptor", _defaultsDescriptor);
if (_tempDirectory != null)
// find the ContextHandler
ContextHandler contextHandler;
if (context instanceof ContextHandler handler)
contextHandler = handler;
else if (Supplier.class.isAssignableFrom(context.getClass()))
{
// TODO work out temp directory
// TODO set if the temp directory is persistent
webapp.setAttribute(Server.BASE_TEMP_DIR_ATTR, _tempDirectory);
@SuppressWarnings("unchecked")
Supplier<ContextHandler> provider = (Supplier<ContextHandler>)context;
contextHandler = provider.get();
}
else
{
throw new IllegalStateException("No ContextHandler for " + context);
}
assert contextHandler != null;
initializeContextPath(contextHandler, file.getName());
if (file.isDirectory())
contextHandler.setBaseResource(Resource.newResource(file));
if (context instanceof Deployable deployable)
deployable.initializeDefaults(properties);
return contextHandler;
}
@Override
public ContextHandler createContextHandler(final App app) throws Exception
{
Environment environment = Environment.get(app.getEnvironment());
Environment environment = Environment.get(app.getEnvironmentName());
if (LOG.isDebugEnabled())
LOG.debug("createContextHandler {} in {}", app, environment);
@ -295,52 +325,31 @@ public class ContextProvider extends ScanningAppProvider
{
Thread.currentThread().setContextClassLoader(environment.getClassLoader());
Resource resource = Resource.newResource(app.getFilename());
if (!resource.exists())
throw new IllegalStateException("App resource does not exist " + resource);
resource = unpack(resource); // TODO move unpacking to below.
// Create de-aliased file
File file = new File(app.getFilename()).toPath().toRealPath().toFile().getCanonicalFile().getAbsoluteFile();
if (!file.exists())
throw new IllegalStateException("App resource does not exist " + file);
File file = resource.getFile();
final String contextName = file.getName();
// Resource aliases (after getting name) to ensure baseResource is not an alias
if (resource.isAlias())
{
file = new File(resource.getAlias()).toPath().toRealPath().toFile();
resource = Resource.newResource(file);
if (!resource.exists())
throw new IllegalStateException("App resource does not exist " + resource);
}
// prepare properties
Map<String, String> properties = new HashMap<>();
properties.putAll(getProperties());
properties.putAll(app.getProperties());
// Handle a context XML file
if (resource.exists() && FileID.isXmlFile(file))
if (FileID.isXmlFile(file))
{
XmlConfiguration xmlc = new XmlConfiguration(resource)
XmlConfiguration xmlc = new XmlConfiguration(file, null, properties)
{
@Override
public void initializeDefaults(Object context)
{
super.initializeDefaults(context);
// If the XML created object is a ContextHandler
if (context instanceof ContextHandler contextHandler)
{
initializeWebAppContextDefaults(contextHandler);
initializeContextPath(contextHandler, contextName, true);
// TODO look for associated WAR file or directory
// TODO if it is a WAR file, then perhaps unpack it
// TODO set as default baseResource
}
ContextProvider.this.initializeContextHandler(context, file, properties);
}
};
xmlc.getIdMap().put("Environment", environment);
getDeploymentManager().scope(xmlc, resource);
if (getConfigurationManager() != null)
xmlc.getProperties().putAll(getConfigurationManager().getProperties());
xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), file);
Object context = xmlc.configure();
if (context instanceof ContextHandler contextHandler)
@ -367,24 +376,10 @@ public class ContextProvider extends ScanningAppProvider
Class<?> contextHandlerClass = Loader.loadClass(contextHandlerClassName);
if (contextHandlerClass == null)
throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app);
ContextHandler contextHandler;
if (Supplier.class.isAssignableFrom(contextHandlerClass))
{
@SuppressWarnings("unchecked")
Supplier<ContextHandler> provider = (Supplier<ContextHandler>)contextHandlerClass.getDeclaredConstructor().newInstance();
contextHandler = provider.get();
}
else
{
contextHandler = (ContextHandler)contextHandlerClass.getDeclaredConstructor().newInstance();
}
initializeWebAppContextDefaults(contextHandler);
initializeContextPath(contextHandler, contextName, !file.isDirectory());
// TODO unpack here (after temp directory is known)
contextHandler.setBaseResource(Resource.newResource(file.getAbsoluteFile()));
return contextHandler;
Object context = contextHandlerClass.getDeclaredConstructor().newInstance();
properties.put(Deployable.WAR, file.getCanonicalPath());
return initializeContextHandler(context, file, properties);
}
finally
{
@ -392,13 +387,10 @@ public class ContextProvider extends ScanningAppProvider
}
}
protected void initializeContextPath(ContextHandler context, String contextName, boolean stripExtension)
protected void initializeContextPath(ContextHandler context, String contextName)
{
String contextPath = contextName;
// Strip any 3 char extension from non directories
if (stripExtension && contextPath.length() > 4 && contextPath.charAt(contextPath.length() - 4) == '.')
contextPath = contextPath.substring(0, contextPath.length() - 4);
String contextPath = FileID.getDot3Basename(contextName);
// Ensure "/" is Not Trailing in context paths.
if (contextPath.endsWith("/") && contextPath.length() > 1)
@ -566,90 +558,4 @@ public class ContextProvider extends ScanningAppProvider
super.fileRemoved(filename);
}
public Resource unpack(Resource resourceBase) throws IOException
{
// Accept aliases for WAR files
if (resourceBase.isAlias())
{
if (LOG.isDebugEnabled())
LOG.debug("{} anti-aliased to {}", resourceBase, resourceBase.getAlias());
URI alias = resourceBase.getAlias();
resourceBase.close();
resourceBase = Resource.newResource(alias);
}
if (!isExtract() || resourceBase.isDirectory() || resourceBase.getFile() == null)
return resourceBase;
// is the extension a known extension
if (!resourceBase.getFile().getName().toLowerCase().endsWith(".war") &&
!resourceBase.getFile().getName().toLowerCase().endsWith(".jar"))
return resourceBase;
// Track the original web_app Resource, as this could be a PathResource.
// Later steps force the Resource to be a JarFileResource, which introduces
// URLConnection caches in such a way that it prevents Hot Redeployment
// on MS Windows.
Resource originalResource = resourceBase;
// Look for unpacked directory
Path path = resourceBase.getPath();
String name = path.getName(path.getNameCount() - 1).toString();
name = name.substring(0, name.length() - 4);
Path directory = path.getParent(); // TODO support unpacking to temp or work directory
File unpacked = directory.resolve(name).toFile();
File extractLock = directory.resolve(".extract_lock").toFile();
if (!Files.isWritable(directory))
{
LOG.warn("!Writable {} -> {}", resourceBase, directory);
return resourceBase;
}
// Does the directory already exist and is newer than the packed file?
if (unpacked.exists())
{
// If it is not a directory, then we can't unpack
if (!unpacked.isDirectory())
{
LOG.warn("Unusable {} -> {}", resourceBase, unpacked);
return resourceBase;
}
// If it is newer than the resource base and there is no partial extraction, then use it.
if (Files.getLastModifiedTime(directory).toMillis() >= resourceBase.lastModified() && !extractLock.exists())
{
if (LOG.isDebugEnabled())
LOG.debug("Reuse {} -> {}", resourceBase, unpacked);
resourceBase.close();
return Resource.newResource(unpacked);
}
extractLock.createNewFile();
IO.delete(unpacked);
}
else
{
extractLock.createNewFile();
}
if (!unpacked.mkdir())
{
LOG.warn("Cannot Create {} -> {}", resourceBase, unpacked);
extractLock.delete();
return resourceBase;
}
LOG.debug("Unpack {} -> {}", resourceBase, unpacked);
try (Resource jar = JarResource.newJarResource(resourceBase))
{
jar.copyTo(unpacked);
}
extractLock.delete();
resourceBase.close();
return Resource.newResource(unpacked);
}
}

View File

@ -32,6 +32,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.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Environment;
import org.eclipse.jetty.util.resource.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,7 +50,7 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
private int _scanInterval = 10;
private Scanner _scanner;
private boolean _useRealPaths;
private String _defaultEnvironment = "ee9"; // TODO null or ee10?
private String _environmentName;
private final Scanner.DiscreteListener _scannerListener = new Scanner.DiscreteListener()
{
@ -83,14 +84,15 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
addBean(_appMap);
}
public String getDefaultEnvironment()
@Override
public String getEnvironmentName()
{
return _defaultEnvironment;
return _environmentName;
}
public void setDefaultEnvironment(String defaultEnvironment)
public void setEnvironmentName(String environmentName)
{
_defaultEnvironment = defaultEnvironment;
_environmentName = environmentName;
}
/**
@ -135,10 +137,17 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
*/
protected App createApp(String filename)
{
// TODO otherways to work out the environment????
String environment = getDefaultEnvironment();
App app = new App(_deploymentManager, this, filename);
return new App(_deploymentManager, this, environment, filename);
// Only deploy apps for this environment
if (app.getEnvironmentName().equals(getEnvironmentName()))
return app;
boolean appProvider4env = _deploymentManager.getAppProviders().stream()
.map(AppProvider::getEnvironmentName).anyMatch(app.getEnvironmentName()::equals);
if (!appProvider4env)
LOG.warn("No AppProvider with environment {} for {}", app.getEnvironmentName(), app);
return null;
}
@Override
@ -148,8 +157,19 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
LOG.debug("{}.doStart()", this.getClass().getSimpleName());
if (_monitored.size() == 0)
throw new IllegalStateException("No configuration dir specified");
if (_environmentName == null)
{
List<Environment> 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();
}
LOG.info("Deployment monitor {}", _monitored);
Environment environment = Environment.get(_environmentName);
if (environment == null)
throw new IllegalStateException("Unknown environment " + _environmentName);
LOG.info("Deployment monitor {} in {} at intervals {}s", getEnvironmentName(), _monitored, getScanInterval());
List<File> files = new ArrayList<>();
for (Resource resource : _monitored)
{
@ -191,9 +211,10 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
protected void fileAdded(String filename) throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("added {}", filename);
App app = ScanningAppProvider.this.createApp(filename);
if (LOG.isDebugEnabled())
LOG.debug("fileAdded {}: {}", filename, app);
if (app != null)
{
_appMap.put(filename, app);
@ -203,14 +224,12 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
protected void fileChanged(String filename) throws Exception
{
App oldApp = _appMap.remove(filename);
if (oldApp != null)
_deploymentManager.removeApp(oldApp);
App app = ScanningAppProvider.this.createApp(filename);
if (LOG.isDebugEnabled())
LOG.debug("changed {}", filename);
App app = _appMap.remove(filename);
if (app != null)
{
_deploymentManager.removeApp(app);
}
app = ScanningAppProvider.this.createApp(filename);
LOG.debug("fileChanged {}: {}", filename, app);
if (app != null)
{
_appMap.put(filename, app);
@ -220,9 +239,9 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
protected void fileRemoved(String filename) throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("removed {}", filename);
App app = _appMap.remove(filename);
if (LOG.isDebugEnabled())
LOG.debug("fileRemoved {}: {}", filename, app);
if (app != null)
_deploymentManager.removeApp(app);
}

View File

@ -22,41 +22,18 @@ import java.util.Locale;
*/
public class FileID
{
/**
* Is the path a Web Archive?
*
* @param path the path to test.
* @return True if a .war or .jar or exploded web directory
* @see FileID#isWebArchiveFile(File)
*/
public static boolean isWebArchive(File path)
{
if (path.isFile())
{
String name = path.getName().toLowerCase(Locale.ENGLISH);
return (name.endsWith(".war") || name.endsWith(".jar"));
}
File webInf = new File(path, "WEB-INF");
File webXml = new File(webInf, "web.xml");
return webXml.exists() && webXml.isFile();
}
/**
* Is the path a Web Archive File (not directory)
*
* @param path the path to test.
* @param file the path to test.
* @return True if a .war or .jar file.
* @see FileID#isWebArchive(File)
*/
public static boolean isWebArchiveFile(File path)
public static boolean isWebArchiveFile(File file)
{
if (!path.isFile())
{
if (!file.isFile())
return false;
}
String name = path.getName().toLowerCase(Locale.ENGLISH);
String name = file.getName().toLowerCase(Locale.ENGLISH);
return (name.endsWith(".war") || name.endsWith(".jar"));
}
@ -70,4 +47,16 @@ public class FileID
String name = path.getName().toLowerCase(Locale.ENGLISH);
return name.endsWith(".xml");
}
/**
* Remove any 3 character suffix (e.g. ".war") from a path
* @param path The string path
* @return The path without the suffix or the original path
*/
public static String getDot3Basename(String path)
{
if (path == null || path.length() <= 4 || path.charAt(path.length() - 4) != '.')
return path;
return path.substring(0, path.length() - 4);
}
}

View File

@ -20,10 +20,14 @@ import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
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.hamcrest.Matchers;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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;
@ -77,6 +81,57 @@ public class DeploymentManagerTest
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("ee10");
depman.addAppProvider(new MockAppProvider()
{
@Override
public String getEnvironmentName()
{
return "ee12";
}
});
assertThat(depman.getDefaultEnvironmentName(), is("ee12"));
Environment.ensure("somethingElse");
depman.addAppProvider(new MockAppProvider()
{
@Override
public String getEnvironmentName()
{
return "ee12";
}
});
assertThat(depman.getDefaultEnvironmentName(), is("ee12"));
}
@Test
@Disabled // TODO
public void testXmlConfigured() throws Exception

View File

@ -20,12 +20,19 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.Environment;
public class MockAppProvider extends AbstractLifeCycle implements AppProvider
{
private DeploymentManager deployMan;
private File webappsDir;
@Override
public String getEnvironmentName()
{
return Environment.ensure("mock").getName();
}
@Override
public void setDeploymentManager(DeploymentManager deploymentManager)
{
@ -40,7 +47,7 @@ public class MockAppProvider extends AbstractLifeCycle implements AppProvider
public void findWebapp(String name)
{
App app = new App(deployMan, this, null, "mock-" + name);
App app = new App(deployMan, this, "mock-" + name);
this.deployMan.addApp(app);
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-core</artifactId>
<version>12.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-ee</artifactId>
<name>Jetty Core :: EE Utility Classes</name>
<properties>
<bundle-symbolic-name>${project.groupId}.ee</bundle-symbolic-name>
<spotbugs.onlyAnalyze>org.eclipse.jetty.ee.*</spotbugs.onlyAnalyze>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
@{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.io=org.eclipse.jetty.logging
</argLine>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jmx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -11,16 +11,14 @@
// ========================================================================
//
package org.eclipse.jetty.deploy;
import java.util.Map;
/**
* ConfigurationManager
*
* Type for allow injection of property values for replacement in jetty xml files during deployment.
*/
public interface ConfigurationManager
module org.eclipse.jetty.ee
{
public Map<String, String> getProperties();
requires org.slf4j;
requires transitive org.eclipse.jetty.io;
// Only required if using JMX.
requires static org.eclipse.jetty.jmx;
exports org.eclipse.jetty.ee;
}

View File

@ -0,0 +1,56 @@
//
// ========================================================================
// Copyright (c) 1995-2022 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.ee;
import java.util.Comparator;
import java.util.Map;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public interface Deployable
{
Pattern EE_ENVIRONMENT_NAME_PATTERN = Pattern.compile("ee(\\d*)");
Predicate<String> EE_ENVIRONMENT_NAME = s -> EE_ENVIRONMENT_NAME_PATTERN.matcher(s).matches();
Comparator<String> EE_ENVIRONMENT_COMPARATOR = (e1, e2) ->
{
Matcher m1 = EE_ENVIRONMENT_NAME_PATTERN.matcher(e1);
Matcher m2 = EE_ENVIRONMENT_NAME_PATTERN.matcher(e2);
if (m1.matches() && m2.matches())
{
int n1 = Integer.parseInt(m1.group(1));
int n2 = Integer.parseInt(m2.group(1));
return Integer.compare(n1, n2);
}
return 0;
};
String ENVIRONMENT = "environment";
String WAR = "jetty.deploy.war";
String BASE_TEMP_DIR = "jetty.deploy.tempDir";
String CONFIGURATION_CLASSES = "jetty.deploy.configurationClasses";
String CONTAINER_SCAN_JARS = "jetty.deploy.containerScanJarPattern";
String DEFAULTS_DESCRIPTOR = "jetty.deploy.defaultsDescriptor";
String EXTRACT_WARS = "jetty.deploy.extractWars";
String PARENT_LOADER_PRIORITY = "jetty.deploy.parentLoaderPriority";
String SCI_EXCLUSION_PATTERN = "jetty.deploy.servletContainerInitializerExclusionPattern";
String SCI_ORDER = "jetty.deploy.servletContainerInitializerOrder";
String WEBINF_SCAN_JARS = "jetty.deploy.webInfScanJarPattern";
void initializeDefaults(Map<String, String> properties);
}

View File

@ -19,6 +19,7 @@ lib/jetty-server-${jetty.version}.jar
lib/jetty-xml-${jetty.version}.jar
lib/jetty-util-${jetty.version}.jar
lib/jetty-io-${jetty.version}.jar
lib/jetty-ee-${jetty.version}.jar
[xml]
etc/jetty.xml

View File

@ -13,6 +13,8 @@
package org.eclipse.jetty.xml;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
@ -157,7 +159,7 @@ public class XmlConfiguration
* @param server The Server object to set
* @param webapp The webapps Resource
*/
public void setJettyStandardIdsAndProperties(Object server, Resource webapp)
public void setJettyStandardIdsAndProperties(Object server, File webapp)
{
try
{
@ -174,7 +176,7 @@ public class XmlConfiguration
if (webapp != null)
{
Path webappPath = webapp.getFile().toPath().toAbsolutePath();
Path webappPath = webapp.toPath().toAbsolutePath();
getProperties().put("jetty.webapp", webappPath.toString());
getProperties().put("jetty.webapps", webappPath.getParent().toString());
getProperties().put("jetty.webapps.uri", normalizeURI(webappPath.getParent().toUri().toString()));
@ -193,8 +195,8 @@ public class XmlConfiguration
return uri;
}
private final Map<String, Object> _idMap = new HashMap<>();
private final Map<String, String> _propertyMap = new HashMap<>();
private final Map<String, Object> _idMap;
private final Map<String, String> _propertyMap;
private final Resource _location;
private final String _dtd;
private ConfigurationProcessor _processor;
@ -216,11 +218,27 @@ public class XmlConfiguration
*/
public XmlConfiguration(Resource resource) throws SAXException, IOException
{
try (ConfigurationParser parser = getParser(); InputStream inputStream = resource.getInputStream())
this(resource.getFile(), null, null);
}
/**
* Reads and parses the XML configuration file.
*
* @param file the XML configuration
* @param idMap Map of objects with IDs
* @param properties Map of properties
* @throws IOException if the configuration could not be read
* @throws SAXException if the configuration could not be parsed
*/
public XmlConfiguration(File file, Map<String, Object> idMap, Map<String, String> properties) throws SAXException, IOException
{
try (ConfigurationParser parser = getParser(); InputStream inputStream = new FileInputStream(file))
{
_location = resource;
_location = Resource.newResource(file);
setConfig(parser.parse(inputStream));
_dtd = parser.getDTD();
_idMap = idMap == null ? new HashMap<>() : idMap;
_propertyMap = properties == null ? new HashMap<>() : properties;
}
}

View File

@ -1609,7 +1609,7 @@ public class XmlConfigurationTest
" </Set>" +
"</Configure>");
configuration.setJettyStandardIdsAndProperties(null, Resource.newResource(war));
configuration.setJettyStandardIdsAndProperties(null, war.toFile());
TestConfiguration tc = new TestConfiguration();
configuration.configure(tc);
@ -1724,7 +1724,7 @@ public class XmlConfigurationTest
try
{
configuration.setJettyStandardIdsAndProperties(null, Resource.newResource(war));
configuration.setJettyStandardIdsAndProperties(null, war.toFile());
configuration.getProperties().put("jetty.base", jettyBasePath);
if (configValue != null)
configuration.getProperties().put("jetty.sslContext.keyStorePath", configValue);

View File

@ -16,6 +16,7 @@
<module>jetty-bom</module>
<module>jetty-client</module>
<module>jetty-deploy</module>
<module>jetty-ee</module>
<module>jetty-fcgi</module>
<module>jetty-http</module>
<module>jetty-http2</module>

View File

@ -14,5 +14,5 @@ webapp
ee10-deploy
[files]
maven://org.eclipse.jetty.ee10.demos/ee10-demo-async-rest-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-async-rest.war
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-async-rest-webapp/${jetty.version}/war|webapps/ee10-demo-async-rest.war

View File

@ -19,10 +19,10 @@ ee10-annotations
ext
[files]
basehome:modules/demo.d/ee10-demo-jaas.xml|webapps-ee10/ee10-demo-jaas.xml
basehome:modules/demo.d/ee10-demo-jaas.xml|webapps/ee10-demo-jaas.xml
basehome:modules/demo.d/ee10-demo-login.conf|etc/ee10-demo-login.conf
basehome:modules/demo.d/ee10-demo-login.properties|etc/ee10-demo-login.properties
maven://org.eclipse.jetty.ee10.demos/ee10-demo-jaas-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-jaas.war
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-jaas-webapp/${jetty.version}/war|webapps/ee10-demo-jaas.war
[ini]
# Enable security via jaas, and configure it

View File

@ -24,6 +24,6 @@ ee10-demo-realm
[files]
webapps-ee10/ee10-demo-jetty.d/
basehome:modules/demo.d/ee10-demo-jetty.xml|webapps-ee10/ee10-demo-jetty.xml
basehome:modules/demo.d/ee10-demo-jetty-override-web.xml|webapps-ee10/ee10-demo-jetty.d/ee10-demo-jetty-override-web.xml
maven://org.eclipse.jetty.ee10.demos/ee10-demo-jetty-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-jetty.war
basehome:modules/demo.d/ee10-demo-jetty.xml|webapps/ee10-demo-jetty.xml
basehome:modules/demo.d/ee10-demo-jetty-override-web.xml|webapps/ee10-demo-jetty.d/ee10-demo-jetty-override-web.xml
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-jetty-webapp/${jetty.version}/war|webapps/ee10-demo-jetty.war

View File

@ -10,5 +10,5 @@ demo
ee10-deploy
[files]
basehome:modules/demo.d/ee10-demo-moved-context.xml|webapps-ee10/ee10-demo-moved-context.xml
basehome:modules/demo.d/ee10-demo-moved-context.xml|webapps/ee10-demo-moved-context.xml

View File

@ -18,6 +18,6 @@ ee10-plus
ee10-demo-mock-resources
[files]
basehome:modules/demo.d/ee10-demo-jndi.xml|webapps-ee10/ee10-demo-jndi.xml
maven://org.eclipse.jetty.ee10.demos/ee10-demo-jndi-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-jndi.war
basehome:modules/demo.d/ee10-demo-jndi.xml|webapps/ee10-demo-jndi.xml
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-jndi-webapp/${jetty.version}/war|webapps/ee10-demo-jndi.war
maven://jakarta.mail/jakarta.mail-api/2.0.0/jar|lib/ext/jakarta.mail-api-2.0.0.jar

View File

@ -14,4 +14,4 @@ ee10-jstl
ee10-deploy
[files]
maven://org.eclipse.jetty.ee10.demos/ee10-demo-jsp-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-jsp.war
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-jsp-webapp/${jetty.version}/war|webapps/ee10-demo-jsp.war

View File

@ -18,7 +18,7 @@
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.demos.demo-mock-resources</Bundle-SymbolicName>
<Bundle-SymbolicName>org.eclipse.jetty.ee10.demos.demo-mock-resources</Bundle-SymbolicName>
<Bundle-Description>Mock resources used for testing</Bundle-Description>
<Export-Package>
org.example;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"

View File

@ -17,4 +17,4 @@ ee10-annotations
lib/ee10-demo-mock-resources-${jetty.version}.jar
[files]
maven://org.eclipse.jetty.ee10.demos/ee10-demo-mock-resources/${jetty.version}/jar|lib/ee10-demo-mock-resources-${jetty.version}.jar
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-mock-resources/${jetty.version}/jar|lib/ee10-demo-mock-resources-${jetty.version}.jar

View File

@ -14,4 +14,4 @@ webapp
ee10-deploy
[files]
maven://org.eclipse.jetty.ee10.demos/ee10-demo-proxy-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-proxy.war
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-proxy-webapp/${jetty.version}/war|webapps/ee10-demo-proxy.war

View File

@ -12,4 +12,4 @@ webapp
ee10-deploy
[files]
maven://org.eclipse.jetty.ee10.demos/ee10-demo-simple-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-simple.war
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-simple-webapp/${jetty.version}/war|webapps/ee10-demo-simple.war

View File

@ -20,7 +20,7 @@
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.demos.demo-servlet-container-initializer;singleton:=true</Bundle-SymbolicName>
<Bundle-SymbolicName>org.eclipse.jetty.ee10.demos.demo-servlet-container-initializer;singleton:=true</Bundle-SymbolicName>
<Bundle-Description>A bundle containing a ServletContainerInitializer for testing</Bundle-Description>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=jakarta.servlet.ServletContainerInitializer</Provide-Capability>

View File

@ -20,5 +20,5 @@ ee10-demo-realm
ee10-demo-mock-resources
[files]
basehome:modules/demo.d/ee10-demo-spec.xml|webapps-ee10/ee10-demo-spec.xml
maven://org.eclipse.jetty.ee10.demos/ee10-demo-spec-webapp/${jetty.version}/war|webapps-ee10/ee10-demo-spec.war
basehome:modules/demo.d/ee10-demo-spec.xml|webapps/ee10-demo-spec.xml
maven://org.eclipse.jetty.ee10.demos/jetty-ee10-demo-spec-webapp/${jetty.version}/war|webapps/ee10-demo-spec.war

View File

@ -350,7 +350,7 @@ public class JettyHomeForker extends AbstractForker
modulesPath = Files.createDirectories(targetBasePath.resolve("modules"));
etcPath = Files.createDirectories(targetBasePath.resolve("etc"));
libPath = Files.createDirectories(targetBasePath.resolve("lib"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps-ee10"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps"));
mavenLibPath = Files.createDirectories(libPath.resolve("maven-ee10"));
//copy in the jetty-maven-plugin jar

View File

@ -48,7 +48,7 @@
<Bundle-SymbolicName>${bundle-symbolic-name}</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test WebApp Fragment</Bundle-Name>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<Fragment-Host>org.eclipse.jetty.demos.spec.webapp</Fragment-Host>
<Fragment-Host>org.eclipse.jetty.ee10.demos.spec.webapp</Fragment-Host>
<Jetty-WarFragmentResourcePath>/</Jetty-WarFragmentResourcePath>
</instructions>
</configuration>

View File

@ -198,7 +198,6 @@ public class ServletContextHandler extends ContextHandler implements Graceful
private final ServletContextApi _servletContext;
protected ContextStatus _contextStatus = ContextStatus.NOTSET;
private final Map<String, String> _initParams = new HashMap<>();
private boolean _contextPathDefault = true;
private String _defaultRequestCharacterEncoding;
private String _defaultResponseCharacterEncoding;
private String _contextPathEncoded = "/";
@ -655,19 +654,6 @@ public class ServletContextHandler extends ContextHandler implements Graceful
.toArray(String[]::new);
}
/**
* Set the default context path.
* A default context path may be overriden by a default-context-path element
* in a web.xml
*
* @param contextPath The _contextPath to set.
*/
public void setDefaultContextPath(String contextPath)
{
setContextPath(contextPath);
_contextPathDefault = true;
}
public void setDefaultRequestCharacterEncoding(String encoding)
{
_defaultRequestCharacterEncoding = encoding;
@ -688,14 +674,6 @@ public class ServletContextHandler extends ContextHandler implements Graceful
return _defaultResponseCharacterEncoding;
}
/**
* @return True if the current contextPath is from default settings
*/
public boolean isContextPathDefault()
{
return _contextPathDefault;
}
/**
* @param contextPath The _contextPath to set.
*/
@ -724,7 +702,6 @@ public class ServletContextHandler extends ContextHandler implements Graceful
super.setContextPath(contextPath);
_contextPathEncoded = URIUtil.encodePath(contextPath);
_contextPathDefault = false;
if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
{

View File

@ -102,5 +102,9 @@
<artifactId>jetty-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -21,14 +21,14 @@
<Call name="addAppProvider">
<Arg>
<New id="WebAppProvider" class="org.eclipse.jetty.deploy.providers.ContextProvider">
<Set name="defaultEnvironment">ee10</Set>
<Set name="environmentName">ee10</Set>
<Set name="monitoredDirName">
<Call name="resolvePath" class="org.eclipse.jetty.xml.XmlConfiguration">
<Arg>
<Property name="jetty.base" />
</Arg>
<Arg>
<Property name="jetty.deploy.monitoredDir" default="webapps-ee10" />
<Property name="jetty.deploy.monitoredDir" default="webapps" />
</Arg>
</Call>
</Set>
@ -40,22 +40,28 @@
</Default>
</Property>
</Set>
<Set name="scanInterval">
<Property name="jetty.deploy.scanInterval" default="1" />
</Set>
<Set name="extractWars">
<Property name="jetty.deploy.extractWars" default="true" />
</Set>
<Set name="configurationManager">
<New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
<!-- file of context configuration properties
<Set name="file"><SystemProperty name="jetty.base"/>/etc/some.properties</Set>
-->
<!-- set a context configuration property
<Call name="put"><Arg>name</Arg><Arg>value</Arg></Call>
-->
</New>
</Set>
<Set name="scanInterval" property="jetty.deploy.scanInterval"/>
<Set name="defaultsDescriptor" property="jetty.deploy.defaultsDescriptor"/>
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">
<Default>.*jakarta.servlet.jsp.jstl-.*\.jar$</Default>
</Property>
</Put>
<Put name="jetty.deploy.webInfScanJarPattern">
<Property name="jetty.deploy.webInfScanJarPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerExclusionPattern">
<Property name="jetty.deploy.servletContainerInitializerExclusionPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerOrder">
<Property name="jetty.deploy.servletContainerInitializerOrder"/>
</Put>
</Get>
</New>
</Arg>
</Call>

View File

@ -11,23 +11,44 @@ ee10-webapp
[lib]
[files]
webapps-ee10/
webapps/
[xml]
etc/jetty-ee10-deploy.xml
[ini-template]
# Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps-ee10
# - OR -
# Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps-ee10
## Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps
## - OR -
## Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps
# Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee10.xml
## Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptor=${jetty.base}/etc/webdefault-ee10.xml
# Monitored directory scan period (seconds)
## Monitored directory scan period (seconds)
# jetty.deploy.scanInterval=1
# Whether to extract *.war files
## Whether to extract *.war files
# jetty.deploy.extractWars=true
## Whether to give the parent classloader priority
# jetty.deploy.parentLoaderPriority=true
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*jakarta.servlet.jsp.jstl-.*\.jar$
## Pattern to select jars from the container classloader to be scanned (or null to scan all jars).
# jetty.deploy.webInfScanJarPattern=
## Pattern to exclude discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerExclusionPattern=
## Order of discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerOrder=

View File

@ -30,6 +30,7 @@ module org.eclipse.jetty.ee10.webapp
requires org.slf4j;
requires transitive java.instrument;
requires transitive org.eclipse.jetty.ee;
requires transitive org.eclipse.jetty.session;
requires transitive org.eclipse.jetty.ee10.servlet;
requires transitive org.eclipse.jetty.xml;

View File

@ -36,7 +36,9 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.PatternMatcher;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.EmptyResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
@ -186,13 +188,19 @@ public class MetaInfConfiguration extends AbstractConfiguration
*/
public void findAndFilterContainerPaths(final WebAppContext context) throws Exception
{
String pattern = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
if (LOG.isDebugEnabled())
LOG.debug("{}={}", CONTAINER_JAR_PATTERN, pattern);
if (StringUtil.isBlank(pattern))
return; // TODO review if this short cut will allow later code simplifications
// Apply an initial name filter to the jars to select which will be eventually
// scanned for META-INF info and annotations. The filter is based on inclusion patterns.
ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, (String)context.getAttribute(CONTAINER_JAR_PATTERN));
ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, pattern);
List<URI> containerUris = getAllContainerJars(context);
if (LOG.isDebugEnabled())
LOG.debug("Matching container urls {}", containerUris);
LOG.debug("All container urls {}", containerUris);
containerPathNameMatcher.match(containerUris);
// When running on jvm 9 or above, we we won't be able to look at the application
@ -264,9 +272,12 @@ public class MetaInfConfiguration extends AbstractConfiguration
throws Exception
{
//Apply filter to WEB-INF/lib jars
WebAppPathNameMatcher matcher = new WebAppPathNameMatcher(context, (String)context.getAttribute(WEBINF_JAR_PATTERN));
String pattern = (String)context.getAttribute(WEBINF_JAR_PATTERN);
WebAppPathNameMatcher matcher = new WebAppPathNameMatcher(context, pattern);
List<Resource> jars = findJars(context);
if (LOG.isDebugEnabled())
LOG.debug("webapp {}={} jars {}", WEBINF_JAR_PATTERN, pattern, jars);
//Convert to uris for matching
if (jars != null)
@ -284,23 +295,21 @@ public class MetaInfConfiguration extends AbstractConfiguration
protected List<URI> getAllContainerJars(final WebAppContext context) throws URISyntaxException
{
List<URI> uris = new ArrayList<>();
if (context.getClassLoader() != null)
ClassLoader loader = MetaInfConfiguration.class.getClassLoader();
while (loader != null)
{
ClassLoader loader = context.getClassLoader().getParent();
while (loader != null)
if (loader instanceof URLClassLoader)
{
if (loader instanceof URLClassLoader)
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
for (URL url : urls)
{
uris.add(new URI(url.toString().replaceAll(" ", "%20")));
}
for (URL url : urls)
uris.add(new URI(url.toString().replaceAll(" ", "%20")));
}
loader = loader.getParent();
}
loader = loader.getParent();
}
return uris;
}

View File

@ -38,10 +38,10 @@ import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.ee10.servlet.ErrorHandler;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler.ServletContextApi;
import org.eclipse.jetty.ee10.servlet.ServletHandler;
import org.eclipse.jetty.ee10.servlet.SessionHandler;
import org.eclipse.jetty.ee10.servlet.security.ConstraintAware;
@ -50,8 +50,8 @@ import org.eclipse.jetty.ee10.servlet.security.ConstraintSecurityHandler;
import org.eclipse.jetty.ee10.servlet.security.SecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.MultiException;
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;
@ -75,7 +75,7 @@ import org.slf4j.LoggerFactory;
*
*/
@ManagedObject("Web Application ContextHandler")
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context, Deployable
{
static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
@ -121,6 +121,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private boolean _logUrlOnStart = false;
private boolean _parentLoaderPriority = Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
private PermissionCollection _permissions;
private boolean _defaultContextPath = true;
private String[] _contextWhiteList = null;
@ -228,6 +229,55 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
setParent(parent);
}
@Override
public void initializeDefaults(Map<String, String> 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.BASE_TEMP_DIR -> setAttribute(BASETEMPDIR, 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;
}
public boolean isContextPathDefault()
{
return _defaultContextPath;
}
@Override
public void setContextPath(String contextPath)
{
super.setContextPath(contextPath);
_defaultContextPath = false;
}
public void setDefaultContextPath(String contextPath)
{
super.setContextPath(contextPath);
_defaultContextPath = true;
}
/**
* @param servletContextName The servletContextName to set.
*/

View File

@ -134,16 +134,17 @@ public class MetaInfConfigurationTest
{
MetaInfConfiguration config = new MetaInfConfiguration();
WebAppContext context = new WebAppContext();
context.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, ".*/jetty-util-[^/]*\\.jar$|.*/jetty-util/target/classes/$|.*/foo-bar-janb.jar");
context.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, ".*servlet-api-[^/]*\\.jar$|.*/foo-bar-janb.jar");
WebAppClassLoader loader = new WebAppClassLoader(context);
context.setClassLoader(loader);
config.findAndFilterContainerPaths(context);
List<Resource> containerResources = context.getMetaData().getContainerResources();
assertEquals(2, containerResources.size());
for (Resource r : containerResources)
{
String s = r.toString();
assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("jetty-util"));
assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("servlet-api"));
}
}
}

View File

@ -41,7 +41,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -21,41 +21,39 @@
<Call name="addAppProvider">
<Arg>
<New id="WebAppProvider" class="org.eclipse.jetty.deploy.providers.ContextProvider">
<Set name="defaultEnvironment">ee8</Set>
<Set name="EnvironmentName">ee8</Set>
<Set name="monitoredDirName">
<Call name="resolvePath" class="org.eclipse.jetty.xml.XmlConfiguration">
<Arg>
<Property name="jetty.base" />
</Arg>
<Arg>
<Property name="jetty.deploy.monitoredDir" default="webapps-ee8" />
<Property name="jetty.deploy.monitoredDir" default="webapps" />
</Arg>
</Call>
</Set>
<Set name="defaultsDescriptor">
<Property>
<Name>jetty.deploy.defaultsDescriptorPath</Name>
<Default>
<Property name="jetty.home" default="." />/etc/webdefault-ee8.xml
</Default>
</Property>
</Set>
<Set name="scanInterval">
<Property name="jetty.deploy.scanInterval" default="1" />
</Set>
<Set name="extractWars">
<Property name="jetty.deploy.extractWars" default="true" />
</Set>
<Set name="configurationManager">
<New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
<!-- file of context configuration properties
<Set name="file"><SystemProperty name="jetty.base"/>/etc/some.properties</Set>
-->
<!-- set a context configuration property
<Call name="put"><Arg>name</Arg><Arg>value</Arg></Call>
-->
</New>
</Set>
<Set name="scanInterval" property="jetty.deploy.scanInterval"/>
<Set name="defaultsDescriptor" property="jetty.deploy.defaultsDescriptor"/>
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">
<Default>.*jakarta.servlet.jsp.jstl-.*\.jar$</Default>
</Property>
</Put>
<Put name="jetty.deploy.webInfScanJarPattern">
<Property name="jetty.deploy.webInfScanJarPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerExclusionPattern">
<Property name="jetty.deploy.servletContainerInitializerExclusionPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerOrder">
<Property name="jetty.deploy.servletContainerInitializerOrder"/>
</Put>
</Get>
</New>
</Arg>
</Call>

View File

@ -11,23 +11,44 @@ ee8-webapp
[lib]
[files]
webapps-ee8/
webapps/
[xml]
etc/jetty-ee8-deploy.xml
[ini-template]
# Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps-ee8
# - OR -
# Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps-ee8
## Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps
## - OR -
## Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps
# Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee8.xml
## Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptor=${jetty.base}/etc/webdefault-ee8.xml
# Monitored directory scan period (seconds)
## Monitored directory scan period (seconds)
# jetty.deploy.scanInterval=1
# Whether to extract *.war files
## Whether to extract *.war files
# jetty.deploy.extractWars=true
## Whether to give the parent classloader priority
# jetty.deploy.parentLoaderPriority=true
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*jakarta.servlet.jsp.jstl-.*\.jar$
## Pattern to select jars from the container classloader to be scanned (or null to scan all jars).
# jetty.deploy.webInfScanJarPattern=
## Pattern to exclude discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerExclusionPattern=
## Order of discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerOrder=

View File

@ -8,7 +8,7 @@
<servlet>
<display-name>SerialRestServlet</display-name>
<servlet-name>SerialRestServlet</servlet-name>
<servlet-class>org.eclipse.jetty.demos.SerialRestServlet</servlet-class>
<servlet-class>org.eclipse.jetty.ee9.demosSerialRestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SerialRestServlet</servlet-name>
@ -18,7 +18,7 @@
<servlet>
<display-name>AsyncRestServlet</display-name>
<servlet-name>AsyncRestServlet</servlet-name>
<servlet-class>org.eclipse.jetty.demos.AsyncRestServlet</servlet-class>
<servlet-class>org.eclipse.jetty.ee9.demosAsyncRestServlet</servlet-class>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>

View File

@ -14,5 +14,5 @@ webapp
ee9-deploy
[files]
maven://org.eclipse.jetty.ee9.demos/ee9-demo-async-rest-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-async-rest.war
basehome:modules/demo.d/ee9-demo-async-rest.properties|webapps/ee9-demo-async-rest.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-async-rest-webapp/${jetty.version}/war|webapps/ee9-demo-async-rest.war

View File

@ -19,7 +19,7 @@
<New id="context" class="org.eclipse.jetty.ee9.servlet.ServletContextHandler">
<Set name="contextPath">/hello</Set>
<Call name="addServlet">
<Arg>org.eclipse.jetty.demos.HelloServlet</Arg>
<Arg>org.eclipse.jetty.ee9.demos.HelloServlet</Arg>
<Arg>/</Arg>
</Call>
</New>

View File

@ -19,10 +19,11 @@ ee9-annotations
ext
[files]
basehome:modules/demo.d/ee9-demo-jaas.xml|webapps-ee9/ee9-demo-jaas.xml
basehome:modules/demo.d/ee9-demo-jaas.xml|webapps/ee9-demo-jaas.xml
basehome:modules/demo.d/ee9-demo-jaas.properties|webapps/ee9-demo-jaas.properties
basehome:modules/demo.d/ee9-demo-login.conf|etc/ee9-demo-login.conf
basehome:modules/demo.d/ee9-demo-login.properties|etc/ee9-demo-login.properties
maven://org.eclipse.jetty.ee9.demos/ee9-demo-jaas-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-jaas.war
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-jaas-webapp/${jetty.version}/war|webapps/ee9-demo-jaas.war
[ini]
# Enable security via jaas, and configure it

View File

@ -24,6 +24,7 @@ ee9-demo-realm
[files]
webapps-ee9/demo-jetty.d/
basehome:modules/demo.d/ee9-demo-jetty.xml|webapps-ee9/ee9-demo-jetty.xml
basehome:modules/demo.d/ee9-demo-jetty-override-web.xml|webapps-ee9/ee9-demo-jetty.d/ee9-demo-jetty-override-web.xml
maven://org.eclipse.jetty.ee9.demos/ee9-demo-jetty-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-jetty.war
basehome:modules/demo.d/ee9-demo-jetty.xml|webapps/ee9-demo-jetty.xml
basehome:modules/demo.d/ee9-demo-jetty-override-web.xml|webapps/ee9-demo-jetty.d/ee9-demo-jetty-override-web.xml
basehome:modules/demo.d/ee9-demo-jetty.properties|webapps/ee9-demo-jetty.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-jetty-webapp/${jetty.version}/war|webapps/ee9-demo-jetty.war

View File

@ -13,5 +13,5 @@ demo
ee9-deploy
[files]
basehome:modules/demo.d/ee9-demo-moved-context.xml|webapps-ee9/ee9-demo-moved-context.xml
basehome:modules/demo.d/ee9-demo-moved-context.xml|webapps/ee9-demo-moved-context.xml

View File

@ -18,6 +18,7 @@ ee9-plus
ee9-demo-mock-resources
[files]
basehome:modules/demo.d/ee9-demo-jndi.xml|webapps-ee9/ee9-demo-jndi.xml
maven://org.eclipse.jetty.ee9.demos/ee9-demo-jndi-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-jndi.war
basehome:modules/demo.d/ee9-demo-jndi.xml|webapps/ee9-demo-jndi.xml
basehome:modules/demo.d/ee9-demo-jndi.properties|webapps/ee9-demo-jndi.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-jndi-webapp/${jetty.version}/war|webapps/ee9-demo-jndi.war
maven://jakarta.mail/jakarta.mail-api/2.0.0/jar|lib/ext/jakarta.mail-api-2.0.0.jar

View File

@ -14,4 +14,5 @@ jstl
ee9-deploy
[files]
maven://org.eclipse.jetty.ee9.demos/ee9-demo-jsp-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-jsp.war
basehome:modules/demo.d/ee9-demo-jsp.properties|webapps/ee9-demo-jsp.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-jsp-webapp/${jetty.version}/war|webapps/ee9-demo-jsp.war

View File

@ -24,7 +24,7 @@
</goals>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.demos.ee9-demo-mock-resources</Bundle-SymbolicName>
<Bundle-SymbolicName>org.eclipse.jetty.ee9.demos.ee9-demo-mock-resources</Bundle-SymbolicName>
<Bundle-Description>Mock resources used for testing</Bundle-Description>
<Export-Package>
org.example;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"

View File

@ -14,4 +14,4 @@ jdbc
ee9-annotations
[files]
maven://org.eclipse.jetty.ee9.demos/ee9-demo-mock-resources/${jetty.version}/jar|lib/ext/ee9-demo-mock-resources-${jetty.version}.jar
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-mock-resources/${jetty.version}/jar|lib/ext/ee9-demo-mock-resources-${jetty.version}.jar

View File

@ -14,4 +14,5 @@ webapp
ee9-deploy
[files]
maven://org.eclipse.jetty.ee9.demos/ee9-demo-proxy-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-proxy.war
basehome:modules/demo.d/ee9-demo-proxy.properties|webapps/ee9-demo-proxy.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-proxy-webapp/${jetty.version}/war|webapps/ee9-demo-proxy.war

View File

@ -12,4 +12,5 @@ webapp
ee9-deploy
[files]
maven://org.eclipse.jetty.ee9.demos/ee9-demo-simple-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-simple.war
basehome:modules/demo.d/ee9-demo-simple.properties|webapps/ee9-demo-simple.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-simple-webapp/${jetty.version}/war|webapps/ee9-demo-simple.war

View File

@ -20,7 +20,7 @@
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.demos.ee9-demo-servlet-container-initializer;singleton:=true</Bundle-SymbolicName>
<Bundle-SymbolicName>org.eclipse.jetty.ee9.demos.ee9-demo-servlet-container-initializer;singleton:=true</Bundle-SymbolicName>
<Bundle-Description>A bundle containing a ServletContainerInitializer for testing</Bundle-Description>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=jakarta.servlet.ServletContainerInitializer</Provide-Capability>

View File

@ -20,5 +20,6 @@ ee9-demo-realm
ee9-demo-mock-resources
[files]
basehome:modules/demo.d/ee9-demo-spec.xml|webapps-ee9/ee9-demo-spec.xml
maven://org.eclipse.jetty.ee9.demos/ee9-demo-spec-webapp/${jetty.version}/war|webapps-ee9/ee9-demo-spec.war
basehome:modules/demo.d/ee9-demo-spec.xml|webapps/ee9-demo-spec.xml
basehome:modules/demo.d/ee9-demo-spec.properties|webapps/ee9-demo-spec.properties
maven://org.eclipse.jetty.ee9.demos/jetty-ee9-demo-spec-webapp/${jetty.version}/war|webapps/ee9-demo-spec.war

View File

@ -350,7 +350,7 @@ public class JettyHomeForker extends AbstractForker
modulesPath = Files.createDirectories(targetBasePath.resolve("modules"));
etcPath = Files.createDirectories(targetBasePath.resolve("etc"));
libPath = Files.createDirectories(targetBasePath.resolve("lib"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps-ee9"));
webappPath = Files.createDirectories(targetBasePath.resolve("webapps"));
mavenLibPath = Files.createDirectories(libPath.resolve("maven-ee9"));
//copy in the jetty-maven-plugin jar

View File

@ -39,7 +39,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>

View File

@ -14,6 +14,7 @@
module org.eclipse.jetty.ee9.nested
{
requires transitive jetty.servlet.api;
requires transitive org.eclipse.jetty.ee;
requires transitive org.eclipse.jetty.http;
requires transitive org.eclipse.jetty.server;
requires transitive org.eclipse.jetty.session;

View File

@ -198,7 +198,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
protected ContextStatus _contextStatus = ContextStatus.NOTSET;
protected APIContext _apiContext;
private final Map<String, String> _initParams;
private boolean _contextPathDefault = true;
private String _defaultRequestCharacterEncoding;
private String _defaultResponseCharacterEncoding;
private String _contextPathEncoded = "/";
@ -1065,19 +1064,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
_coreContextHandler.setClassLoader(classLoader);
}
/**
* Set the default context path.
* A default context path may be overriden by a default-context-path element
* in a web.xml
*
* @param contextPath The _contextPath to set.
*/
public void setDefaultContextPath(String contextPath)
{
setContextPath(contextPath);
_contextPathDefault = true;
}
public void setDefaultRequestCharacterEncoding(String encoding)
{
_defaultRequestCharacterEncoding = encoding;
@ -1098,14 +1084,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
return _defaultResponseCharacterEncoding;
}
/**
* @return True if the current contextPath is from default settings
*/
public boolean isContextPathDefault()
{
return _contextPathDefault;
}
/**
* @param contextPath The _contextPath to set.
*/
@ -1133,7 +1111,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
_coreContextHandler.setContextPath(contextPath);
_contextPathEncoded = URIUtil.encodePath(contextPath);
_contextPathDefault = false;
// update context mappings
if (getServer() != null && getServer().isRunning())

View File

@ -48,7 +48,7 @@
<Bundle-SymbolicName>${bundle-symbolic-name}</Bundle-SymbolicName>
<Bundle-Name>Jetty OSGi Test WebApp Fragment</Bundle-Name>
<Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
<Fragment-Host>org.eclipse.jetty.demos.spec.webapp</Fragment-Host>
<Fragment-Host>org.eclipse.jetty.ee9.demos.spec.webapp</Fragment-Host>
<Jetty-WarFragmentResourcePath>/</Jetty-WarFragmentResourcePath>
</instructions>
</configuration>

View File

@ -21,41 +21,39 @@
<Call name="addAppProvider">
<Arg>
<New id="WebAppProvider" class="org.eclipse.jetty.deploy.providers.ContextProvider">
<Set name="defaultEnvironment">ee9</Set>
<Set name="EnvironmentName">ee9</Set>
<Set name="monitoredDirName">
<Call name="resolvePath" class="org.eclipse.jetty.xml.XmlConfiguration">
<Arg>
<Property name="jetty.base" />
</Arg>
<Arg>
<Property name="jetty.deploy.monitoredDir" default="webapps-ee9" />
<Property name="jetty.deploy.monitoredDir" default="webapps" />
</Arg>
</Call>
</Set>
<Set name="defaultsDescriptor">
<Property>
<Name>jetty.deploy.defaultsDescriptorPath</Name>
<Default>
<Property name="jetty.home" default="." />/etc/webdefault-ee9.xml
</Default>
</Property>
</Set>
<Set name="scanInterval">
<Property name="jetty.deploy.scanInterval" default="1" />
</Set>
<Set name="extractWars">
<Property name="jetty.deploy.extractWars" default="true" />
</Set>
<Set name="configurationManager">
<New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
<!-- file of context configuration properties
<Set name="file"><SystemProperty name="jetty.base"/>/etc/some.properties</Set>
-->
<!-- set a context configuration property
<Call name="put"><Arg>name</Arg><Arg>value</Arg></Call>
-->
</New>
</Set>
<Set name="scanInterval" property="jetty.deploy.scanInterval"/>
<Set name="defaultsDescriptor" property="jetty.deploy.defaultsDescriptor"/>
<Set name="extractWars" property="jetty.deploy.extractWars" />
<Set name="parentLoaderPriority" property="jetty.deploy.parentLoaderPriority" />
<Set name="configurationClasses" property="jetty.deploy.configurationClasses" />
<Set name="tempDir" property="jetty.deploy.tempDir" />
<Get name="properties">
<Put name="jetty.deploy.containerScanJarPattern">
<Property name="jetty.deploy.containerScanJarPattern">
<Default>.*jakarta.servlet.jsp.jstl-.*\.jar$</Default>
</Property>
</Put>
<Put name="jetty.deploy.webInfScanJarPattern">
<Property name="jetty.deploy.webInfScanJarPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerExclusionPattern">
<Property name="jetty.deploy.servletContainerInitializerExclusionPattern"/>
</Put>
<Put name="jetty.deploy.servletContainerInitializerOrder">
<Property name="jetty.deploy.servletContainerInitializerOrder"/>
</Put>
</Get>
</New>
</Arg>
</Call>

View File

@ -11,23 +11,44 @@ ee9-webapp
[lib]
[files]
webapps-ee9/
webapps/
[xml]
etc/jetty-ee9-deploy.xml
[ini-template]
# Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps-ee9
# - OR -
# Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps-ee9
## Monitored directory name (relative to $jetty.base)
# jetty.deploy.monitoredDir=webapps
## - OR -
## Monitored directory path (fully qualified)
# jetty.deploy.monitoredPath=/var/www/webapps
# Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptorPath=${jetty.base}/etc/webdefault-ee9.xml
## Defaults Descriptor for all deployed webapps
# jetty.deploy.defaultsDescriptor=${jetty.base}/etc/webdefault-ee9.xml
# Monitored directory scan period (seconds)
## Monitored directory scan period (seconds)
# jetty.deploy.scanInterval=1
# Whether to extract *.war files
## Whether to extract *.war files
# jetty.deploy.extractWars=true
## Whether to give the parent classloader priority
# jetty.deploy.parentLoaderPriority=true
## Comma separated list of configuration classes to set.
# jetty.deploy.configurationClasses=
## Base temporary directory for deployed web applications.
# jetty.deploy.tempDir=
## Pattern to select jars from the container classloader to be scanned (or null to scan no jars)
# jetty.deploy.containerScanJarPattern=.*jakarta.servlet.jsp.jstl-.*\.jar$
## Pattern to select jars from the container classloader to be scanned (or null to scan all jars).
# jetty.deploy.webInfScanJarPattern=
## Pattern to exclude discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerExclusionPattern=
## Order of discovered ServletContainerInitializers
# jetty.deploy.servletContainerInitializerOrder=

View File

@ -36,7 +36,9 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.PatternMatcher;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.resource.EmptyResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
@ -186,9 +188,13 @@ public class MetaInfConfiguration extends AbstractConfiguration
*/
public void findAndFilterContainerPaths(final WebAppContext context) throws Exception
{
String pattern = (String)context.getAttribute(CONTAINER_JAR_PATTERN);
if (StringUtil.isBlank(pattern))
return; // TODO review if this short cut will allow later code simplifications
// Apply an initial name filter to the jars to select which will be eventually
// scanned for META-INF info and annotations. The filter is based on inclusion patterns.
ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, (String)context.getAttribute(CONTAINER_JAR_PATTERN));
ContainerPathNameMatcher containerPathNameMatcher = new ContainerPathNameMatcher(context, pattern);
List<URI> containerUris = getAllContainerJars(context);
if (LOG.isDebugEnabled())
@ -284,22 +290,19 @@ public class MetaInfConfiguration extends AbstractConfiguration
protected List<URI> getAllContainerJars(final WebAppContext context) throws URISyntaxException
{
List<URI> uris = new ArrayList<>();
if (context.getClassLoader() != null)
ClassLoader loader = MetaInfConfiguration.class.getClassLoader();
while (loader != null)
{
ClassLoader loader = context.getClassLoader().getParent();
while (loader != null)
if (loader instanceof URLClassLoader)
{
if (loader instanceof URLClassLoader)
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
{
URL[] urls = ((URLClassLoader)loader).getURLs();
if (urls != null)
for (URL url : urls)
{
uris.add(new URI(url.toString().replaceAll(" ", "%20")));
}
for (URL url : urls)
uris.add(new URI(url.toString().replaceAll(" ", "%20")));
}
loader = loader.getParent();
}
loader = loader.getParent();
}
return uris;
}

View File

@ -38,10 +38,9 @@ import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee9.nested.AbstractHandler;
import org.eclipse.jetty.ee.Deployable;
import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.ErrorHandler;
import org.eclipse.jetty.ee9.nested.ManagedAttributeListener;
import org.eclipse.jetty.ee9.nested.SessionHandler;
import org.eclipse.jetty.ee9.security.ConstraintAware;
import org.eclipse.jetty.ee9.security.ConstraintMapping;
@ -50,13 +49,12 @@ import org.eclipse.jetty.ee9.security.SecurityHandler;
import org.eclipse.jetty.ee9.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
import org.eclipse.jetty.ee9.servlet.ServletHandler;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.TopologicalSort;
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;
@ -79,76 +77,9 @@ import org.slf4j.LoggerFactory;
* the default being {@link WebXmlConfiguration} and
* {@link JettyWebXmlConfiguration}.
*
*
* <p>
* The Start/Configuration of a WebAppContext is rather complex so as to allow
* pluggable behaviour to be added in almost arbitrary ordering. The
* sequence of a WebappContext start is as follows:
* <blockquote>
* {@link #doStart()}:
* <ul>
* <li>{@link #preConfigure()}
* <ul>
* <li>Add all Server class inclusions from all known configurations {@link Configurations#getKnown()}</li>
* <li>{@link #loadConfigurations()}, which uses either explicitly set Configurations or takes the server
* default (which is all known {@link Configuration#isEnabledByDefault()} Configurations.</li>
* <li>Sort the configurations using {@link TopologicalSort} in {@link Configurations#sort()}.</li>
* <li>Add all Server class exclusions from this webapps {@link Configurations}</li>
* <li>Add all System classes inclusions and exclusions for this webapps {@link Configurations}</li>
* <li>Instantiate the WebAppClassLoader (if one not already explicitly set)</li>
* <li>{@link Configuration#preConfigure(WebAppContext)} which calls
* {@link Configuration#preConfigure(WebAppContext)} for this webapps {@link Configurations}</li>
* </ul>
* </li>
* <li>{@link ServletContextHandler#doStart()}
* <ul>
* <li>{@link ContextHandler#doStart()}
* <ul>
* <li>Init {@link MimeTypes}</li>
* <li>enterScope
* <ul>
* <li>{@link #startContext()}
* <ul>
* <li>{@link #configure()}
* <ul>
* <li>Call {@link Configuration#configure(WebAppContext)} on enabled {@link Configurations}</li>
* </ul>
* </li>
* <li>{@link MetaData#resolve(WebAppContext)}</li>
* <li>{@link #startContext()}
* <li>QuickStart may generate here and/or abort start
* <ul>
* <li>{@link ServletContextHandler#startContext}
* <ul>
* <li>Decorate listeners</li>
* <li>{@link ContextHandler#startContext}
* <ul>
* <li>add {@link ManagedAttributeListener}</li>
* <li>{@link AbstractHandler#doStart}</li>
* <li>{@link #callContextInitialized(jakarta.servlet.ServletContextListener, jakarta.servlet.ServletContextEvent)}</li>
* </ul>
* </li>
* <li>{@link ServletHandler#initialize()}</li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
* </li>
* </ul>
* </li>
* <li>exitScope</li>
* </ul>
* </li>
* </ul>
* </li>
* <li>{@link #postConfigure()}</li>
* </ul>
*
* </blockquote>
*/
@ManagedObject("Web Application ContextHandler")
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context, Deployable
{
static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
@ -211,6 +142,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
private boolean _throwUnavailableOnStartupException = false;
private MetaData _metadata = new MetaData();
private boolean _defaultContextPath = true;
public static WebAppContext getCurrentWebAppContext()
{
@ -307,6 +239,54 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
parent.addHandler(this);
}
@Override
public void initializeDefaults(Map<String, String> 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.BASE_TEMP_DIR -> setAttribute(BASETEMPDIR, 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;
}
public boolean isContextPathDefault()
{
return _defaultContextPath;
}
@Override
public void setContextPath(String contextPath)
{
super.setContextPath(contextPath);
_defaultContextPath = false;
}
public void setDefaultContextPath(String contextPath)
{
super.setContextPath(contextPath);
_defaultContextPath = true;
}
/**
* @param displayName The servletContextName to set.
*/
@ -950,14 +930,14 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
name = String.format("%s@%x", name, hashCode());
dumpObjects(out, indent,
Dumpable.named("environment", ENVIRONMENT.getName()),
Dumpable.named("environment", ContextHandler.ENVIRONMENT.getName()),
new ClassLoaderDump(getClassLoader()),
new DumpableCollection("Systemclasses " + name, systemClasses),
new DumpableCollection("Serverclasses " + name, serverClasses),
new DumpableCollection("Configurations " + name, _configurations),
new DumpableCollection("Handler attributes " + name, getAttributes().asAttributeMap().entrySet()),
new DumpableCollection("Context attributes " + name, getServletContext().getContextHandler().asAttributeMap().entrySet()),
new DumpableCollection("Environment attributes " + name, ENVIRONMENT.asAttributeMap().entrySet()),
new DumpableCollection("Environment attributes " + name, ContextHandler.ENVIRONMENT.asAttributeMap().entrySet()),
new DumpableCollection("EventListeners " + this, getEventListeners()),
new DumpableCollection("Initparams " + name, getInitParams().entrySet())
);

View File

@ -20,7 +20,6 @@ import java.util.List;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -131,12 +130,11 @@ public class MetaInfConfigurationTest
* @throws Exception if the test fails
*/
@Test
@Disabled // TODO
public void testFindAndFilterContainerPathsJDK9() throws Exception
{
MetaInfConfiguration config = new MetaInfConfiguration();
WebAppContext context = new WebAppContext();
context.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, ".*/jetty-util-[^/]*\\.jar$|.*/jetty-util/target/classes/$|.*/foo-bar-janb.jar");
context.setAttribute(MetaInfConfiguration.CONTAINER_JAR_PATTERN, ".*servlet-api-[^/]*\\.jar$|.*/foo-bar-janb.jar");
WebAppClassLoader loader = new WebAppClassLoader(context);
context.setClassLoader(loader);
config.findAndFilterContainerPaths(context);
@ -145,7 +143,7 @@ public class MetaInfConfigurationTest
for (Resource r : containerResources)
{
String s = r.toString();
assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("jetty-util"));
assertTrue(s.endsWith("foo-bar-janb.jar") || s.contains("servlet-api"));
}
}
}

View File

@ -16,7 +16,7 @@ deploy
[files]
webapps-ee10/root-ee10/
webapps-ee10/root-ee10/images/
basehome:modules/demo.d/ee10-root/index.html|webapps-ee10/root-ee10/index.html
basehome:modules/demo.d/ee10-root/jetty.css|webapps-ee10/root-ee10/jetty.css
basehome:modules/demo.d/ee10-root/images/jetty-pic.png|webapps-ee10/root-ee10/images/jetty-pic.png
basehome:modules/demo.d/ee10-root/images/webtide_logo.jpg|webapps-ee10/root-ee10/images/webtide_logo.jpg
basehome:modules/demo.d/ee10-root/index.html|webapps/root-ee10/index.html
basehome:modules/demo.d/ee10-root/jetty.css|webapps/root-ee10/jetty.css
basehome:modules/demo.d/ee10-root/images/jetty-pic.png|webapps/root-ee10/images/jetty-pic.png
basehome:modules/demo.d/ee10-root/images/webtide_logo.jpg|webapps/root-ee10/images/webtide_logo.jpg

View File

@ -186,7 +186,7 @@
<!-- <module>documentation</module>-->
<module>jetty-core</module>
<module>jetty-integrations</module>
<module>jetty-ee8</module>
<!-- <module>jetty-ee8</module>-->
<module>jetty-ee9</module>
<module>jetty-ee10</module>
<module>jetty-home</module>
@ -1208,6 +1208,11 @@
<artifactId>jetty-deploy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-hazelcast</artifactId>