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}. * 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 attributes The {@link Attributes} instance to add classes to
* @param patterns the patterns to use * @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) public static void addHiddenClasses(Attributes attributes, String... patterns)
{ {
if (patterns != null && patterns.length > 0) if (patterns != null && patterns.length > 0)

View File

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

View File

@ -21,7 +21,6 @@ import java.util.Deque;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer; 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.HttpHeader;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.QuotedCSV; import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
@ -68,7 +68,7 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private final boolean _rfc7239; private final boolean _rfc7239;
private final String _forwardedHeader; 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 volatile boolean _enabled;
private int _threadLimit = 10; private int _threadLimit = 10;
@ -163,7 +163,10 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
} }
// We accept the request and will always handle it. // 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(); limitedRequest.handle();
return true; return true;
} }
@ -177,7 +180,8 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private Remote getRemote(Request baseRequest) private Remote getRemote(Request baseRequest)
{ {
String ip = getRemoteIP(baseRequest); String ip = getRemoteIP(baseRequest);
LOG.debug("ip={}", ip); if (LOG.isDebugEnabled())
LOG.debug("ip={}", ip);
if (ip == null) if (ip == null)
return null; return null;
@ -185,15 +189,18 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
if (limit <= 0) if (limit <= 0)
return null; return null;
Remote remote = _remotes.get(ip); // Use a compute method to create or retain the Remote instance as it is necessary for
if (remote == null) // 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); if (v != null)
remote = _remotes.putIfAbsent(ip, r); {
if (remote == null) v._referenceCounter.retain();
remote = r; return v;
} }
return remote; return new Remote(baseRequest.getContext(), k, limit);
});
} }
protected String getRemoteIP(Request baseRequest) 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 // 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! // RequestCustomizer!
if (baseRequest.getConnectionMetaData().getRemoteSocketAddress() instanceof InetSocketAddress inetAddr) if (baseRequest.getConnectionMetaData().getRemoteSocketAddress() instanceof InetSocketAddress inetAddr)
{ {
@ -255,7 +262,12 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
int comma = forwardedFor.lastIndexOf(','); int comma = forwardedFor.lastIndexOf(',');
return (comma >= 0) ? forwardedFor.substring(comma + 1).trim() : forwardedFor; return (comma >= 0) ? forwardedFor.substring(comma + 1).trim() : forwardedFor;
} }
int getRemoteCount()
{
return _remotes.size();
}
private static class LimitedRequest extends Request.Wrapper private static class LimitedRequest extends Request.Wrapper
{ {
private final Remote _remote; private final Remote _remote;
@ -517,6 +529,7 @@ public class ThreadLimitHandler extends ConditionalHandler.Abstract
private static final class Remote private static final class Remote
{ {
private final Executor _executor; private final Executor _executor;
private final Retainable.ReferenceCounter _referenceCounter = new Retainable.ReferenceCounter();
private final String _ip; private final String _ip;
private final int _limit; private final int _limit;
private final AutoLock _lock = new AutoLock(); private final AutoLock _lock = new AutoLock();

View File

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

View File

@ -86,6 +86,22 @@
</plugins> </plugins>
</build> </build>
</profile> </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> </profiles>
</project> </project>

View File

@ -1063,9 +1063,7 @@ public class HttpClientStreamTest extends AbstractTest
@MethodSource("transports") @MethodSource("transports")
public void testUploadWithPendingReadConcurrentServerCloseClosesStream(Transport transport) throws Exception public void testUploadWithPendingReadConcurrentServerCloseClosesStream(Transport transport) throws Exception
{ {
CountDownLatch serverDemandLatch = new CountDownLatch(1);
CountDownLatch serverLatch = new CountDownLatch(1); CountDownLatch serverLatch = new CountDownLatch(1);
AtomicReference<Content.Chunk> lastChunk = new AtomicReference<>();
start(transport, new Handler.Abstract() start(transport, new Handler.Abstract()
{ {
@Override @Override
@ -1076,16 +1074,14 @@ public class HttpClientStreamTest extends AbstractTest
@Override @Override
public void run() 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(); Content.Chunk chunk = request.read();
if (chunk != null) if (chunk != null)
chunk.release(); chunk.release();
if (chunk == null || !chunk.isLast()) if (chunk == null || !chunk.isLast())
{
request.demand(this); request.demand(this);
return;
}
lastChunk.set(chunk);
serverDemandLatch.countDown();
} }
}); });
serverLatch.countDown(); serverLatch.countDown();
@ -1149,9 +1145,6 @@ public class HttpClientStreamTest extends AbstractTest
assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
assertTrue(closeLatch.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 @ParameterizedTest

View File

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

View File

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

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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. --> <!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Call name="addServerClassMatcher"> <Call name="addHiddenClassMatcher">
<Arg> <Arg>
<New class="org.eclipse.jetty.util.ClassMatcher"> <New class="org.eclipse.jetty.util.ClassMatcher">
<Arg> <Arg>

View File

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

View File

@ -67,10 +67,9 @@
<Item>org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration</Item> <Item>org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration</Item>
<Item>org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration</Item> <Item>org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.ee10.webapp.JmxConfiguration</Item> <Item>org.eclipse.jetty.ee10.webapp.JmxConfiguration</Item>
<Item>config.org.eclipse.jetty.ee10.websocket.server.JettyWebSocketConfiguration</Item> <Item>org.eclipse.jetty.ee10.websocket.server.config.JettyWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketConfiguration</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.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiWebInfConfiguration</Item>
<Item>org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration</Item> <Item>org.eclipse.jetty.ee10.osgi.boot.OSGiMetaInfConfiguration</Item>
</Array> </Array>
</Arg> </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(systemProperty("org.ops4j.pax.url.mvn.settings").value(System.getProperty("settingsFilePath")));
} }
res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject().noStart()); //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
* Jetty 11 uses slf4j 2.0.0 by default, however we want to test with slf4j 1.7.30 for backwards compatibility. //note 2: if you do set the above system property, more than likely the test will not finish, no idea why
* To do that, we need to use slf4j-simple as the logging implementation. We make a simplelogger.properties res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-slf4j-impl").versionAsInProject().start());
* file available so that jetty logging can be configured res.add(mavenBundle().groupId("org.slf4j").artifactId("slf4j-api").versionAsInProject());
*/
// 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
TinyBundle loggingPropertiesBundle = TinyBundles.bundle(); TinyBundle loggingPropertiesBundle = TinyBundles.bundle();
loggingPropertiesBundle.add("jetty-logging.properties", ClassLoader.getSystemResource("jetty-logging.properties")); loggingPropertiesBundle.add("jetty-logging.properties", ClassLoader.getSystemResource("jetty-logging.properties"));
loggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "jetty-logging-properties"); loggingPropertiesBundle.set(Constants.BUNDLE_SYMBOLICNAME, "jetty-logging-properties");
loggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "org.eclipse.jetty.logging"); loggingPropertiesBundle.set(Constants.FRAGMENT_HOST, "org.eclipse.jetty.logging");
loggingPropertiesBundle.add(FragmentActivator.class); loggingPropertiesBundle.add(FragmentActivator.class);
res.add(CoreOptions.streamBundle(loggingPropertiesBundle.build()).noStart()); 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.el").artifactId("jakarta.el-api").versionAsInProject().start());
res.add(mavenBundle().groupId("jakarta.servlet").artifactId("jakarta.servlet-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.eclipse.platform").artifactId("org.eclipse.osgi.util").versionAsInProject());
res.add(mavenBundle().groupId("org.osgi").artifactId("org.osgi.service.cm").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.Collections;
import java.util.List; import java.util.List;
import org.eclipse.jetty.util.ClassMatcher;
public class AbstractConfiguration implements Configuration public class AbstractConfiguration implements Configuration
{ {
private final boolean _enabledByDefault; private final boolean _enabledByDefault;
private final List<String> _after; private final List<String> _after;
private final List<String> _before; private final List<String> _before;
private final ClassMatcher _system; private final ClassMatcher _protected;
private final ClassMatcher _server; private final ClassMatcher _hidden;
public static class Builder public static class Builder
{ {
@ -88,7 +90,7 @@ public class AbstractConfiguration implements Configuration
/** /**
* Protect classes from modification by the web application by adding them * 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 * @param classes classname or package pattern
*/ */
@ -100,7 +102,7 @@ public class AbstractConfiguration implements Configuration
/** /**
* Hide classes from the web application by adding them * 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 * @param classes classname or package pattern
*/ */
@ -112,7 +114,7 @@ public class AbstractConfiguration implements Configuration
/** /**
* Expose classes to the web application by adding them * 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 * @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 * 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 * 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 * @param classes classname or package pattern
*/ */
@ -154,8 +156,8 @@ public class AbstractConfiguration implements Configuration
_enabledByDefault = builder._enabledByDefault; _enabledByDefault = builder._enabledByDefault;
_after = List.copyOf(builder._after); _after = List.copyOf(builder._after);
_before = List.copyOf(builder._before); _before = List.copyOf(builder._before);
_system = new ClassMatcher(builder._system).asImmutable(); _protected = new ClassMatcher(builder._system).asImmutable();
_server = new ClassMatcher(builder._server).asImmutable(); _hidden = new ClassMatcher(builder._server).asImmutable();
} }
@Override @Override
@ -171,15 +173,15 @@ public class AbstractConfiguration implements Configuration
} }
@Override @Override
public ClassMatcher getSystemClasses() public ClassMatcher getProtectedClasses()
{ {
return _system; return _protected;
} }
@Override @Override
public ClassMatcher getServerClasses() public ClassMatcher getHiddenClasses()
{ {
return _server; return _hidden;
} }
@Override @Override

View File

@ -21,7 +21,6 @@ import org.eclipse.jetty.util.IncludeExcludeSet;
/** /**
* @deprecated Use org.eclipse.jetty.util.ClassMatcher * @deprecated Use org.eclipse.jetty.util.ClassMatcher
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @Deprecated(since = "12.0.8", forRemoval = true)
public class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher 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.Collections;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.TopologicalSort; 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 * (eg {@link JndiConfiguration}, {@link JaasConfiguration}} etc.) can be added or removed without concern
* for ordering. * for ordering.
* </p> * </p>
* <p>Also since Jetty-9.4, Configurations are responsible for providing {@link #getServerClasses()} and * <p>Also since Jetty-9.4, Configurations are responsible for providing {@link #getHiddenClasses()} and
* {@link #getSystemClasses()} to configure the {@link WebAppClassLoader} for each context. * {@link #getProtectedClasses()} to configure the {@link WebAppClassLoader} for each context.
* </p> * </p>
*/ */
public interface Configuration 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. * @return ClassMatcher of system classes.
*/ */
default ClassMatcher getSystemClasses() default ClassMatcher getProtectedClasses()
{ {
return new ClassMatcher(); 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. * @return ClassMatcher of server classes.
*/ */
default ClassMatcher getServerClasses() default ClassMatcher getHiddenClasses()
{ {
return new ClassMatcher(); 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. * Set up for configuration.
* <p> * <p>

View File

@ -111,9 +111,33 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
List<Resource> getExtraClasspath(); 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))) if (webappUrl != null && (!checkSystemResource || !_context.isProtectedResource(name, webappUrl)))
{ {
webappClass = this.foundClass(name, webappUrl); webappClass = this.foundClass(name, webappUrl);
resolveClass(webappClass); resolveClass(webappClass);
if (LOG.isDebugEnabled()) 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 // Add the known server class inclusions for all known configurations
for (Configuration configuration : Configurations.getKnown()) for (Configuration configuration : Configurations.getKnown())
{ {
_hiddenClasses.include(configuration.getServerClasses().getInclusions()); _hiddenClasses.include(configuration.getHiddenClasses().getInclusions());
} }
// Setup Configuration classes for this webapp! // Setup Configuration classes for this webapp!
@ -436,8 +436,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
_configurations.sort(); _configurations.sort();
for (Configuration configuration : _configurations) for (Configuration configuration : _configurations)
{ {
_protectedClasses.add(configuration.getSystemClasses().getPatterns()); _protectedClasses.add(configuration.getProtectedClasses().getPatterns());
_hiddenClasses.exclude(configuration.getServerClasses().getExclusions()); _hiddenClasses.exclude(configuration.getHiddenClasses().getExclusions());
} }
// Configure classloader // 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> * <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 * 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. * 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> * <p>
* These classes/packages are provided by the JVM and * These classes/packages are provided by the JVM and
* cannot be replaced by classes of the same name from WEB-INF, * 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() public ClassMatcher getHiddenClassMatcher()
{ {
@ -696,7 +696,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _protectedClasses.getPatterns(); 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() public String[] getHiddenClasses()
{ {
return _hiddenClasses.getPatterns(); return _hiddenClasses.getPatterns();
@ -730,56 +730,54 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
* @deprecated use {@link #setHiddenClassMatcher(ClassMatcher)} * @deprecated use {@link #setHiddenClassMatcher(ClassMatcher)}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @Deprecated(since = "12.0.8", forRemoval = true)
public void setServerClassMatcher(ClassMatcher serverClasses) public void setServerClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher serverClasses)
{ {
_hiddenClasses.clear(); setHiddenClassMatcher(serverClasses);
_hiddenClasses.add(serverClasses.getPatterns());
} }
/** /**
* @deprecated use {@link #setProtectedClassMatcher(ClassMatcher)} * @deprecated use {@link #setProtectedClassMatcher(ClassMatcher)}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @Deprecated(since = "12.0.8", forRemoval = true)
public void setSystemClassMatcher(ClassMatcher systemClasses) public void setSystemClassMatcher(org.eclipse.jetty.ee10.webapp.ClassMatcher systemClasses)
{ {
_protectedClasses.clear(); setProtectedClassMatcher(systemClasses);
_protectedClasses.add(systemClasses.getPatterns());
} }
/** /**
* @deprecated use {@link #addHiddenClassMatcher(ClassMatcher)} * @deprecated use {@link #addHiddenClassMatcher(ClassMatcher)}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @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 use {@link #addProtectedClassMatcher(ClassMatcher)}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @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 use {@link #getProtectedClassMatcher()}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @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 use {@link #getHiddenClassMatcher()}
*/ */
@Deprecated(since = "12.0.8", forRemoval = true) @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) @Deprecated(since = "12.0.8", forRemoval = true)
public String[] getSystemClasses() 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) @Deprecated(since = "12.0.8", forRemoval = true)
public String[] getServerClasses() 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) @Deprecated(since = "12.0.8", forRemoval = true)
public boolean isServerClass(Class<?> clazz) 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) @Deprecated(since = "12.0.8", forRemoval = true)
public boolean isSystemClass(Class<?> clazz) public boolean isSystemClass(Class<?> clazz)
{ {
return _protectedClasses.match(clazz); return isProtectedClass(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);
} }
@Override @Override

View File

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

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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. --> <!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee8.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee8.webapp.WebAppContext">
<Call name="addServerClassMatcher"> <Call name="addServerClassMatcher">
<Arg> <Arg>
<New class="org.eclipse.jetty.util.ClassMatcher"> <New class="org.eclipse.jetty.ee8.webapp.ClassMatcher">
<Arg> <Arg>
<Array type="java.lang.String"> <Array type="java.lang.String">
<Item>-org.eclipse.jetty.util.Decorator</Item> <Item>-org.eclipse.jetty.util.Decorator</Item>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?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. --> <!-- Weld needs access to some internal classes. Same configuration as "cdi2" module provides on server. -->
<Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee9.webapp.WebAppContext">
<Call name="addServerClassMatcher"> <Call name="addServerClassMatcher">
<Arg> <Arg>
<New class="org.eclipse.jetty.util.ClassMatcher"> <New class="org.eclipse.jetty.ee9.webapp.ClassMatcher">
<Arg> <Arg>
<Array type="java.lang.String"> <Array type="java.lang.String">
<Item>-org.eclipse.jetty.util.Decorator</Item> <Item>-org.eclipse.jetty.util.Decorator</Item>

View File

@ -21,17 +21,19 @@ import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncContext;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HostPortHttpField; import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.QuotedCSV; import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.util.IncludeExcludeSet; import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.InetAddressSet; import org.eclipse.jetty.util.InetAddressSet;
@ -72,7 +74,7 @@ public class ThreadLimitHandler extends HandlerWrapper
private final boolean _rfc7239; private final boolean _rfc7239;
private final String _forwardedHeader; private final String _forwardedHeader;
private final IncludeExcludeSet<String, InetAddress> _includeExcludeSet = new IncludeExcludeSet<>(InetAddressSet.class); 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 volatile boolean _enabled;
private int _threadLimit = 10; private int _threadLimit = 10;
@ -178,6 +180,17 @@ public class ThreadLimitHandler extends HandlerWrapper
} }
else 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? // Do we already have a future permit from a previous invocation?
Closeable permit = (Closeable)baseRequest.getAttribute(PERMIT); Closeable permit = (Closeable)baseRequest.getAttribute(PERMIT);
try try
@ -249,14 +262,18 @@ public class ThreadLimitHandler extends HandlerWrapper
if (limit <= 0) if (limit <= 0)
return null; return null;
remote = _remotes.get(ip); // Use a compute method to create or retain the Remote instance as it is necessary for
if (remote == null) // 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); if (v != null)
remote = _remotes.putIfAbsent(ip, r); {
if (remote == null) v._referenceCounter.retain();
remote = r; return v;
} }
return new Remote(k, limit);
});
baseRequest.setAttribute(REMOTE, remote); baseRequest.setAttribute(REMOTE, remote);
@ -325,6 +342,7 @@ public class ThreadLimitHandler extends HandlerWrapper
private final String _ip; private final String _ip;
private final int _limit; private final int _limit;
private final AutoLock _lock = new AutoLock(); private final AutoLock _lock = new AutoLock();
private final Retainable.ReferenceCounter _referenceCounter = new Retainable.ReferenceCounter();
private int _permits; private int _permits;
private Deque<CompletableFuture<Closeable>> _queue = new ArrayDeque<>(); private Deque<CompletableFuture<Closeable>> _queue = new ArrayDeque<>();
private final CompletableFuture<Closeable> _permitted = CompletableFuture.completedFuture(this); 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.PlusConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item> <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
<Item>org.eclipse.jetty.ee9.webapp.JmxConfiguration</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.websocket.jakarta.server.config.JakartaWebSocketConfiguration</Item>
<Item>org.eclipse.jetty.ee9.osgi.annotations.AnnotationConfiguration</Item> <Item>org.eclipse.jetty.ee9.osgi.annotations.AnnotationConfiguration</Item>
<Item>org.eclipse.jetty.ee9.osgi.boot.OSGiWebInfConfiguration</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; 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 class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher
{ {
public 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. * @return ClassMatcher of server classes.
*/ */

View File

@ -20,28 +20,24 @@ import java.lang.instrument.IllegalClassFormatException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.CodeSource; import java.security.CodeSource;
import java.security.PermissionCollection; import java.security.PermissionCollection;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
import org.eclipse.jetty.util.ClassVisibilityChecker; import org.eclipse.jetty.util.ClassVisibilityChecker;
import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.FileID;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil; 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.Resource;
import org.eclipse.jetty.util.resource.ResourceCollators; import org.eclipse.jetty.util.resource.ResourceCollators;
import org.eclipse.jetty.util.resource.ResourceFactory; 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))) if (webappUrl != null && (!checkSystemResource || !_context.isSystemResource(name, webappUrl)))
{ {
webappClass = this.foundClass(name, webappUrl); webappClass = this.foundClass(name, webappUrl);
resolveClass(webappClass); resolveClass(webappClass);
if (LOG.isDebugEnabled()) 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.Handler;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.ExceptionUtil;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
@ -673,7 +672,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
/** /**
* Set the server classes patterns. * Set the server classes patterns.
* <p> * <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 * 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. * 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. * Set the system classes patterns.
* <p> * <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, * cannot be replaced by classes of the same name from WEB-INF,
* regardless of the value of {@link #setParentLoaderPriority(boolean)}. * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
* *
@ -753,11 +752,23 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
@Override @Override
public boolean isHiddenClass(Class<?> clazz) public boolean isHiddenClass(Class<?> clazz)
{ {
return _serverClasses.match(clazz); return isServerClass(clazz);
} }
@Override @Override
public boolean isProtectedClass(Class<?> clazz) 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); return _systemClasses.match(clazz);
} }

View File

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