Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x

This commit is contained in:
Jan Bartel 2024-05-03 12:18:49 +10:00
commit ce13b8590f
28 changed files with 351 additions and 184 deletions

View File

@ -169,8 +169,9 @@ public class WebAppClassLoading
* Add a hidden (server) Class pattern to use for all WebAppContexts of a given {@link Server}.
* @param attributes The {@link Attributes} instance to add classes to
* @param patterns the patterns to use
* @deprecated use {@link #addHiddenClasses(Server, String...)} instead
*/
@Deprecated (forRemoval = true)
@Deprecated (since = "12.0.9", forRemoval = true)
public static void addHiddenClasses(Attributes attributes, String... patterns)
{
if (patterns != null && patterns.length > 0)

View File

@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.StringTokenizer;
import org.eclipse.jetty.deploy.AppLifeCycle;
@ -195,13 +196,14 @@ public class JettyServerFactory
private static DeploymentManager ensureDeploymentManager(Server server)
{
Collection<DeploymentManager> deployers = server.getBeans(DeploymentManager.class);
DeploymentManager deploymentManager;
DeploymentManager deploymentManager = null;
if (deployers != null)
{
deploymentManager = deployers.stream().findFirst().get();
deploymentManager = deployers.stream().findFirst().orElse(null);
}
else
if (deploymentManager == null)
{
deploymentManager = new DeploymentManager();
deploymentManager.setContexts(getContextHandlerCollection(server));

View File

@ -21,7 +21,6 @@ import java.util.Deque;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@ -31,6 +30,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
@ -68,7 +68,7 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private final boolean _rfc7239;
private final String _forwardedHeader;
private final ConcurrentMap<String, Remote> _remotes = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Remote> _remotes = new ConcurrentHashMap<>();
private volatile boolean _enabled;
private int _threadLimit = 10;
@ -163,7 +163,10 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
}
// We accept the request and will always handle it.
LimitedRequest limitedRequest = new LimitedRequest(remote, next, request, response, callback);
// Use a compute method to remove the Remote instance as it is necessary for
// the ref counter release and the removal to be atomic.
LimitedRequest limitedRequest = new LimitedRequest(remote, next, request, response, Callback.from(callback, () ->
_remotes.computeIfPresent(remote._ip, (k, v) -> v._referenceCounter.release() ? null : v)));
limitedRequest.handle();
return true;
}
@ -177,7 +180,8 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private Remote getRemote(Request baseRequest)
{
String ip = getRemoteIP(baseRequest);
LOG.debug("ip={}", ip);
if (LOG.isDebugEnabled())
LOG.debug("ip={}", ip);
if (ip == null)
return null;
@ -185,15 +189,18 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
if (limit <= 0)
return null;
Remote remote = _remotes.get(ip);
if (remote == null)
// Use a compute method to create or retain the Remote instance as it is necessary for
// the ref counter increment or the instance creation to be mutually exclusive.
// The map MUST be a CHM as it guarantees the remapping function is only called once.
return _remotes.compute(ip, (k, v) ->
{
Remote r = new Remote(baseRequest.getContext(), ip, limit);
remote = _remotes.putIfAbsent(ip, r);
if (remote == null)
remote = r;
}
return remote;
if (v != null)
{
v._referenceCounter.retain();
return v;
}
return new Remote(baseRequest.getContext(), k, limit);
});
}
protected String getRemoteIP(Request baseRequest)
@ -208,7 +215,7 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
}
// If no remote IP from a header, determine it directly from the channel
// Do not use the request methods, as they may have been lied to by the
// Do not use the request methods, as they may have been lied to by the
// RequestCustomizer!
if (baseRequest.getConnectionMetaData().getRemoteSocketAddress() instanceof InetSocketAddress inetAddr)
{
@ -255,7 +262,12 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
int comma = forwardedFor.lastIndexOf(',');
return (comma >= 0) ? forwardedFor.substring(comma + 1).trim() : forwardedFor;
}
int getRemoteCount()
{
return _remotes.size();
}
private static class LimitedRequest extends Request.Wrapper
{
private final Remote _remote;
@ -517,6 +529,7 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private static final class Remote
{
private final Executor _executor;
private final Retainable.ReferenceCounter _referenceCounter = new Retainable.ReferenceCounter();
private final String _ip;
private final int _limit;
private final AutoLock _lock = new AutoLock();

View File

@ -104,6 +104,8 @@ public class ThreadLimitHandlerTest
last.set(null);
_local.getResponse("GET / HTTP/1.0\r\nForwarded: for=1.2.3.4\r\n\r\n");
assertThat(last.get(), is("0.0.0.0"));
await().atMost(5, TimeUnit.SECONDS).until(handler::getRemoteCount, is(0));
}
@Test
@ -147,6 +149,8 @@ public class ThreadLimitHandlerTest
last.set(null);
_local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.1.1.1\r\nX-Forwarded-For: 6.6.6.6,1.2.3.4\r\nForwarded: for=1.2.3.4\r\n\r\n");
assertThat(last.get(), is("1.2.3.4"));
await().atMost(5, TimeUnit.SECONDS).until(handler::getRemoteCount, is(0));
}
@Test
@ -190,6 +194,8 @@ public class ThreadLimitHandlerTest
last.set(null);
_local.getResponse("GET / HTTP/1.0\r\nX-Forwarded-For: 1.1.1.1\r\nForwarded: for=6.6.6.6; for=1.2.3.4\r\nX-Forwarded-For: 6.6.6.6\r\nForwarded: proto=https\r\n\r\n");
assertThat(last.get(), is("1.2.3.4"));
await().atMost(5, TimeUnit.SECONDS).until(handler::getRemoteCount, is(0));
}
@Test
@ -248,6 +254,8 @@ public class ThreadLimitHandlerTest
await().atMost(10, TimeUnit.SECONDS).until(total::get, is(10));
await().atMost(10, TimeUnit.SECONDS).until(count::get, is(0));
await().atMost(5, TimeUnit.SECONDS).until(handler::getRemoteCount, is(0));
}
@Test
@ -367,5 +375,7 @@ public class ThreadLimitHandlerTest
assertThat(response, containsString(" 200 OK"));
assertThat(response, containsString(" read 2"));
}
await().atMost(5, TimeUnit.SECONDS).until(handler::getRemoteCount, is(0));
}
}

View File

@ -86,6 +86,22 @@
</plugins>
</build>
</profile>
<profile>
<id>enable-foreign</id>
<activation>
<jdk>[22,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine} ${jetty.surefire.argLine} --enable-native-access=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -1063,9 +1063,7 @@ public class HttpClientStreamTest extends AbstractTest
@MethodSource("transports")
public void testUploadWithPendingReadConcurrentServerCloseClosesStream(Transport transport) throws Exception
{
CountDownLatch serverDemandLatch = new CountDownLatch(1);
CountDownLatch serverLatch = new CountDownLatch(1);
AtomicReference<Content.Chunk> lastChunk = new AtomicReference<>();
start(transport, new Handler.Abstract()
{
@Override
@ -1076,16 +1074,14 @@ public class HttpClientStreamTest extends AbstractTest
@Override
public void run()
{
// With H2, once the connector is stopping, there is no guarantee that the demand will be serviced
// as the execution strategy is busy shutting down but is needed to run the dispatched thread that
// services the demand; so we cannot expect that a last chunk will be read here.
Content.Chunk chunk = request.read();
if (chunk != null)
chunk.release();
if (chunk == null || !chunk.isLast())
{
request.demand(this);
return;
}
lastChunk.set(chunk);
serverDemandLatch.countDown();
}
});
serverLatch.countDown();
@ -1149,9 +1145,6 @@ public class HttpClientStreamTest extends AbstractTest
assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
assertTrue(serverDemandLatch.await(5, TimeUnit.SECONDS));
assertTrue(Content.Chunk.isFailure(lastChunk.get(), true));
assertInstanceOf(IOException.class, lastChunk.get().getFailure());
}
@ParameterizedTest

View File

@ -21,7 +21,7 @@ public interface ClassVisibilityChecker
{
/**
* Is the class a Protected (System) Class.
* A System class is a class that is visible to a webapplication,
* A System class is a class that is visible to a web application,
* but that cannot be overridden by the contents of WEB-INF/lib or
* WEB-INF/classes
*
@ -33,8 +33,8 @@ public interface ClassVisibilityChecker
/**
* Is the class a Hidden (Server) Class.
* A Server class is a class that is part of the implementation of
* the server and is NIT visible to a webapplication. The web
* application may provide it's own implementation of the class,
* the server and is NOT visible to a web application. The web
* application may provide its own implementation of the class,
* to be loaded from WEB-INF/lib or WEB-INF/classes
*
* @param clazz The fully qualified name of the class.
@ -44,7 +44,7 @@ public interface ClassVisibilityChecker
/**
* Is the class a System Class.
* A System class is a class that is visible to a webapplication,
* A System class is a class that is visible to a web application,
* but that cannot be overridden by the contents of WEB-INF/lib or
* WEB-INF/classes
*
@ -61,8 +61,8 @@ public interface ClassVisibilityChecker
/**
* Is the class a Server Class.
* A Server class is a class that is part of the implementation of
* the server and is NIT visible to a webapplication. The web
* application may provide it's own implementation of the class,
* the server and is NOT visible to a web application. The web
* application may provide its own implementation of the class,
* to be loaded from WEB-INF/lib or WEB-INF/classes
*
* @param clazz The fully qualified name of the class.

View File

@ -13,8 +13,6 @@
package org.eclipse.jetty.ee10.cdi;
import java.util.function.Predicate;
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.ee10.webapp.AbstractConfiguration;
@ -33,11 +31,11 @@ public class CdiConfiguration extends AbstractConfiguration
{
super(new Builder()
.protectAndExpose("org.eclipse.jetty.ee10.cdi.CdiServletContainerInitializer")
.hide(getHiddenClasses())
.hide(getCdiHiddenClasses())
.addDependents(AnnotationConfiguration.class, PlusConfiguration.class));
}
private static String[] getHiddenClasses()
private static String[] getCdiHiddenClasses()
{
//Only hide the cdi api classes if there is not also an impl on the
//environment classpath - vital for embedded uses.
@ -45,4 +43,4 @@ public class CdiConfiguration extends AbstractConfiguration
return new String[]{"jakarta.enterprise.", "jakarta.decorator."};
return new String[0];
}
}
}

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Call name="addServerClassMatcher">
<Call name="addHiddenClassMatcher">
<Arg>
<New class="org.eclipse.jetty.util.ClassMatcher">
<Arg>

View File

@ -42,6 +42,10 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>jetty-ee10-demo-mock-resources</artifactId>
@ -263,18 +267,6 @@
<artifactId>jetty-alpn-server</artifactId>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
-->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-slf4j-impl</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.demos</groupId>
<artifactId>jetty-ee10-demo-container-initializer</artifactId>
@ -539,6 +531,8 @@
<systemPropertyVariables>
<mavenRepoPath>${session.repositorySession.localRepository.basedir.absolutePath}</mavenRepoPath>
<settingsFilePath>${env.GLOBAL_MVN_SETTINGS}</settingsFilePath>
<!-- uncomment to be able to use jetty logging -->
<!-- pax.exam.system>default</pax.exam.system -->
</systemPropertyVariables>
<argLine>-Dconscrypt-version=${conscrypt.version}</argLine>
<!-- TODO -->

View File

@ -67,10 +67,9 @@
<Item>org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration</Item>
<Item>org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JmxConfiguration</Item>
<Item>config.org.eclipse.jetty.ee10.websocket.server.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration</Item>
</Array>
</Arg>

View File

@ -0,0 +1,123 @@
//
// ========================================================================
// 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.ee10.osgi.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.osgi.OSGiServerConstants;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
/**
* TestJettyOSGiBootWithBundle
*
* Tests reading config from a bundle and loading classes from it
*
* Tests the ServiceContextProvider.
*/
@RunWith(PaxExam.class)
public class TestJettyOSGiBootWithBundle
{
private static final String TEST_JETTY_HOME_BUNDLE = "test-jetty-xml-bundle";
@Inject
BundleContext bundleContext = null;
@Configuration
public static Option[] configure() throws IOException
{
ArrayList<Option> options = new ArrayList<>();
options.addAll(TestOSGiUtil.configurePaxExamLogging());
options.add(TestOSGiUtil.optionalRemoteDebug());
options.add(CoreOptions.junitBundles());
options.addAll(configureJettyHomeAndPort());
options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*"));
options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res", "com.sun.org.apache.xml.internal.utils",
"com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal",
"com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects"));
TestOSGiUtil.coreJettyDependencies(options);
TestOSGiUtil.coreJspDependencies(options);
options.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-java-client").versionAsInProject().start());
options.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-alpn-client").versionAsInProject().start());
//back down version of bnd used here because tinybundles expects only this version
options.add(mavenBundle().groupId("biz.aQute.bnd").artifactId("biz.aQute.bndlib").version("3.5.0").start());
options.add(mavenBundle().groupId("org.ops4j.pax.tinybundles").artifactId("tinybundles").versionAsInProject().start());
TinyBundle bundle = TinyBundles.bundle();
bundle.add(SomeCustomBean.class);
bundle.set(Constants.BUNDLE_SYMBOLICNAME, TEST_JETTY_HOME_BUNDLE);
File etcFolder = new File("src/test/config/etc");
bundle.add("jettyhome/etc/jetty-http-boot-with-bundle.xml", new FileInputStream(new File(etcFolder, "jetty-http-boot-with-bundle.xml")));
bundle.add("jettyhome/etc/jetty-with-custom-class.xml", new FileInputStream(new File(etcFolder, "jetty-with-custom-class.xml")));
options.add(CoreOptions.streamBundle(bundle.build()).startLevel(1));
options.add(CoreOptions.cleanCaches(true));
return options.toArray(new Option[0]);
}
public static List<Option> configureJettyHomeAndPort()
{
List<Option> options = new ArrayList<>();
options.add(systemProperty(OSGiServerConstants.MANAGED_JETTY_XML_CONFIG_URLS).value("etc/jetty-with-custom-class.xml,etc/jetty-http-boot-with-bundle.xml"));
options.add(systemProperty("jetty.http.port").value("0"));
// TODO: FIXME: options.add(systemProperty("jetty.ssl.port").value(String.valueOf(TestOSGiUtil.DEFAULT_SSL_PORT)));
options.add(systemProperty("jetty.home.bundle").value(TEST_JETTY_HOME_BUNDLE));
return options;
}
@Test
public void testContextHandler() throws Exception
{
if (Boolean.getBoolean(TestOSGiUtil.BUNDLE_DEBUG))
TestOSGiUtil.diagnoseBundles(bundleContext);
// now test the context
HttpClient client = new HttpClient();
try
{
client.start();
String tmp = System.getProperty("boot.bundle.port");
assertNotNull(tmp);
int port = Integer.valueOf(tmp.trim());
ContentResponse response = client.GET("http://127.0.0.1:" + port);
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
String content = new String(response.getContent());
assertNotNull(content);
}
finally
{
client.stop();
}
}
}

View File

@ -153,41 +153,20 @@ public class TestOSGiUtil
res.add(systemProperty("org.ops4j.pax.url.mvn.settings").value(System.getProperty("settingsFilePath")));
}
res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject().noStart());
/*
* Jetty 11 uses slf4j 2.0.0 by default, however we want to test with slf4j 1.7.30 for backwards compatibility.
* To do that, we need to use slf4j-simple as the logging implementation. We make a simplelogger.properties
* file available so that jetty logging can be configured
*/
// BEGIN - slf4j 1.7.x
/* slf4j-simple conflicts with both slf4j 1.7.x, and jetty-slf4j-impl. (but in different ways)
TinyBundle simpleLoggingPropertiesBundle = TinyBundles.bundle();
simpleLoggingPropertiesBundle.add("simplelogger.properties", ClassLoader.getSystemResource("simplelogger.properties"));
simpleLoggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "simple-logger-properties");
simpleLoggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "slf4j-simple");
simpleLoggingPropertiesBundle.add(FragmentActivator.class);
res.add(CoreOptions.streamBundle(simpleLoggingPropertiesBundle.build()).noStart());
res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-simple").versionAsInProject().noStart());
*/
// END - slf4j 1.7.x
/*
* When running with slf4j >= 2.0.0, remove the slf4j simple logger above and uncomment the following lines
*/
// BEGIN - slf4j 2.x
//configure jetty slf4j logging, and provide a jetty-logging properties file
//note 1: you will need to change the surefire plugin config in pom.xml to set the system property "pax.exam.system=false"
//to make paxexam use this slf4j
//note 2: if you do set the above system property, more than likely the test will not finish, no idea why
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-slf4j-impl").versionAsInProject().start());
res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject());
TinyBundle loggingPropertiesBundle = TinyBundles.bundle();
loggingPropertiesBundle.add("jetty-logging.properties", ClassLoader.getSystemResource("jetty-logging.properties"));
loggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "jetty-logging-properties");
loggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "org.eclipse.jetty.logging");
loggingPropertiesBundle.add(FragmentActivator.class);
res.add(CoreOptions.streamBundle(loggingPropertiesBundle.build()).noStart());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-slf4j-impl").versionAsInProject().start());
// END - slf4j 2.x
res.add(mavenBundle().groupId("jakarta.el").artifactId("jakarta.el-api").versionAsInProject().start());
res.add(mavenBundle().groupId("jakarta.servlet").artifactId("jakarta.servlet-api").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.platform").artifactId("org.eclipse.osgi.util").versionAsInProject());
res.add(mavenBundle().groupId("org.osgi").artifactId("org.osgi.service.cm").versionAsInProject());

View File

@ -19,13 +19,15 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.util.ClassMatcher;
public class AbstractConfiguration implements Configuration
{
private final boolean _enabledByDefault;
private final List<String> _after;
private final List<String> _before;
private final ClassMatcher _system;
private final ClassMatcher _server;
private final ClassMatcher _protected;
private final ClassMatcher _hidden;
public static class Builder
{
@ -88,7 +90,7 @@ public class AbstractConfiguration implements Configuration
/**
* Protect classes from modification by the web application by adding them
* to the {@link WebAppConfiguration#getSystemClasses()}
* to the {@link WebAppConfiguration#getProtectedClasses()}
*
* @param classes classname or package pattern
*/
@ -100,7 +102,7 @@ public class AbstractConfiguration implements Configuration
/**
* Hide classes from the web application by adding them
* to the {@link WebAppConfiguration#getServerClasses()}
* to the {@link WebAppConfiguration#getHiddenClasses()}
*
* @param classes classname or package pattern
*/
@ -112,7 +114,7 @@ public class AbstractConfiguration implements Configuration
/**
* Expose classes to the web application by adding them
* as exclusions to the {@link WebAppConfiguration#getServerClasses()}
* as exclusions to the {@link WebAppConfiguration#getHiddenClasses()}
*
* @param classes classname or package pattern
*/
@ -129,9 +131,9 @@ public class AbstractConfiguration implements Configuration
/**
* Protect classes from modification by the web application by adding them
* to the {@link WebAppConfiguration#getSystemClasses()} and
* to the {@link WebAppConfiguration#getProtectedClasses()} and
* expose them to the web application by adding them
* as exclusions to the {@link WebAppConfiguration#getServerClasses()}
* as exclusions to the {@link WebAppConfiguration#getHiddenClasses()}
*
* @param classes classname or package pattern
*/
@ -154,8 +156,8 @@ public class AbstractConfiguration implements Configuration
_enabledByDefault = builder._enabledByDefault;
_after = List.copyOf(builder._after);
_before = List.copyOf(builder._before);
_system = new ClassMatcher(builder._system).asImmutable();
_server = new ClassMatcher(builder._server).asImmutable();
_protected = new ClassMatcher(builder._system).asImmutable();
_hidden = new ClassMatcher(builder._server).asImmutable();
}
@Override
@ -171,15 +173,15 @@ public class AbstractConfiguration implements Configuration
}
@Override
public ClassMatcher getSystemClasses()
public ClassMatcher getProtectedClasses()
{
return _system;
return _protected;
}
@Override
public ClassMatcher getServerClasses()
public ClassMatcher getHiddenClasses()
{
return _server;
return _hidden;
}
@Override

View File

@ -21,7 +21,6 @@ import org.eclipse.jetty.util.IncludeExcludeSet;
/**
* @deprecated Use org.eclipse.jetty.util.ClassMatcher
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher
{

View File

@ -17,6 +17,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.ServiceLoader;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.TopologicalSort;
/**
@ -43,8 +44,8 @@ import org.eclipse.jetty.util.TopologicalSort;
* (eg {@link JndiConfiguration}, {@link JaasConfiguration}} etc.) can be added or removed without concern
* for ordering.
* </p>
* <p>Also since Jetty-9.4, Configurations are responsible for providing {@link #getServerClasses()} and
* {@link #getSystemClasses()} to configure the {@link WebAppClassLoader} for each context.
* <p>Also since Jetty-9.4, Configurations are responsible for providing {@link #getHiddenClasses()} and
* {@link #getProtectedClasses()} to configure the {@link WebAppClassLoader} for each context.
* </p>
*/
public interface Configuration
@ -93,25 +94,43 @@ public interface Configuration
}
/**
* Get the system classes associated with this Configuration.
* Get the system (protected) classes associated with this Configuration.
*
* @return ClassMatcher of system classes.
*/
default ClassMatcher getSystemClasses()
default ClassMatcher getProtectedClasses()
{
return new ClassMatcher();
}
/**
* Get the system classes associated with this Configuration.
* Get the server (hidden) classes associated with this Configuration.
*
* @return ClassMatcher of server classes.
*/
default ClassMatcher getServerClasses()
default ClassMatcher getHiddenClasses()
{
return new ClassMatcher();
}
/**
* @deprecated use {@link #getProtectedClasses()} instead
*/
@Deprecated(since = "12.0.8", forRemoval = true)
default org.eclipse.jetty.ee10.webapp.ClassMatcher getSystemClasses()
{
return new org.eclipse.jetty.ee10.webapp.ClassMatcher(getProtectedClasses());
}
/**
* @deprecated use {@link #getHiddenClasses()} instead
*/
@Deprecated(since = "12.0.8", forRemoval = true)
default org.eclipse.jetty.ee10.webapp.ClassMatcher getServerClasses()
{
return new org.eclipse.jetty.ee10.webapp.ClassMatcher(getHiddenClasses());
}
/**
* Set up for configuration.
* <p>

View File

@ -111,9 +111,33 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
List<Resource> getExtraClasspath();
boolean isHiddenResource(String name, URL parentUrl);
/**
* @deprecated use {@link #isHiddenResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
default boolean isServerResource(String name, URL parentUrl)
{
return isHiddenResource(name, parentUrl);
}
boolean isProtectedResource(String name, URL webappUrl);
/**
* @deprecated use {@link #isProtectedResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
default boolean isSystemResource(String name, URL webappUrl)
{
return isProtectedResource(name, webappUrl);
}
default boolean isHiddenResource(String name, URL parentUrl)
{
return false;
}
default boolean isProtectedResource(String name, URL webappUrl)
{
return false;
}
}
/**
@ -521,7 +545,6 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
if (webappUrl != null && (!checkSystemResource || !_context.isProtectedResource(name, webappUrl)))
{
webappClass = this.foundClass(name, webappUrl);
resolveClass(webappClass);
if (LOG.isDebugEnabled())

View File

@ -428,7 +428,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
// Add the known server class inclusions for all known configurations
for (Configuration configuration : Configurations.getKnown())
{
_hiddenClasses.include(configuration.getServerClasses().getInclusions());
_hiddenClasses.include(configuration.getHiddenClasses().getInclusions());
}
// Setup Configuration classes for this webapp!
@ -436,8 +436,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
_configurations.sort();
for (Configuration configuration : _configurations)
{
_protectedClasses.add(configuration.getSystemClasses().getPatterns());
_hiddenClasses.exclude(configuration.getServerClasses().getExclusions());
_protectedClasses.add(configuration.getProtectedClasses().getPatterns());
_hiddenClasses.exclude(configuration.getHiddenClasses().getExclusions());
}
// Configure classloader
@ -623,9 +623,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
/**
* Set the hidden (aka server) classes patterns.
* Set the hidden (server) classes patterns.
* <p>
* These classes/packages are used to implement the server and are hiddenClasses
* These classes/packages are used to implement the server and are hidden
* from the context. If the context needs to load these classes, it must have its
* own copy of them in WEB-INF/lib or WEB-INF/classes.
*
@ -638,7 +638,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
/**
* Set the protected (aka system) classes patterns.
* Set the protected (system) classes patterns.
* <p>
* These classes/packages are provided by the JVM and
* cannot be replaced by classes of the same name from WEB-INF,
@ -683,7 +683,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
}
/**
* @return The ClassMatcher used to match Server (hiddenClasses) classes
* @return The ClassMatcher used to match Server (hidden) classes
*/
public ClassMatcher getHiddenClassMatcher()
{
@ -696,7 +696,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _protectedClasses.getPatterns();
}
@ManagedAttribute(value = "classes and packages hiddenClasses by the context classloader", readonly = true)
@ManagedAttribute(value = "classes and packages hidden by the context classloader", readonly = true)
public String[] getHiddenClasses()
{
return _hiddenClasses.getPatterns();
@ -730,56 +730,54 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
* @deprecated use {@link #setHiddenClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void setServerClassMatcher(ClassMatcher serverClasses)
public void setServerClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher serverClasses)
{
_hiddenClasses.clear();
_hiddenClasses.add(serverClasses.getPatterns());
setHiddenClassMatcher(serverClasses);
}
/**
* @deprecated use {@link #setProtectedClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void setSystemClassMatcher(ClassMatcher systemClasses)
public void setSystemClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher systemClasses)
{
_protectedClasses.clear();
_protectedClasses.add(systemClasses.getPatterns());
setProtectedClassMatcher(systemClasses);
}
/**
* @deprecated use {@link #addHiddenClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void addServerClassMatcher(ClassMatcher serverClasses)
public void addServerClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher serverClasses)
{
_hiddenClasses.add(serverClasses.getPatterns());
addHiddenClassMatcher(serverClasses);
}
/**
* @deprecated use {@link #addProtectedClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void addSystemClassMatcher(ClassMatcher systemClasses)
public void addSystemClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher systemClasses)
{
_protectedClasses.add(systemClasses.getPatterns());
addProtectedClassMatcher(systemClasses);
}
/**
* @deprecated use {@link #getProtectedClassMatcher()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public ClassMatcher getSystemClassMatcher()
public org.eclipse.jetty.ee10.webapp.ClassMatcher getSystemClassMatcher()
{
return _protectedClasses;
return new org.eclipse.jetty.ee10.webapp.ClassMatcher(getProtectedClassMatcher());
}
/**
* @deprecated use {@link #getHiddenClassMatcher()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public ClassMatcher getServerClassMatcher()
public org.eclipse.jetty.ee10.webapp.ClassMatcher getServerClassMatcher()
{
return _hiddenClasses;
return new org.eclipse.jetty.ee10.webapp.ClassMatcher(getHiddenClassMatcher());
}
/**
@ -788,7 +786,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Deprecated(since = "12.0.8", forRemoval = true)
public String[] getSystemClasses()
{
return _protectedClasses.getPatterns();
return getProtectedClasses();
}
/**
@ -797,7 +795,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Deprecated(since = "12.0.8", forRemoval = true)
public String[] getServerClasses()
{
return _hiddenClasses.getPatterns();
return getHiddenClasses();
}
/**
@ -806,7 +804,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isServerClass(Class<?> clazz)
{
return _hiddenClasses.match(clazz);
return isHiddenClass(clazz);
}
/**
@ -815,25 +813,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isSystemClass(Class<?> clazz)
{
return _protectedClasses.match(clazz);
}
/**
* @deprecated use {@link #isHiddenResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isServerResource(String name, URL url)
{
return _hiddenClasses.match(name, url);
}
/**
* @deprecated use {@link #isProtectedResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isSystemResource(String name, URL url)
{
return _protectedClasses.match(name, url);
return isProtectedClass(clazz);
}
@Override

View File

@ -75,7 +75,7 @@ public class WebAppClassLoaderTest
}
@AfterEach
public void afterEach() throws Exception
public void afterEach()
{
IO.close(_loader);
LifeCycle.stop(_server);
@ -315,7 +315,6 @@ public class WebAppClassLoaderTest
resources = Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(webappWebInfLibAcme);
expected.add(webappWebInfClasses);
expected.add(targetTestClasses);

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee8.webapp.WebAppContext">
<Call name="addServerClassMatcher">
<Arg>
<New class="org.eclipse.jetty.util.ClassMatcher">
<New class="org.eclipse.jetty.ee8.webapp.ClassMatcher">
<Arg>
<Array type="java.lang.String">
<Item>-org.eclipse.jetty.util.Decorator</Item>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_9_3.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Call name="addServerClassMatcher">
<Arg>
<New class="org.eclipse.jetty.util.ClassMatcher">
<New class="org.eclipse.jetty.ee9.webapp.ClassMatcher">
<Arg>
<Array type="java.lang.String">
<Item>-org.eclipse.jetty.util.Decorator</Item>

View File

@ -21,17 +21,19 @@ import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.InetAddressSet;
@ -72,7 +74,7 @@ public class ThreadLimitHandler extends HandlerWrapper
private final boolean _rfc7239;
private final String _forwardedHeader;
private final IncludeExcludeSet<String, InetAddress> _includeExcludeSet = new IncludeExcludeSet<>(InetAddressSet.class);
private final ConcurrentMap<String, Remote> _remotes = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Remote> _remotes = new ConcurrentHashMap<>();
private volatile boolean _enabled;
private int _threadLimit = 10;
@ -178,6 +180,17 @@ public class ThreadLimitHandler extends HandlerWrapper
}
else
{
baseRequest.addEventListener(new ServletRequestListener()
{
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
// Use a compute method to remove the Remote instance as it is necessary for
// the ref counter release and the removal to be atomic.
_remotes.computeIfPresent(remote._ip, (k, v) -> v._referenceCounter.release() ? null : v);
}
});
// Do we already have a future permit from a previous invocation?
Closeable permit = (Closeable)baseRequest.getAttribute(PERMIT);
try
@ -249,14 +262,18 @@ public class ThreadLimitHandler extends HandlerWrapper
if (limit <= 0)
return null;
remote = _remotes.get(ip);
if (remote == null)
// Use a compute method to create or retain the Remote instance as it is necessary for
// the ref counter increment or the instance creation to be mutually exclusive.
// The map MUST be a CHM as it guarantees the remapping function is only called once.
remote = _remotes.compute(ip, (k, v) ->
{
Remote r = new Remote(ip, limit);
remote = _remotes.putIfAbsent(ip, r);
if (remote == null)
remote = r;
}
if (v != null)
{
v._referenceCounter.retain();
return v;
}
return new Remote(k, limit);
});
baseRequest.setAttribute(REMOTE, remote);
@ -325,6 +342,7 @@ public class ThreadLimitHandler extends HandlerWrapper
private final String _ip;
private final int _limit;
private final AutoLock _lock = new AutoLock();
private final Retainable.ReferenceCounter _referenceCounter = new Retainable.ReferenceCounter();
private int _permits;
private Deque<CompletableFuture<Closeable>> _queue = new ArrayDeque<>();
private final CompletableFuture<Closeable> _permitted = CompletableFuture.completedFuture(this);

View File

@ -67,7 +67,7 @@
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.ee9.webapp.JmxConfiguration</Item>
<Item>config.org.eclipse.jetty.ee10.websocket.server.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee9.websocket.server.config.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee9.osgi.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.ee9.osgi.boot.OSGiWebInfConfiguration</Item>

View File

@ -18,11 +18,6 @@ import java.util.Map;
import org.eclipse.jetty.util.IncludeExcludeSet;
/**
* @deprecated Use org.eclipse.jetty.util.ClassMatcher
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher
{
public ClassMatcher()

View File

@ -104,7 +104,7 @@ public interface Configuration
}
/**
* Get the system classes associated with this Configuration.
* Get the server classes associated with this Configuration.
*
* @return ClassMatcher of server classes.
*/

View File

@ -20,28 +20,24 @@ import java.lang.instrument.IllegalClassFormatException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
import org.eclipse.jetty.util.ClassVisibilityChecker;
import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollators;
import org.eclipse.jetty.util.resource.ResourceFactory;
@ -530,7 +526,6 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
if (webappUrl != null && (!checkSystemResource || !_context.isSystemResource(name, webappUrl)))
{
webappClass = this.foundClass(name, webappUrl);
resolveClass(webappClass);
if (LOG.isDebugEnabled())

View File

@ -55,7 +55,6 @@ import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil;
@ -673,7 +672,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
/**
* Set the server classes patterns.
* <p>
* Server classes/packages are classes used to implement the server and are hidden
* These classes/packages are used to implement the server and are hidden
* from the context. If the context needs to load these classes, it must have its
* own copy of them in WEB-INF/lib or WEB-INF/classes.
*
@ -688,7 +687,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
/**
* Set the system classes patterns.
* <p>
* System classes/packages are classes provided by the JVM and that
* These classes/packages are provided by the JVM and
* cannot be replaced by classes of the same name from WEB-INF,
* regardless of the value of {@link #setParentLoaderPriority(boolean)}.
*
@ -753,11 +752,23 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Override
public boolean isHiddenClass(Class<?> clazz)
{
return _serverClasses.match(clazz);
return isServerClass(clazz);
}
@Override
public boolean isProtectedClass(Class<?> clazz)
{
return isSystemClass(clazz);
}
@Override
public boolean isServerClass(Class<?> clazz)
{
return _serverClasses.match(clazz);
}
@Override
public boolean isSystemClass(Class<?> clazz)
{
return _systemClasses.match(clazz);
}

View File

@ -26,7 +26,6 @@ import java.util.List;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.resource.Resource;
@ -75,7 +74,7 @@ public class WebAppClassLoaderTest
}
@AfterEach
public void afterEach() throws Exception
public void afterEach()
{
IO.close(_loader);
LifeCycle.stop(_server);
@ -315,7 +314,6 @@ public class WebAppClassLoaderTest
resources = Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(webappWebInfLibAcme);
expected.add(webappWebInfClasses);
expected.add(targetTestClasses);