Issue #11847 implement environment context xml (#11859)

* Issue #11847 implement environment context xml
This commit is contained in:
Jan Bartel 2024-06-19 09:36:06 +02:00 committed by GitHub
parent 95059356c9
commit 6ee17f002c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 181 additions and 47 deletions

View File

@ -21,6 +21,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -77,8 +78,8 @@ import org.slf4j.LoggerFactory;
* <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 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>
* <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 deployment of webapps based on presence in directory")
@ -243,6 +244,7 @@ public class ContextProvider extends ScanningAppProvider
/**
* This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property.
*
* @param configurations The configuration class names as a comma separated list
*/
public void setConfigurationClasses(String configurations)
@ -252,6 +254,7 @@ public class ContextProvider extends ScanningAppProvider
/**
* This is equivalent to setting the {@link Deployable#CONFIGURATION_CLASSES} property.
*
* @param configurations The configuration class names.
*/
public void setConfigurationClasses(String[] configurations)
@ -262,8 +265,8 @@ public class ContextProvider extends ScanningAppProvider
}
/**
*
* 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")
@ -341,32 +344,48 @@ public class ContextProvider extends ScanningAppProvider
// prepare properties
Map<String, String> properties = new HashMap<>();
//add in properties from start mechanism
properties.putAll(getProperties());
Object context = null;
//check if there is a specific ContextHandler type to create set in the
//properties associated with the webapp. If there is, we create it _before_
//applying the environment xml file.
String contextHandlerClassName = app.getProperties().get(Deployable.CONTEXT_HANDLER_CLASS);
if (contextHandlerClassName != null)
context = Class.forName(contextHandlerClassName).getDeclaredConstructor().newInstance();
//add in environment-specific properties
String env = app.getEnvironmentName() == null ? "" : app.getEnvironmentName();
Path envProperties = app.getPath().getParent().resolve(env + ".properties");
if (Files.exists(envProperties))
{
try (InputStream stream = Files.newInputStream(envProperties))
{
Properties p = new Properties();
p.load(stream);
p.stringPropertyNames().forEach(k -> properties.put(k, p.getProperty(k)));
}
String str = properties.get(Deployable.ENVIRONMENT_XML);
if (!StringUtil.isEmpty(str))
{
Path envXmlPath = Paths.get(str);
if (!envXmlPath.isAbsolute())
envXmlPath = getMonitoredDirResource().getPath().getParent().resolve(envXmlPath);
context = applyXml(context, envXmlPath, env, properties);
}
}
//add in properties specific to the deployable
properties.putAll(app.getProperties());
// Handle a context XML file
if (FileID.isXml(path))
{
XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(path), null, properties)
{
@Override
public void initializeDefaults(Object context)
{
super.initializeDefaults(context);
ContextProvider.this.initializeContextHandler(context, path, properties);
}
};
xmlc.getIdMap().put("Environment", environment);
xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), path);
// If it is a core context environment, then look for a classloader
ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(path) : null;
if (coreContextClassLoader != null)
Thread.currentThread().setContextClassLoader(coreContextClassLoader);
// Create the context by running the configuration
Object context = xmlc.configure();
context = applyXml(context, path, env, properties);
// Look for the contextHandler itself
ContextHandler contextHandler = null;
@ -382,27 +401,33 @@ public class ContextProvider extends ScanningAppProvider
throw new IllegalStateException("Unknown context type of " + context);
// Set the classloader if we have a coreContextClassLoader
ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(path) : null;
if (coreContextClassLoader != null)
contextHandler.setClassLoader(coreContextClassLoader);
return contextHandler;
}
// Otherwise it must be a directory or an archive
else if (!Files.isDirectory(path) && !FileID.isWebArchive(path))
{
throw new IllegalStateException("unable to create ContextHandler for " + app);
}
// Build the web application
String contextHandlerClassName = (String)environment.getAttribute("contextHandlerClass");
if (StringUtil.isBlank(contextHandlerClassName))
throw new IllegalStateException("No ContextHandler classname for " + app);
Class<?> contextHandlerClass = Loader.loadClass(contextHandlerClassName);
if (contextHandlerClass == null)
throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app);
// Build the web application if necessary
if (context == null)
{
contextHandlerClassName = (String)environment.getAttribute("contextHandlerClass");
if (StringUtil.isBlank(contextHandlerClassName))
throw new IllegalStateException("No ContextHandler classname for " + app);
Class<?> contextHandlerClass = Loader.loadClass(contextHandlerClassName);
if (contextHandlerClass == null)
throw new IllegalStateException("Unknown ContextHandler class " + contextHandlerClassName + " for " + app);
context = contextHandlerClass.getDeclaredConstructor().newInstance();
properties.put(Deployable.WAR, path.toString());
}
Object context = contextHandlerClass.getDeclaredConstructor().newInstance();
properties.put(Deployable.WAR, path.toString());
return initializeContextHandler(context, path, properties);
}
finally
@ -411,6 +436,36 @@ public class ContextProvider extends ScanningAppProvider
}
}
protected Object applyXml(Object context, Path xml, String environment, Map<String, String> properties) throws Exception
{
if (!FileID.isXml(xml))
return null;
XmlConfiguration xmlc = new XmlConfiguration(ResourceFactory.of(this).newResource(xml), null, properties)
{
@Override
public void initializeDefaults(Object context)
{
super.initializeDefaults(context);
ContextProvider.this.initializeContextHandler(context, xml, properties);
}
};
xmlc.getIdMap().put("Environment", environment);
xmlc.setJettyStandardIdsAndProperties(getDeploymentManager().getServer(), xml);
// If it is a core context environment, then look for a classloader
ClassLoader coreContextClassLoader = Environment.CORE.equals(environment) ? findCoreContextClassLoader(xml) : null;
if (coreContextClassLoader != null)
Thread.currentThread().setContextClassLoader(coreContextClassLoader);
// Create or configure the context
if (context == null)
return xmlc.configure();
return xmlc.configure(context);
}
protected ClassLoader findCoreContextClassLoader(Path path) throws IOException
{
Path webapps = path.getParent();

View File

@ -0,0 +1,20 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.deploy;
import org.eclipse.jetty.server.handler.ContextHandler;
public class BarContextHandler extends ContextHandler
{
}

View File

@ -15,27 +15,18 @@ package org.eclipse.jetty.deploy.providers;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import org.eclipse.jetty.deploy.AppProvider;
import org.eclipse.jetty.deploy.DeploymentManager;
import org.eclipse.jetty.deploy.BarContextHandler;
import org.eclipse.jetty.deploy.test.XmlConfiguredJetty;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenPaths;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -43,6 +34,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -77,7 +69,10 @@ public class ContextProviderStartupTest
// Should not throw an Exception
jetty.load();
}
public void startJetty() throws Exception
{
// Start it
jetty.start();
}
@ -89,9 +84,47 @@ public class ContextProviderStartupTest
}
@Test
public void testStartupContext()
public void testStartupContext() throws Exception
{
startJetty();
// Check Server for Handlers
jetty.assertContextHandlerExists("/bar");
}
@Test
public void testStartupWithRelativeEnvironmentContext() throws Exception
{
Path jettyBase = jetty.getJettyBasePath();
Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = etc/core-context.xml", StandardOpenOption.CREATE_NEW);
assertTrue(Files.exists(propsFile));
Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING);
jetty.copyWebapp("bar-core-context.properties", "bar.properties");
startJetty();
//check environment context xml was applied to the produced context
ContextHandler context = jetty.getContextHandler("/bar");
assertNotNull(context);
assertThat(context.getAttribute("somename"), equalTo("somevalue"));
assertTrue(context instanceof BarContextHandler);
}
@Test
public void testStartupWithAbsoluteEnvironmentContext() throws Exception
{
Path jettyBase = jetty.getJettyBasePath();
Path propsFile = Files.writeString(jettyBase.resolve("webapps/core.properties"), Deployable.ENVIRONMENT_XML + " = " +
MavenPaths.findTestResourceFile("etc/core-context.xml"), StandardOpenOption.CREATE_NEW);
assertTrue(Files.exists(propsFile));
Files.copy(MavenPaths.findTestResourceFile("etc/core-context.xml"), jettyBase.resolve("etc/core-context.xml"), StandardCopyOption.REPLACE_EXISTING);
jetty.copyWebapp("bar-core-context.properties", "bar-core-context.properties");
startJetty();
//check environment context xml was applied to the produced context
ContextHandler context = jetty.getContextHandler("/bar");
assertNotNull(context);
assertThat(context.getAttribute("somename"), equalTo("somevalue"));
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- // -->
<!-- // ======================================================================== -->
<!-- // Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others. -->
<!-- // -->
<!-- // This program and the accompanying materials are made available under the -->
<!-- // terms of the Eclipse Public License v. 2.0 which is available at -->
<!-- // https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -->
<!-- // which is available at https://www.apache.org/licenses/LICENSE-2.0. -->
<!-- // -->
<!-- // SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -->
<!-- // ======================================================================== -->
<!-- // -->
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<Set name="contextPath">/global</Set>
<Call name="setAttribute">
<Arg>somename</Arg>
<Arg>somevalue</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,2 @@
environment: core
jetty.deploy.contextHandlerClass: org.eclipse.jetty.deploy.BarContextHandler

View File

@ -56,8 +56,10 @@ public interface Deployable
String CONFIGURATION_CLASSES = "jetty.deploy.configurationClasses";
String CONTAINER_SCAN_JARS = "jetty.deploy.containerScanJarPattern";
String CONTEXT_PATH = "jetty.deploy.contextPath";
String CONTEXT_HANDLER_CLASS = "jetty.deploy.contextHandlerClass";
String DEFAULTS_DESCRIPTOR = "jetty.deploy.defaultsDescriptor";
String ENVIRONMENT = "environment";
String ENVIRONMENT_XML = "jetty.deploy.environmentXml";
String EXTRACT_WARS = "jetty.deploy.extractWars";
String PARENT_LOADER_PRIORITY = "jetty.deploy.parentLoaderPriority";
String SCI_EXCLUSION_PATTERN = "jetty.deploy.servletContainerInitializerExclusionPattern";