Move ClassMatcher to core to have a consistent fix for addServerClasses in all environments (#11566)

* Issue #11514 - Cleanup `jetty.webapp.addServerClasses` property behavior for ee10/ee9/ee8

* Fix test

* Merging patterns (default -> env -> config)

* Moved ClassMatcher to util

* Adding more deprecations

* Changing XML demos/tests to use new getter names

* rollback xml changes in ee9/ee8

---------

Co-authored-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
Co-authored-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Greg Wilkins 2024-04-12 09:31:47 +02:00 committed by GitHub
parent 83526f77d3
commit 30bee710f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 1987 additions and 2149 deletions

View File

@ -128,7 +128,7 @@ Appends the value to the existing value.
This is useful to append a value to properties that accept a comma separated list of values, for example: This is useful to append a value to properties that accept a comma separated list of values, for example:
+ +
---- ----
jetty.webapp.addServerClasses+=,com.acme jetty.webapp.addProtectedClasses+=,com.acme
---- ----
+ +
// TODO: check what happens if the property is empty and +=,value is done: is the comma stripped? If so add a sentence about this. // TODO: check what happens if the property is empty and +=,value is done: is the comma stripped? If so add a sentence about this.

View File

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

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addProtectedClasses">
<Arg><Ref refid="Server"/></Arg>
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.server.addProtectedClasses"/></Arg>
</Call>
</Arg>
</Call>
<Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addHiddenClasses">
<Arg><Ref refid="Server"/></Arg>
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.server.addHiddenClasses"/></Arg>
</Call>
</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,31 @@
# DO NOT EDIT THIS FILE - See: https://eclipse.dev/jetty/documentation/
[description]
# tag::description[]
This module provide common configuration of Java Servlet web applications over all environments.
# end::description[]
[xml]
etc/jetty-ee-webapp.xml
[lib]
lib/jetty-ee-${jetty.version}.jar
[ini-template]
# tag::ini-template[]
## Add to the server wide default jars and packages protected or hidden from webapps.
## Protected (aka System) classes cannot be overridden by a webapp.
## Hidden (aka Server) classes cannot be seen by a webapp
## Lists of patterns are comma separated and may be either:
## + a qualified classname e.g. 'com.acme.Foo'
## + a package name e.g. 'net.example.'
## + a jar file e.g. '${jetty.base.uri}/lib/dependency.jar'
## + a directory of jars,resource or classes e.g. '${jetty.base.uri}/resources'
## + A pattern preceded with a '-' is an exclusion, all other patterns are inclusions
##
## The +=, operator appends to a CSV list with a comma as needed.
##
#jetty.server.addProtectedClasses+=,org.example.
#jetty.server.addHiddenClasses+=,org.example.
# end::ini-template[]

View File

@ -0,0 +1,22 @@
//
// ========================================================================
// 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
// ========================================================================
//
module org.eclipse.jetty.ee
{
requires org.slf4j;
requires transitive org.eclipse.jetty.util;
requires transitive org.eclipse.jetty.server;
exports org.eclipse.jetty.ee;
}

View File

@ -0,0 +1,214 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.component.Environment;
/**
* Common attributes and methods for configuring the {@link ClassLoader Class loading} of web application:
* <ul>
* <li>Protected (a.k.a. System) classes are classes typically provided by the JVM, that cannot be replaced by the
* web application, and they are always loaded via the environment or system classloader. They are visible but
* protected.</li>
* <li>Hidden (a.k.a. Server) classes are those used to implement the Server and are not made available to the
* web application. They are hidden from the web application {@link ClassLoader}.</li>
* </ul>
* <p>These protections are set to reasonable defaults {@link #DEFAULT_PROTECTED_CLASSES} and {@link #DEFAULT_HIDDEN_CLASSES},
* which may be programmatically configured and will affect the defaults applied to all web applications in the same JVM.
*
* <p>
* The defaults applied by a specific {@link Server} can be configured using {@link #addProtectedClasses(Server, String...)} and
* {@link #addHiddenClasses(Server, String...)}. Alternately the {@link Server} attributes {@link #PROTECTED_CLASSES_ATTRIBUTE}
* and {@link #HIDDEN_CLASSES_ATTRIBUTE} may be used to direct set a {@link ClassMatcher} to use for all web applications
* within the server instance.
* </p>
* <p>
* The defaults applied by a specific {@link Environment} can be configured using {@link #addProtectedClasses(Environment, String...)} and
* {@link #addHiddenClasses(Environment, String...)}. Alternately the {@link Environment} attributes {@link #PROTECTED_CLASSES_ATTRIBUTE}
* and {@link #HIDDEN_CLASSES_ATTRIBUTE} may be used to direct set a {@link ClassMatcher} to use for all web applications
* within the server instance.
* </p>
* <p>
* Ultimately, the configurations set by this class only affects the defaults applied to each web application
* {@link org.eclipse.jetty.server.handler.ContextHandler Context} and the {@link ClassMatcher} fields of the web applications
* can be directly access to configure a specific context.
* </p>
*/
public class WebAppClassLoading
{
public static final String PROTECTED_CLASSES_ATTRIBUTE = "org.eclipse.jetty.webapp.systemClasses";
public static final String HIDDEN_CLASSES_ATTRIBUTE = "org.eclipse.jetty.webapp.serverClasses";
/**
* The default protected (system) classes used by a web application, which will be applied to the {@link ClassMatcher}s created
* by {@link #getProtectedClasses(Environment)}.
*/
public static final ClassMatcher DEFAULT_PROTECTED_CLASSES = new ClassMatcher(
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"jakarta.", // Jakarta classes (per servlet spec v5.0 / Section 15.2.1)
"org.xml.", // javax.xml
"org.w3c." // javax.xml
);
/**
* The default hidden (server) classes used by a web application, which can be applied to the {@link ClassMatcher}s created
* by {@link #getHiddenClasses(Environment)}.
*/
public static final ClassMatcher DEFAULT_HIDDEN_CLASSES = new ClassMatcher(
"org.eclipse.jetty." // hide jetty classes
);
/**
* Get the default protected (system) classes for a {@link Server}
* @param server The {@link Server} for the defaults
* @return The default protected (system) classes for the {@link Server}, which will be empty if not previously configured.
*/
public static ClassMatcher getProtectedClasses(Server server)
{
return getClassMatcher(server, PROTECTED_CLASSES_ATTRIBUTE, null);
}
/**
* Get the default protected (system) classes for an {@link Environment}
* @param environment The {@link Server} for the defaults
* @return The default protected (system) classes for the {@link Environment}, which will be the {@link #DEFAULT_PROTECTED_CLASSES} if not previously configured.
*/
public static ClassMatcher getProtectedClasses(Environment environment)
{
return getClassMatcher(environment, PROTECTED_CLASSES_ATTRIBUTE, DEFAULT_PROTECTED_CLASSES);
}
/**
* Add a protected (system) Class pattern to use for all WebAppContexts.
* @param patterns the patterns to use
*/
public static void addProtectedClasses(String... patterns)
{
DEFAULT_PROTECTED_CLASSES.add(patterns);
}
/**
* Add a protected (system) 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
*/
public static void addProtectedClasses(Attributes attributes, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(attributes, PROTECTED_CLASSES_ATTRIBUTE, null).add(patterns);
}
/**
* Add a protected (system) Class pattern to use for all WebAppContexts of a given {@link Server}.
* @param server The {@link Server} instance to add classes to
* @param patterns the patterns to use
*/
public static void addProtectedClasses(Server server, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(server, PROTECTED_CLASSES_ATTRIBUTE, null).add(patterns);
}
/**
* Add a protected (system) Class pattern to use for WebAppContexts of a given environment.
* @param environment The {@link Environment} instance to add classes to
* @param patterns the patterns to use
*/
public static void addProtectedClasses(Environment environment, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(environment, PROTECTED_CLASSES_ATTRIBUTE, DEFAULT_PROTECTED_CLASSES).add(patterns);
}
/**
* Get the default hidden (server) classes for a {@link Server}
* @param server The {@link Server} for the defaults
* @return The default hidden (server) classes for the {@link Server}, which will be empty if not previously configured.
*
*/
public static ClassMatcher getHiddenClasses(Server server)
{
return getClassMatcher(server, HIDDEN_CLASSES_ATTRIBUTE, null);
}
/**
* Get the default hidden (server) classes for an {@link Environment}
* @param environment The {@link Server} for the defaults
* @return The default hidden (server) classes for the {@link Environment}, which will be {@link #DEFAULT_PROTECTED_CLASSES} if not previously configured.
*/
public static ClassMatcher getHiddenClasses(Environment environment)
{
return getClassMatcher(environment, HIDDEN_CLASSES_ATTRIBUTE, DEFAULT_HIDDEN_CLASSES);
}
/**
* Add a hidden (server) Class pattern to use for all WebAppContexts of a given {@link Server}.
* @param patterns the patterns to use
*/
public static void addHiddenClasses(String... patterns)
{
DEFAULT_HIDDEN_CLASSES.add(patterns);
}
/**
* 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 (forRemoval = true)
public static void addHiddenClasses(Attributes attributes, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(attributes, HIDDEN_CLASSES_ATTRIBUTE, null).add(patterns);
}
/**
* Add a hidden (server) Class pattern to use for all WebAppContexts of a given {@link Server}.
* @param server The {@link Server} instance to add classes to
* @param patterns the patterns to use
*/
public static void addHiddenClasses(Server server, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(server, HIDDEN_CLASSES_ATTRIBUTE, null).add(patterns);
}
/**
* Add a hidden (server) Class pattern to use for all ee9 WebAppContexts.
* @param environment The {@link Environment} instance to add classes to
* @param patterns the patterns to use
*/
public static void addHiddenClasses(Environment environment, String... patterns)
{
if (patterns != null && patterns.length > 0)
getClassMatcher(environment, HIDDEN_CLASSES_ATTRIBUTE, DEFAULT_HIDDEN_CLASSES).add(patterns);
}
private static ClassMatcher getClassMatcher(Attributes attributes, String attribute, ClassMatcher defaultPatterns)
{
Object existing = attributes.getAttribute(attribute);
if (existing instanceof ClassMatcher cm)
return cm;
ClassMatcher classMatcher = (existing instanceof String[] stringArray)
? new ClassMatcher(stringArray) : new ClassMatcher(defaultPatterns);
attributes.setAttribute(attribute, classMatcher);
return classMatcher;
}
}

View File

@ -0,0 +1,222 @@
//
// ========================================================================
// 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 orge.eclipse.jetty.ee;
import java.util.Arrays;
import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ClassMatcher;
import org.eclipse.jetty.util.component.Environment;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItemInArray;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance;
public class WebAppClassLoadingTest
{
@BeforeEach
public void beforeEach()
{
Environment.ensure("Test");
}
@AfterEach
public void afterEach()
{
Environment.ensure("Test").clearAttributes();
}
@Test
public void testServerDefaults()
{
Server server = new Server();
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(server);
assertThat(protect.size(), is(0));
assertThat(server.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(server);
assertThat(hide.size(), is(0));
assertThat(server.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testServerAttributeDefaults()
{
Server server = new Server();
ClassMatcher protect = new ClassMatcher("org.protect.");
server.setAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE, protect);
ClassMatcher hide = new ClassMatcher("org.hide.");
server.setAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE, hide);
assertThat(WebAppClassLoading.getProtectedClasses(server), sameInstance(protect));
assertThat(WebAppClassLoading.getHiddenClasses(server), sameInstance(hide));
}
@Test
public void testServerStringAttributeDefaults()
{
Server server = new Server();
server.setAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE, new String[] {"org.protect."});
server.setAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE, new String[] {"org.hide."});
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(server);
assertThat(protect.size(), is(1));
assertThat(Arrays.asList(protect.getPatterns()), contains("org.protect."));
assertThat(server.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(server);
assertThat(hide.size(), is(1));
assertThat(Arrays.asList(hide.getPatterns()), contains("org.hide."));
assertThat(server.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testServerProgrammaticDefaults()
{
Server server = new Server();
WebAppClassLoading.addProtectedClasses(server, "org.protect.");
WebAppClassLoading.addHiddenClasses(server, "org.hide.");
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(server);
assertThat(protect.size(), is(1));
assertThat(Arrays.asList(protect.getPatterns()), contains("org.protect."));
assertThat(server.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(server);
assertThat(hide.size(), is(1));
assertThat(Arrays.asList(hide.getPatterns()), contains("org.hide."));
assertThat(server.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testServerAddPatterns()
{
Server server = new Server();
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(server);
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(server);
assertThat(protect.size(), is(0));
assertThat(hide.size(), is(0));
WebAppClassLoading.addProtectedClasses(server, "org.protect.", "com.protect.");
WebAppClassLoading.addHiddenClasses(server, "org.hide.", "com.hide.");
assertThat(protect.size(), is(2));
assertThat(Arrays.asList(protect.getPatterns()), containsInAnyOrder("org.protect.", "com.protect."));
assertThat(server.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
assertThat(hide.size(), is(2));
assertThat(Arrays.asList(hide.getPatterns()), containsInAnyOrder("org.hide.", "com.hide."));
assertThat(server.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testEnvironmentDefaults()
{
Environment environment = Environment.get("Test");
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(environment);
assertThat(protect, equalTo(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES));
assertThat(environment.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(environment);
assertThat(hide, equalTo(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES));
assertThat(environment.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testEnvironmentAttributeDefaults()
{
Environment environment = Environment.get("Test");
ClassMatcher protect = new ClassMatcher("org.protect.");
environment.setAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE, protect);
ClassMatcher hide = new ClassMatcher("org.hide.");
environment.setAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE, hide);
assertThat(WebAppClassLoading.getProtectedClasses(environment), sameInstance(protect));
assertThat(WebAppClassLoading.getHiddenClasses(environment), sameInstance(hide));
}
@Test
public void testEnvironmentStringAttributeDefaults()
{
Environment environment = Environment.get("Test");
environment.setAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE, new String[] {"org.protect."});
environment.setAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE, new String[] {"org.hide."});
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(environment);
assertThat(protect.size(), is(1));
assertThat(Arrays.asList(protect.getPatterns()), contains("org.protect."));
assertThat(environment.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(environment);
assertThat(hide.size(), is(1));
assertThat(Arrays.asList(hide.getPatterns()), contains("org.hide."));
assertThat(environment.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testEnvironmentProgrammaticDefaults()
{
Environment environment = Environment.get("Test");
WebAppClassLoading.addProtectedClasses(environment, "org.protect.");
WebAppClassLoading.addHiddenClasses(environment, "org.hide.");
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(environment);
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(environment);
assertThat(protect.size(), is(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES.size() + 1));
assertThat(protect.getPatterns(), hasItemInArray("org.protect."));
for (String pattern : WebAppClassLoading.DEFAULT_PROTECTED_CLASSES)
assertThat(protect.getPatterns(), hasItemInArray(pattern));
assertThat(environment.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
assertThat(hide.size(), is(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES.size() + 1));
assertThat(hide.getPatterns(), hasItemInArray("org.hide."));
for (String pattern : WebAppClassLoading.DEFAULT_HIDDEN_CLASSES)
assertThat(hide.getPatterns(), hasItemInArray(pattern));
assertThat(environment.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
@Test
public void testEnvironmentAddPatterns()
{
Environment environment = Environment.get("Test");
ClassMatcher protect = WebAppClassLoading.getProtectedClasses(environment);
ClassMatcher hide = WebAppClassLoading.getHiddenClasses(environment);
assertThat(protect, equalTo(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES));
assertThat(hide, equalTo(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES));
WebAppClassLoading.addProtectedClasses(environment, "org.protect.", "com.protect.");
WebAppClassLoading.addHiddenClasses(environment, "org.hide.", "com.hide.");
assertThat(protect.size(), is(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES.size() + 2));
assertThat(protect.getPatterns(), hasItemInArray("org.protect."));
assertThat(protect.getPatterns(), hasItemInArray("com.protect."));
for (String pattern : WebAppClassLoading.DEFAULT_PROTECTED_CLASSES)
assertThat(protect.getPatterns(), hasItemInArray(pattern));
assertThat(environment.getAttribute(WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE), sameInstance(protect));
assertThat(hide.size(), is(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES.size() + 2));
assertThat(hide.getPatterns(), hasItemInArray("org.hide."));
assertThat(hide.getPatterns(), hasItemInArray("com.hide."));
for (String pattern : WebAppClassLoading.DEFAULT_HIDDEN_CLASSES)
assertThat(hide.getPatterns(), hasItemInArray(pattern));
assertThat(environment.getAttribute(WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE), sameInstance(hide));
}
}

View File

@ -25,7 +25,7 @@ etc/jetty-test-keystore.xml
[ini] [ini]
bouncycastle.version?=@bouncycastle.version@ bouncycastle.version?=@bouncycastle.version@
jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/bouncycastle/ jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/bouncycastle/
jetty.sslContext.keyStorePath?=etc/test-keystore.p12 jetty.sslContext.keyStorePath?=etc/test-keystore.p12
jetty.sslContext.keyStoreType?=PKCS12 jetty.sslContext.keyStoreType?=PKCS12
jetty.sslContext.keyStorePassword?=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 jetty.sslContext.keyStorePassword?=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4

View File

@ -89,7 +89,7 @@ public class SessionData implements Serializable
//Clazz not loaded by context classloader, but ask if loadable by context classloader, //Clazz not loaded by context classloader, but ask if loadable by context classloader,
//because preferable to use context classloader if possible (eg for deep structures). //because preferable to use context classloader if possible (eg for deep structures).
ClassVisibilityChecker checker = (ClassVisibilityChecker)(contextLoader); ClassVisibilityChecker checker = (ClassVisibilityChecker)(contextLoader);
isContextLoader = (checker.isSystemClass(clazz) && !(checker.isServerClass(clazz))); isContextLoader = (checker.isProtectedClass(clazz) && !(checker.isHiddenClass(clazz)));
} }
else else
{ {

View File

@ -0,0 +1,810 @@
//
// ========================================================================
// 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.util;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* A matcher for classes based on package and/or location and/or module/
* <p>
* Performs pattern matching of a class against a set of pattern entries.
* A class pattern is a string of one of the forms:<ul>
* <li>'org.package.SomeClass' will match a specific class
* <li>'org.package.' will match a specific package hierarchy
* <li>'org.package.SomeClass$NestedClass ' will match a nested class exactly otherwise.
* Nested classes are matched by their containing class. (eg. org.example.MyClass
* matches org.example.MyClass$AnyNestedClass)
* <li>'file:///some/location/' - A file system directory from which
* the class was loaded
* <li>'file:///some/location.jar' - The URI of a jar file from which
* the class was loaded
* <li>'jrt:/modulename' - A Java9 module name</li>
* <li>Any of the above patterns preceded by '-' will exclude rather than include the match.
* </ul>
* When class is initialized from a classpath pattern string, entries
* in this string should be separated by ':' (semicolon) or ',' (comma).
*/
public class ClassMatcher extends AbstractSet<String>
{
public static class Entry
{
private final String _pattern;
private final String _name;
private final boolean _inclusive;
protected Entry(String name, boolean inclusive)
{
_name = name;
_inclusive = inclusive;
_pattern = inclusive ? _name : ("-" + _name);
}
public String getPattern()
{
return _pattern;
}
public String getName()
{
return _name;
}
@Override
public String toString()
{
return _pattern;
}
@Override
public int hashCode()
{
return _pattern.hashCode();
}
@Override
public boolean equals(Object o)
{
return (o instanceof Entry) && _pattern.equals(((Entry)o)._pattern);
}
public boolean isInclusive()
{
return _inclusive;
}
}
private static class PackageEntry extends Entry
{
protected PackageEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class ClassEntry extends Entry
{
protected ClassEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class LocationEntry extends Entry
{
private final Path _path;
protected LocationEntry(String name, boolean inclusive)
{
super(name, inclusive);
URI uri = URI.create(name);
if (!uri.isAbsolute() && !"file".equalsIgnoreCase(uri.getScheme()))
throw new IllegalArgumentException("Not a valid file URI: " + name);
_path = Paths.get(uri);
}
public Path getPath()
{
return _path;
}
}
private static class ModuleEntry extends Entry
{
private final String _module;
protected ModuleEntry(String name, boolean inclusive)
{
super(name, inclusive);
if (!getName().startsWith("jrt:"))
throw new IllegalArgumentException(name);
_module = getName().split("/")[1];
}
public String getModule()
{
return _module;
}
}
public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(String name)
{
return _entries.getBest(name) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean isEmpty()
{
return _entries.isEmpty();
}
@Override
public boolean add(Entry entry)
{
String name = entry.getName();
if (entry instanceof ClassEntry)
name += "$";
else if (!(entry instanceof PackageEntry))
throw new IllegalArgumentException(entry.toString());
else if (".".equals(name))
name = "";
if (_entries.get(name) != null)
return false;
return _entries.put(name, entry);
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
@Override
public void clear()
{
_entries.clear();
}
}
public static class ByClass extends HashSet<Entry> implements Predicate<String>
{
private final Map<String, Entry> _entries = new HashMap<>();
@Override
public boolean test(String name)
{
return _entries.containsKey(name);
}
@Override
public Iterator<Entry> iterator()
{
return _entries.values().iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ClassEntry))
throw new IllegalArgumentException(entry.toString());
return _entries.put(entry.getName(), entry) == null;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByPackageOrName extends AbstractSet<Entry> implements Predicate<String>
{
private final ByClass _byClass = new ByClass();
private final ByPackage _byPackage = new ByPackage();
@Override
public boolean test(String name)
{
return _byPackage.test(name) || _byClass.test(name);
}
@Override
public Iterator<Entry> iterator()
{
// by package contains all entries (classes are also $ packages).
return _byPackage.iterator();
}
@Override
public int size()
{
return _byPackage.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof PackageEntry)
return _byPackage.add(entry);
if (entry instanceof ClassEntry)
{
// Add class name to packages also as classes act
// as packages for nested classes.
boolean added = _byPackage.add(entry);
added = _byClass.add(entry) || added;
return added;
}
throw new IllegalArgumentException();
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof Entry))
return false;
boolean removedPackage = _byPackage.remove(o);
boolean removedClass = _byClass.remove(o);
return removedPackage || removedClass;
}
@Override
public void clear()
{
_byPackage.clear();
_byClass.clear();
}
}
public static class ByLocation extends HashSet<Entry> implements Predicate<URI>
{
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equals("file"))
return false;
Path path = Paths.get(uri);
for (Entry entry : this)
{
if (!(entry instanceof LocationEntry))
throw new IllegalStateException();
Path entryPath = ((LocationEntry)entry).getPath();
if (Files.isDirectory(entryPath))
{
if (path.startsWith(entryPath))
{
return true;
}
}
else
{
try
{
if (Files.isSameFile(path, entryPath))
{
return true;
}
}
catch (IOException ignore)
{
// this means there is a FileSystem issue preventing comparison.
// Use old technique
if (path.equals(entryPath))
{
return true;
}
}
}
}
return false;
}
}
public static class ByModule extends HashSet<Entry> implements Predicate<URI>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equalsIgnoreCase("jrt"))
return false;
String module = uri.getPath();
int end = module.indexOf('/', 1);
if (end < 1)
end = module.length();
return _entries.get(module, 1, end - 1) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ModuleEntry))
throw new IllegalArgumentException(entry.toString());
String module = ((ModuleEntry)entry).getModule();
if (_entries.get(module) != null)
return false;
_entries.put(module, entry);
return true;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByLocationOrModule extends AbstractSet<Entry> implements Predicate<URI>
{
private final ByLocation _byLocation = new ByLocation();
private final ByModule _byModule = new ByModule();
@Override
public boolean test(URI name)
{
if ((name == null) || (!name.isAbsolute()))
return false;
return _byLocation.test(name) || _byModule.test(name);
}
@Override
public Iterator<Entry> iterator()
{
Set<Entry> entries = new HashSet<>();
entries.addAll(_byLocation);
entries.addAll(_byModule);
return entries.iterator();
}
@Override
public int size()
{
return _byLocation.size() + _byModule.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof LocationEntry)
return _byLocation.add(entry);
if (entry instanceof ModuleEntry)
return _byModule.add(entry);
throw new IllegalArgumentException(entry.toString());
}
@Override
public boolean remove(Object o)
{
if (o instanceof LocationEntry)
return _byLocation.remove(o);
if (o instanceof ModuleEntry)
return _byModule.remove(o);
return false;
}
@Override
public void clear()
{
_byLocation.clear();
_byModule.clear();
}
}
protected final Map<String, Entry> _entries;
protected final IncludeExcludeSet<Entry, String> _patterns;
protected final IncludeExcludeSet<Entry, URI> _locations;
protected ClassMatcher(Map<String, Entry> entries, IncludeExcludeSet<Entry, String> patterns, IncludeExcludeSet<Entry, URI> locations)
{
_entries = entries;
_patterns = patterns == null ? new IncludeExcludeSet<>(ByPackageOrName.class) : patterns;
_locations = locations == null ? new IncludeExcludeSet<>(ByLocationOrModule.class) : locations;
}
private ClassMatcher(Map<String, Entry> entries)
{
this(entries, null, null);
}
public ClassMatcher()
{
this(new HashMap<>());
}
public ClassMatcher(ClassMatcher patterns)
{
this(new HashMap<>());
if (patterns != null)
setAll(patterns.getPatterns());
}
public ClassMatcher(String... patterns)
{
this(new HashMap<>());
if (patterns != null && patterns.length > 0)
setAll(patterns);
}
public ClassMatcher(String pattern)
{
this(new HashMap<>());
add(pattern);
}
public ClassMatcher asImmutable()
{
return new ClassMatcher(Map.copyOf(_entries),
_patterns.asImmutable(),
_locations.asImmutable());
}
public boolean include(String name)
{
if (name == null)
return false;
return add(newEntry(name, true));
}
public boolean include(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, true)) || added;
}
return added;
}
public boolean exclude(String name)
{
if (name == null)
return false;
return add(newEntry(name, false));
}
public boolean exclude(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, false)) || added;
}
return added;
}
@Override
public boolean add(String pattern)
{
if (pattern == null)
return false;
return add(newEntry(pattern));
}
public boolean add(String... pattern)
{
boolean added = false;
for (String p : pattern)
{
if (p != null)
added = add(newEntry(p)) || added;
}
return added;
}
protected boolean add(Entry entry)
{
if (_entries.containsKey(entry.getPattern()))
return false;
_entries.put(entry.getPattern(), entry);
if (entry instanceof LocationEntry || entry instanceof ModuleEntry)
{
if (entry.isInclusive())
_locations.include(entry);
else
_locations.exclude(entry);
}
else
{
if (entry.isInclusive())
_patterns.include(entry);
else
_patterns.exclude(entry);
}
return true;
}
protected Entry newEntry(String pattern)
{
if (pattern.startsWith("-"))
return newEntry(pattern.substring(1), false);
return newEntry(pattern, true);
}
protected Entry newEntry(String name, boolean inclusive)
{
if (name.startsWith("-"))
throw new IllegalStateException(name);
if (name.startsWith("file:"))
return new LocationEntry(name, inclusive);
if (name.startsWith("jrt:"))
return new ModuleEntry(name, inclusive);
if (name.endsWith("."))
return new PackageEntry(name, inclusive);
return new ClassEntry(name, inclusive);
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof String pattern))
return false;
Entry entry = _entries.remove(pattern);
if (entry == null)
return false;
List<Entry> saved = new ArrayList<>(_entries.values());
clear();
for (Entry e : saved)
{
add(e);
}
return true;
}
@Override
public void clear()
{
_entries.clear();
_patterns.clear();
_locations.clear();
}
@Override
public Iterator<String> iterator()
{
return _entries.keySet().iterator();
}
@Override
public int size()
{
return _entries.size();
}
/**
* Initialize the matcher by parsing each classpath pattern in an array
*
* @param classes array of classpath patterns
*/
private void setAll(String[] classes)
{
_entries.clear();
addAll(classes);
}
/**
* Add array of classpath patterns.
* @param classes array of classpath patterns
*/
private void addAll(String[] classes)
{
if (classes != null)
addAll(Arrays.asList(classes));
}
/**
* @return array of classpath patterns
*/
public String[] getPatterns()
{
return toArray(new String[0]);
}
/**
* @return array of inclusive classpath patterns
*/
public String[] getInclusions()
{
return _entries.values().stream().filter(Entry::isInclusive).map(Entry::getName).toArray(String[]::new);
}
/**
* @return array of excluded classpath patterns (without '-' prefix)
*/
public String[] getExclusions()
{
return _entries.values().stream().filter(e -> !e.isInclusive()).map(Entry::getName).toArray(String[]::new);
}
/**
* Match the class name against the pattern
*
* @param name name of the class to match
* @return true if class matches the pattern
*/
public boolean match(String name)
{
return _patterns.test(name);
}
/**
* Match the class name against the pattern
*
* @param clazz A class to try to match
* @return true if class matches the pattern
*/
public boolean match(Class<?> clazz)
{
try
{
return combine(_patterns, clazz.getName(), _locations, () -> TypeUtil.getLocationOfClass(clazz));
}
catch (Exception ignored)
{
}
return false;
}
public boolean match(String name, URL url)
{
if (url == null)
return false;
// Strip class suffix for name matching
if (name.endsWith(".class"))
name = name.substring(0, name.length() - 6);
// Treat path elements as packages for name matching
name = StringUtil.replace(name, '/', '.');
return combine(_patterns, name, _locations, () ->
{
try
{
return URIUtil.unwrapContainer(url.toURI());
}
catch (URISyntaxException ignored)
{
return null;
}
});
}
/**
* Match a class against inclusions and exclusions by name and location.
* Name based checks are performed before location checks. For a class to match,
* it must not be excluded by either name or location, and must either be explicitly
* included, or for there to be no inclusions. In the case where the location
* of the class is null, it will match if it is included by name, or
* if there are no location exclusions.
*
* @param names configured inclusions and exclusions by name
* @param name the name to check
* @param locations configured inclusions and exclusions by location
* @param location the location of the class (can be null)
* @return true if the class is not excluded but is included, or there are
* no inclusions. False otherwise.
*/
static boolean combine(IncludeExcludeSet<Entry, String> names, String name, IncludeExcludeSet<Entry, URI> locations, Supplier<URI> location)
{
// check the name set
Boolean byName = names.isIncludedAndNotExcluded(name);
// If we excluded by name, then no match
if (Boolean.FALSE == byName)
return false;
// check the location set
URI uri = location.get();
Boolean byLocation = uri == null ? null : locations.isIncludedAndNotExcluded(uri);
// If we excluded by location or couldn't check location exclusion, then no match
if (Boolean.FALSE == byLocation || (locations.hasExcludes() && uri == null))
return false;
// If there are includes, then we must be included to match.
if (names.hasIncludes() || locations.hasIncludes())
return byName == Boolean.TRUE || byLocation == Boolean.TRUE;
// Otherwise there are no includes and it was not excluded, so match
return true;
}
}

View File

@ -14,13 +14,33 @@
package org.eclipse.jetty.util; package org.eclipse.jetty.util;
/** /**
* ClassVisibilityChecker
*
* Interface to be implemented by classes capable of checking class visibility * Interface to be implemented by classes capable of checking class visibility
* for a context. * for a context.
*/ */
public interface ClassVisibilityChecker public interface ClassVisibilityChecker
{ {
/**
* Is the class a Protected (System) Class.
* A System class is a class that is visible to a webapplication,
* but that cannot be overridden by the contents of WEB-INF/lib or
* WEB-INF/classes
*
* @param clazz The fully qualified name of the class.
* @return True if the class is a system class.
*/
boolean isProtectedClass(Class<?> clazz);
/**
* 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,
* to be loaded from WEB-INF/lib or WEB-INF/classes
*
* @param clazz The fully qualified name of the class.
* @return True if the class is a server class.
*/
boolean isHiddenClass(Class<?> clazz);
/** /**
* Is the class a System Class. * Is the class a System Class.
@ -30,8 +50,13 @@ public interface ClassVisibilityChecker
* *
* @param clazz The fully qualified name of the class. * @param clazz The fully qualified name of the class.
* @return True if the class is a system class. * @return True if the class is a system class.
* @deprecated use {@link #isProtectedClass(Class)}
*/ */
boolean isSystemClass(Class<?> clazz); @Deprecated (forRemoval = true, since = "12.0.9")
default boolean isSystemClass(Class<?> clazz)
{
return isProtectedClass(clazz);
}
/** /**
* Is the class a Server Class. * Is the class a Server Class.
@ -42,6 +67,11 @@ public interface ClassVisibilityChecker
* *
* @param clazz The fully qualified name of the class. * @param clazz The fully qualified name of the class.
* @return True if the class is a server class. * @return True if the class is a server class.
* @deprecated use {@link #isHiddenClass(Class)}
*/ */
boolean isServerClass(Class<?> clazz); @Deprecated (forRemoval = true, since = "12.0.9")
default boolean isServerClass(Class<?> clazz)
{
return isHiddenClass(clazz);
}
} }

View File

@ -11,17 +11,15 @@
// ======================================================================== // ========================================================================
// //
package org.eclipse.jetty.ee10.webapp; package org.eclipse.jetty.util;
import java.net.URI; import java.net.URI;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.eclipse.jetty.ee10.webapp.ClassMatcher.ByLocationOrModule; import org.eclipse.jetty.util.ClassMatcher.ByLocationOrModule;
import org.eclipse.jetty.ee10.webapp.ClassMatcher.ByPackageOrName; import org.eclipse.jetty.util.ClassMatcher.ByPackageOrName;
import org.eclipse.jetty.ee10.webapp.ClassMatcher.Entry; import org.eclipse.jetty.util.ClassMatcher.Entry;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.TypeUtil;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;

View File

@ -17,6 +17,7 @@
<module>jetty-client</module> <module>jetty-client</module>
<module>jetty-demos</module> <module>jetty-demos</module>
<module>jetty-deploy</module> <module>jetty-deploy</module>
<module>jetty-ee</module>
<module>jetty-fcgi</module> <module>jetty-fcgi</module>
<module>jetty-http</module> <module>jetty-http</module>
<module>jetty-http-spi</module> <module>jetty-http-spi</module>

View File

@ -158,7 +158,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
//try environment scope next //try environment scope next
if (!bound) if (!bound)
bound = NamingEntryUtil.bindToENC(ServletContextHandler.__environment.getName(), name, mappedName); bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName);
//try Server scope next //try Server scope next
if (!bound) if (!bound)
@ -313,7 +313,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
//try the environment's scope //try the environment's scope
if (!bound) if (!bound)
bound = NamingEntryUtil.bindToENC(ServletContextHandler.__environment.getName(), name, mappedName); bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName);
//try the server's scope //try the server's scope
if (!bound) if (!bound)

View File

@ -9,7 +9,7 @@ org.eclipse.jetty.ee10.servlet.WebApplicationContext object
--> -->
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Get name="ServerClassMatcher"> <Get name="HiddenClassMatcher">
<Call name="exclude"> <Call name="exclude">
<Arg> <Arg>
<Array type="String"> <Array type="String">

View File

@ -54,7 +54,7 @@ public class ProxyWebAppTest
// This is a pieced together WebApp. // This is a pieced together WebApp.
// We don't have a valid WEB-INF/lib to rely on at this point. // We don't have a valid WEB-INF/lib to rely on at this point.
// So, open up server classes here, for purposes of this testcase. // So, open up server classes here, for purposes of this testcase.
webapp.getServerClassMatcher().add("-org.eclipse.jetty.ee10.proxy."); webapp.getHiddenClassMatcher().add("-org.eclipse.jetty.ee10.proxy.");
webapp.setWar(MavenTestingUtils.getProjectDirPath("src/main/webapp").toString()); webapp.setWar(MavenTestingUtils.getProjectDirPath("src/main/webapp").toString());
webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("test-classes").toString()); webapp.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("test-classes").toString());
server.setHandler(webapp); server.setHandler(webapp);

View File

@ -6,7 +6,7 @@
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Call name="addServerClassMatcher"> <Call name="addServerClassMatcher">
<Arg> <Arg>
<New class="org.eclipse.jetty.ee10.webapp.ClassMatcher"> <New class="org.eclipse.jetty.util.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

@ -234,6 +234,7 @@ public class TestOSGiUtil
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-jndi").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-jndi").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-osgi").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-osgi").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-ee").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-servlet").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-servlet").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-webapp").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-webapp").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-servlets").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-servlets").versionAsInProject().start());

View File

@ -13,7 +13,6 @@
package org.eclipse.jetty.ee10.plus.webapp; package org.eclipse.jetty.ee10.plus.webapp;
import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.InitialContext; import javax.naming.InitialContext;
@ -208,8 +207,8 @@ public class EnvConfiguration extends AbstractConfiguration
LOG.debug("Binding env entries from the server scope"); LOG.debug("Binding env entries from the server scope");
doBindings(envCtx, context.getServer()); doBindings(envCtx, context.getServer());
LOG.debug("Binding env entries from environment {} scope", ServletContextHandler.__environment.getName()); LOG.debug("Binding env entries from environment {} scope", ServletContextHandler.ENVIRONMENT.getName());
doBindings(envCtx, ServletContextHandler.__environment.getName()); doBindings(envCtx, ServletContextHandler.ENVIRONMENT.getName());
LOG.debug("Binding env entries from the context scope"); LOG.debug("Binding env entries from the context scope");
doBindings(envCtx, context); doBindings(envCtx, context);

View File

@ -83,13 +83,13 @@ public class PlusConfiguration extends AbstractConfiguration
{ {
try try
{ {
Transaction.bindTransactionToENC(ServletContextHandler.__environment.getName()); Transaction.bindTransactionToENC(ServletContextHandler.ENVIRONMENT.getName());
} }
catch (NameNotFoundException e) catch (NameNotFoundException e)
{ {
try try
{ {
org.eclipse.jetty.plus.jndi.Transaction.bindTransactionToENC(ServletContextHandler.__environment.getName()); org.eclipse.jetty.plus.jndi.Transaction.bindTransactionToENC(ServletContextHandler.ENVIRONMENT.getName());
} }
catch (NameNotFoundException x) catch (NameNotFoundException x)
{ {

View File

@ -129,7 +129,7 @@ public class PlusDescriptorProcessorTest
context.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()}); context.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()});
context.preConfigure(); context.preConfigure();
context.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), context)); context.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), context));
context.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.plus.webapp."); //need visbility of the TestInjections class context.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.plus.webapp."); //need visbility of the TestInjections class
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader(); ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader()); Thread.currentThread().setContextClassLoader(context.getClassLoader());
Context icontext = new InitialContext(); Context icontext = new InitialContext();

View File

@ -89,7 +89,7 @@ public class TestQuickStart
WebAppContext webapp = new WebAppContext(); WebAppContext webapp = new WebAppContext();
webapp.setBaseResourceAsPath(testDir.toPath()); webapp.setBaseResourceAsPath(testDir.toPath());
webapp.addConfiguration(new QuickStartConfiguration()); webapp.addConfiguration(new QuickStartConfiguration());
webapp.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); webapp.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart.");
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
//add in the servlet //add in the servlet
webapp.getServletHandler().addServlet(fooHolder); webapp.getServletHandler().addServlet(fooHolder);
@ -139,7 +139,7 @@ public class TestQuickStart
webapp.addConfiguration(new QuickStartConfiguration()); webapp.addConfiguration(new QuickStartConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setBaseResourceAsPath(testDir.toPath()); webapp.setBaseResourceAsPath(testDir.toPath());
webapp.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); webapp.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart.");
server.setHandler(webapp); server.setHandler(webapp);
server.setDryRun(false); server.setDryRun(false);
@ -180,7 +180,7 @@ public class TestQuickStart
webapp.addConfiguration(new QuickStartConfiguration()); webapp.addConfiguration(new QuickStartConfiguration());
webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
webapp.setBaseResourceAsPath(testDir.toPath()); webapp.setBaseResourceAsPath(testDir.toPath());
webapp.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); webapp.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart.");
server.setHandler(webapp); server.setHandler(webapp);
server.setDryRun(false); server.setDryRun(false);
@ -255,7 +255,7 @@ public class TestQuickStart
//a freshly applied context xml //a freshly applied context xml
quickstart = new WebAppContext(); quickstart = new WebAppContext();
//need visibility of FooServlet, FooFilter, FooContextListener when we quickstart //need visibility of FooServlet, FooFilter, FooContextListener when we quickstart
quickstart.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); quickstart.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart.");
quickstart.addConfiguration(new QuickStartConfiguration()); quickstart.addConfiguration(new QuickStartConfiguration());
quickstart.setWar(testDir.toURI().toURL().toExternalForm()); quickstart.setWar(testDir.toURI().toURL().toExternalForm());
quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath()); quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath());

View File

@ -131,7 +131,12 @@ import static jakarta.servlet.ServletContext.TEMPDIR;
public class ServletContextHandler extends ContextHandler public class ServletContextHandler extends ContextHandler
{ {
private static final Logger LOG = LoggerFactory.getLogger(ServletContextHandler.class); private static final Logger LOG = LoggerFactory.getLogger(ServletContextHandler.class);
public static final Environment __environment = Environment.ensure("ee10"); public static final Environment ENVIRONMENT = Environment.ensure("ee10");
/**
* @deprecated Use {@link ServletContextHandler#ENVIRONMENT} instead.
*/
@Deprecated(since = "12.0.9", forRemoval = true)
public static final Environment __environment = ENVIRONMENT;
public static final Class<?>[] SERVLET_LISTENER_TYPES = public static final Class<?>[] SERVLET_LISTENER_TYPES =
{ {
ServletContextListener.class, ServletContextListener.class,

View File

@ -29,7 +29,6 @@ import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
@ -204,8 +203,8 @@ public class EmbeddedWeldTest
webapp.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener()); webapp.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener());
String pkg = EmbeddedWeldTest.class.getPackage().getName(); String pkg = EmbeddedWeldTest.class.getPackage().getName();
webapp.getServerClassMatcher().add("-" + pkg + "."); webapp.getHiddenClassMatcher().add("-" + pkg + ".");
webapp.getSystemClassMatcher().add(pkg + "."); webapp.getProtectedClassMatcher().add(pkg + ".");
webapp.addServlet(GreetingsServlet.class, "/greet"); webapp.addServlet(GreetingsServlet.class, "/greet");
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
@ -240,8 +239,8 @@ public class EmbeddedWeldTest
// This is ugly but needed for maven for testing in a overlaid war pom // This is ugly but needed for maven for testing in a overlaid war pom
String pkg = EmbeddedWeldTest.class.getPackage().getName(); String pkg = EmbeddedWeldTest.class.getPackage().getName();
webapp.getServerClassMatcher().add("-" + pkg + "."); webapp.getHiddenClassMatcher().add("-" + pkg + ".");
webapp.getSystemClassMatcher().add(pkg + "."); webapp.getProtectedClassMatcher().add(pkg + ".");
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));

View File

@ -3,11 +3,11 @@
<Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext"> <Configure class="org.eclipse.jetty.ee10.webapp.WebAppContext">
<Set name="contextPath">/rfc2616-webapp</Set> <Set name="contextPath">/rfc2616-webapp</Set>
<Set name="war"><Property name="test.webapps" default="target/webapps" />/ee10-test-rfc2616.war</Set> <Set name="war"><Property name="test.webapps" default="target/webapps" />/ee10-test-rfc2616.war</Set>
<Get name="systemClassMatcher"> <Get name="ProtectedClassMatcher">
<Call name="add"><Arg>org.slf4j.</Arg></Call> <Call name="add"><Arg>org.slf4j.</Arg></Call>
<Call name="add"><Arg>org.eclipse.jetty.logging.</Arg></Call> <Call name="add"><Arg>org.eclipse.jetty.logging.</Arg></Call>
</Get> </Get>
<Get name="serverClassMatcher"> <Get name="HiddenClassMatcher">
<Call name="add"><Arg>-org.slf4j.</Arg></Call> <Call name="add"><Arg>-org.slf4j.</Arg></Call>
<Call name="add"><Arg>-org.eclipse.jetty.logging.</Arg></Call> <Call name="add"><Arg>-org.eclipse.jetty.logging.</Arg></Call>
</Get> </Get>

View File

@ -17,6 +17,10 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-session</artifactId> <artifactId>jetty-session</artifactId>

View File

@ -2,22 +2,21 @@
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call class="org.eclipse.jetty.ee10.webapp.WebAppContext" name="addSystemClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addProtectedClasses">
<Arg><Ref refid="Server"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addSystemClasses"/></Arg> <Arg><Property name="jetty.webapp.addProtectedClasses" deprecated="jetty.webapp.addSystemClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
<Call class="org.eclipse.jetty.ee10.webapp.WebAppContext" name="addServerClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addHiddenClasses">
<Arg><Ref refid="Server"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addServerClasses"/></Arg> <Arg><Property name="jetty.webapp.addHiddenClasses" deprecated="jetty.webapp.addServerClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
</Configure> </Configure>

View File

@ -9,6 +9,7 @@ This module enables deployment of Java Servlet web applications.
ee10 ee10
[depend] [depend]
ee-webapp
ee10-servlet ee10-servlet
ee10-security ee10-security
@ -20,9 +21,9 @@ lib/jetty-ee10-webapp-${jetty.version}.jar
[ini-template] [ini-template]
# tag::ini-template[] # tag::ini-template[]
## Add to the server wide default jars and packages protected or hidden from webapps. ## Add to the environment wide default jars and packages protected or hidden from webapps.
## System classes are protected and cannot be overridden by a webapp. ## Protected (aka System) classes cannot be overridden by a webapp.
## Server classes are hidden and cannot be seen by a webapp ## Hidden (aka Server) classes cannot be seen by a webapp
## Lists of patterns are comma separated and may be either: ## Lists of patterns are comma separated and may be either:
## + a qualified classname e.g. 'com.acme.Foo' ## + a qualified classname e.g. 'com.acme.Foo'
## + a package name e.g. 'net.example.' ## + a package name e.g. 'net.example.'
@ -32,8 +33,8 @@ lib/jetty-ee10-webapp-${jetty.version}.jar
## ##
## The +=, operator appends to a CSV list with a comma as needed. ## The +=, operator appends to a CSV list with a comma as needed.
## ##
#jetty.webapp.addSystemClasses+=,org.example. #jetty.webapp.addProtectedClasses+=,org.example.
#jetty.webapp.addServerClasses+=,org.example. #jetty.webapp.addHiddenClasses+=,org.example.
# end::ini-template[] # end::ini-template[]
[ini] [ini]

View File

@ -33,6 +33,7 @@ module org.eclipse.jetty.ee10.webapp
requires transitive org.eclipse.jetty.session; requires transitive org.eclipse.jetty.session;
requires transitive org.eclipse.jetty.ee10.servlet; requires transitive org.eclipse.jetty.ee10.servlet;
requires transitive org.eclipse.jetty.xml; requires transitive org.eclipse.jetty.xml;
requires transitive org.eclipse.jetty.ee;
exports org.eclipse.jetty.ee10.webapp; exports org.eclipse.jetty.ee10.webapp;

View File

@ -13,804 +13,53 @@
package org.eclipse.jetty.ee10.webapp; package org.eclipse.jetty.ee10.webapp;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.eclipse.jetty.util.IncludeExcludeSet; import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.URIUtil;
/** /**
* A matcher for classes based on package and/or location and/or module/ * @deprecated Use org.eclipse.jetty.util.ClassMatcher
* <p>
* Performs pattern matching of a class against a set of pattern entries.
* A class pattern is a string of one of the forms:<ul>
* <li>'org.package.SomeClass' will match a specific class
* <li>'org.package.' will match a specific package hierarchy
* <li>'org.package.SomeClass$NestedClass ' will match a nested class exactly otherwise.
* Nested classes are matched by their containing class. (eg. org.example.MyClass
* matches org.example.MyClass$AnyNestedClass)
* <li>'file:///some/location/' - A file system directory from which
* the class was loaded
* <li>'file:///some/location.jar' - The URI of a jar file from which
* the class was loaded
* <li>'jrt:/modulename' - A Java9 module name</li>
* <li>Any of the above patterns preceded by '-' will exclude rather than include the match.
* </ul>
* When class is initialized from a classpath pattern string, entries
* in this string should be separated by ':' (semicolon) or ',' (comma).
*/ */
public class ClassMatcher extends AbstractSet<String> @Deprecated(since = "12.0.8", forRemoval = true)
public class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher
{ {
public static class Entry
{
private final String _pattern;
private final String _name;
private final boolean _inclusive;
protected Entry(String name, boolean inclusive)
{
_name = name;
_inclusive = inclusive;
_pattern = inclusive ? _name : ("-" + _name);
}
public String getPattern()
{
return _pattern;
}
public String getName()
{
return _name;
}
@Override
public String toString()
{
return _pattern;
}
@Override
public int hashCode()
{
return _pattern.hashCode();
}
@Override
public boolean equals(Object o)
{
return (o instanceof Entry) && _pattern.equals(((Entry)o)._pattern);
}
public boolean isInclusive()
{
return _inclusive;
}
}
private static class PackageEntry extends Entry
{
protected PackageEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class ClassEntry extends Entry
{
protected ClassEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class LocationEntry extends Entry
{
private final Path _path;
protected LocationEntry(String name, boolean inclusive)
{
super(name, inclusive);
URI uri = URI.create(name);
if (!uri.isAbsolute() && !"file".equalsIgnoreCase(uri.getScheme()))
throw new IllegalArgumentException("Not a valid file URI: " + name);
_path = Paths.get(uri);
}
public Path getPath()
{
return _path;
}
}
private static class ModuleEntry extends Entry
{
private final String _module;
protected ModuleEntry(String name, boolean inclusive)
{
super(name, inclusive);
if (!getName().startsWith("jrt:"))
throw new IllegalArgumentException(name);
_module = getName().split("/")[1];
}
public String getModule()
{
return _module;
}
}
public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(String name)
{
return _entries.getBest(name) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean isEmpty()
{
return _entries.isEmpty();
}
@Override
public boolean add(Entry entry)
{
String name = entry.getName();
if (entry instanceof ClassEntry)
name += "$";
else if (!(entry instanceof PackageEntry))
throw new IllegalArgumentException(entry.toString());
else if (".".equals(name))
name = "";
if (_entries.get(name) != null)
return false;
return _entries.put(name, entry);
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
@Override
public void clear()
{
_entries.clear();
}
}
public static class ByClass extends HashSet<Entry> implements Predicate<String>
{
private final Map<String, Entry> _entries = new HashMap<>();
@Override
public boolean test(String name)
{
return _entries.containsKey(name);
}
@Override
public Iterator<Entry> iterator()
{
return _entries.values().iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ClassEntry))
throw new IllegalArgumentException(entry.toString());
return _entries.put(entry.getName(), entry) == null;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByPackageOrName extends AbstractSet<Entry> implements Predicate<String>
{
private final ByClass _byClass = new ByClass();
private final ByPackage _byPackage = new ByPackage();
@Override
public boolean test(String name)
{
return _byPackage.test(name) || _byClass.test(name);
}
@Override
public Iterator<Entry> iterator()
{
// by package contains all entries (classes are also $ packages).
return _byPackage.iterator();
}
@Override
public int size()
{
return _byPackage.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof PackageEntry)
return _byPackage.add(entry);
if (entry instanceof ClassEntry)
{
// Add class name to packages also as classes act
// as packages for nested classes.
boolean added = _byPackage.add(entry);
added = _byClass.add(entry) || added;
return added;
}
throw new IllegalArgumentException();
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof Entry))
return false;
boolean removedPackage = _byPackage.remove(o);
boolean removedClass = _byClass.remove(o);
return removedPackage || removedClass;
}
@Override
public void clear()
{
_byPackage.clear();
_byClass.clear();
}
}
public static class ByLocation extends HashSet<Entry> implements Predicate<URI>
{
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equals("file"))
return false;
Path path = Paths.get(uri);
for (Entry entry : this)
{
if (!(entry instanceof LocationEntry))
throw new IllegalStateException();
Path entryPath = ((LocationEntry)entry).getPath();
if (Files.isDirectory(entryPath))
{
if (path.startsWith(entryPath))
{
return true;
}
}
else
{
try
{
if (Files.isSameFile(path, entryPath))
{
return true;
}
}
catch (IOException ignore)
{
// this means there is a FileSystem issue preventing comparison.
// Use old technique
if (path.equals(entryPath))
{
return true;
}
}
}
}
return false;
}
}
public static class ByModule extends HashSet<Entry> implements Predicate<URI>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equalsIgnoreCase("jrt"))
return false;
String module = uri.getPath();
int end = module.indexOf('/', 1);
if (end < 1)
end = module.length();
return _entries.get(module, 1, end - 1) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ModuleEntry))
throw new IllegalArgumentException(entry.toString());
String module = ((ModuleEntry)entry).getModule();
if (_entries.get(module) != null)
return false;
_entries.put(module, entry);
return true;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByLocationOrModule extends AbstractSet<Entry> implements Predicate<URI>
{
private final ByLocation _byLocation = new ByLocation();
private final ByModule _byModule = new ByModule();
@Override
public boolean test(URI name)
{
if ((name == null) || (!name.isAbsolute()))
return false;
return _byLocation.test(name) || _byModule.test(name);
}
@Override
public Iterator<Entry> iterator()
{
Set<Entry> entries = new HashSet<>();
entries.addAll(_byLocation);
entries.addAll(_byModule);
return entries.iterator();
}
@Override
public int size()
{
return _byLocation.size() + _byModule.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof LocationEntry)
return _byLocation.add(entry);
if (entry instanceof ModuleEntry)
return _byModule.add(entry);
throw new IllegalArgumentException(entry.toString());
}
@Override
public boolean remove(Object o)
{
if (o instanceof LocationEntry)
return _byLocation.remove(o);
if (o instanceof ModuleEntry)
return _byModule.remove(o);
return false;
}
@Override
public void clear()
{
_byLocation.clear();
_byModule.clear();
}
}
private final Map<String, Entry> _entries;
private final IncludeExcludeSet<Entry, String> _patterns;
private final IncludeExcludeSet<Entry, URI> _locations;
private ClassMatcher(Map<String, Entry> entries, IncludeExcludeSet<Entry, String> patterns, IncludeExcludeSet<Entry, URI> locations)
{
_entries = entries;
_patterns = patterns == null ? new IncludeExcludeSet<>(ByPackageOrName.class) : patterns;
_locations = locations == null ? new IncludeExcludeSet<>(ByLocationOrModule.class) : locations;
}
private ClassMatcher(Map<String, Entry> entries)
{
this(entries, null, null);
}
public ClassMatcher() public ClassMatcher()
{ {
this(new HashMap<>()); super();
} }
public ClassMatcher(ClassMatcher patterns) public ClassMatcher(ClassMatcher patterns)
{ {
this(new HashMap<>()); super(patterns);
if (patterns != null) }
setAll(patterns.getPatterns());
public ClassMatcher(org.eclipse.jetty.util.ClassMatcher patterns)
{
super(patterns);
} }
public ClassMatcher(String... patterns) public ClassMatcher(String... patterns)
{ {
this(new HashMap<>()); super(patterns);
if (patterns != null && patterns.length > 0)
setAll(patterns);
} }
public ClassMatcher(String pattern) public ClassMatcher(String pattern)
{ {
this(new HashMap<>()); super(pattern);
add(pattern);
} }
protected ClassMatcher(Map<String, Entry> entries, IncludeExcludeSet<Entry, String> patterns, IncludeExcludeSet<Entry, URI> locations)
{
super(entries, patterns, locations);
}
@Override
public ClassMatcher asImmutable() public ClassMatcher asImmutable()
{ {
return new ClassMatcher(Map.copyOf(_entries), return new ClassMatcher(Map.copyOf(_entries),
_patterns.asImmutable(), _patterns.asImmutable(),
_locations.asImmutable()); _locations.asImmutable());
} }
public boolean include(String name)
{
if (name == null)
return false;
return add(newEntry(name, true));
}
public boolean include(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, true)) || added;
}
return added;
}
public boolean exclude(String name)
{
if (name == null)
return false;
return add(newEntry(name, false));
}
public boolean exclude(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, false)) || added;
}
return added;
}
@Override
public boolean add(String pattern)
{
if (pattern == null)
return false;
return add(newEntry(pattern));
}
public boolean add(String... pattern)
{
boolean added = false;
for (String p : pattern)
{
if (p != null)
added = add(newEntry(p)) || added;
}
return added;
}
protected boolean add(Entry entry)
{
if (_entries.containsKey(entry.getPattern()))
return false;
_entries.put(entry.getPattern(), entry);
if (entry instanceof LocationEntry || entry instanceof ModuleEntry)
{
if (entry.isInclusive())
_locations.include(entry);
else
_locations.exclude(entry);
}
else
{
if (entry.isInclusive())
_patterns.include(entry);
else
_patterns.exclude(entry);
}
return true;
}
protected Entry newEntry(String pattern)
{
if (pattern.startsWith("-"))
return newEntry(pattern.substring(1), false);
return newEntry(pattern, true);
}
protected Entry newEntry(String name, boolean inclusive)
{
if (name.startsWith("-"))
throw new IllegalStateException(name);
if (name.startsWith("file:"))
return new LocationEntry(name, inclusive);
if (name.startsWith("jrt:"))
return new ModuleEntry(name, inclusive);
if (name.endsWith("."))
return new PackageEntry(name, inclusive);
return new ClassEntry(name, inclusive);
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof String pattern))
return false;
Entry entry = _entries.remove(pattern);
if (entry == null)
return false;
List<Entry> saved = new ArrayList<>(_entries.values());
clear();
for (Entry e : saved)
{
add(e);
}
return true;
}
@Override
public void clear()
{
_entries.clear();
_patterns.clear();
_locations.clear();
}
@Override
public Iterator<String> iterator()
{
return _entries.keySet().iterator();
}
@Override
public int size()
{
return _entries.size();
}
/**
* Initialize the matcher by parsing each classpath pattern in an array
*
* @param classes array of classpath patterns
*/
private void setAll(String[] classes)
{
_entries.clear();
addAll(classes);
}
/**
* Add array of classpath patterns.
* @param classes array of classpath patterns
*/
private void addAll(String[] classes)
{
if (classes != null)
addAll(Arrays.asList(classes));
}
/**
* @return array of classpath patterns
*/
public String[] getPatterns()
{
return toArray(new String[0]);
}
/**
* @return array of inclusive classpath patterns
*/
public String[] getInclusions()
{
return _entries.values().stream().filter(Entry::isInclusive).map(Entry::getName).toArray(String[]::new);
}
/**
* @return array of excluded classpath patterns (without '-' prefix)
*/
public String[] getExclusions()
{
return _entries.values().stream().filter(e -> !e.isInclusive()).map(Entry::getName).toArray(String[]::new);
}
/**
* Match the class name against the pattern
*
* @param name name of the class to match
* @return true if class matches the pattern
*/
public boolean match(String name)
{
return _patterns.test(name);
}
/**
* Match the class name against the pattern
*
* @param clazz A class to try to match
* @return true if class matches the pattern
*/
public boolean match(Class<?> clazz)
{
try
{
return combine(_patterns, clazz.getName(), _locations, () -> TypeUtil.getLocationOfClass(clazz));
}
catch (Exception ignored)
{
}
return false;
}
public boolean match(String name, URL url)
{
if (url == null)
return false;
// Strip class suffix for name matching
if (name.endsWith(".class"))
name = name.substring(0, name.length() - 6);
// Treat path elements as packages for name matching
name = StringUtil.replace(name, '/', '.');
return combine(_patterns, name, _locations, () ->
{
try
{
return URIUtil.unwrapContainer(url.toURI());
}
catch (URISyntaxException ignored)
{
return null;
}
});
}
/**
* Match a class against inclusions and exclusions by name and location.
* Name based checks are performed before location checks. For a class to match,
* it must not be excluded by either name or location, and must either be explicitly
* included, or for there to be no inclusions. In the case where the location
* of the class is null, it will match if it is included by name, or
* if there are no location exclusions.
*
* @param names configured inclusions and exclusions by name
* @param name the name to check
* @param locations configured inclusions and exclusions by location
* @param location the location of the class (can be null)
* @return true if the class is not excluded but is included, or there are
* no inclusions. False otherwise.
*/
static boolean combine(IncludeExcludeSet<Entry, String> names, String name, IncludeExcludeSet<Entry, URI> locations, Supplier<URI> location)
{
// check the name set
Boolean byName = names.isIncludedAndNotExcluded(name);
// If we excluded by name, then no match
if (Boolean.FALSE == byName)
return false;
// check the location set
URI uri = location.get();
Boolean byLocation = uri == null ? null : locations.isIncludedAndNotExcluded(uri);
// If we excluded by location or couldn't check location exclusion, then no match
if (Boolean.FALSE == byLocation || (locations.hasExcludes() && uri == null))
return false;
// If there are includes, then we must be included to match.
if (names.hasIncludes() || locations.hasIncludes())
return byName == Boolean.TRUE || byLocation == Boolean.TRUE;
// Otherwise there are no includes and it was not excluded, so match
return true;
}
} }

View File

@ -60,8 +60,8 @@ import org.slf4j.LoggerFactory;
* parent loader. Java2 compliant loading, where the parent loader * parent loader. Java2 compliant loading, where the parent loader
* always has priority, can be selected with the * always has priority, can be selected with the
* {@link WebAppContext#setParentLoaderPriority(boolean)} * {@link WebAppContext#setParentLoaderPriority(boolean)}
* method and influenced with {@link WebAppContext#isServerClass(Class)} and * method and influenced with {@link WebAppContext#isHiddenClass(Class)} and
* {@link WebAppContext#isSystemClass(Class)}. * {@link WebAppContext#isProtectedClass(Class)}.
* <p> * <p>
* If no parent class loader is provided, then the current thread * If no parent class loader is provided, then the current thread
* context classloader will be used. If that is null then the * context classloader will be used. If that is null then the
@ -115,9 +115,9 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
List<Resource> getExtraClasspath(); List<Resource> getExtraClasspath();
boolean isServerResource(String name, URL parentUrl); boolean isHiddenResource(String name, URL parentUrl);
boolean isSystemResource(String name, URL webappUrl); boolean isProtectedResource(String name, URL webappUrl);
} }
/** /**
@ -302,7 +302,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
while (urls != null && urls.hasMoreElements()) while (urls != null && urls.hasMoreElements())
{ {
URL url = urls.nextElement(); URL url = urls.nextElement();
if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerResource(name, url)) if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isHiddenResource(name, url))
fromParent.add(url); fromParent.add(url);
} }
@ -310,7 +310,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
while (urls != null && urls.hasMoreElements()) while (urls != null && urls.hasMoreElements())
{ {
URL url = urls.nextElement(); URL url = urls.nextElement();
if (!_context.isSystemResource(name, url) || fromParent.isEmpty()) if (!_context.isProtectedResource(name, url) || fromParent.isEmpty())
fromWebapp.add(url); fromWebapp.add(url);
} }
@ -351,7 +351,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
// return if we have a url the webapp is allowed to see // return if we have a url the webapp is allowed to see
if (parentUrl != null && if (parentUrl != null &&
(Boolean.TRUE.equals(__loadServerClasses.get()) || (Boolean.TRUE.equals(__loadServerClasses.get()) ||
!_context.isServerResource(name, parentUrl))) !_context.isHiddenResource(name, parentUrl)))
resource = parentUrl; resource = parentUrl;
else else
{ {
@ -370,7 +370,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
{ {
URL webappUrl = this.findResource(name); URL webappUrl = this.findResource(name);
if (webappUrl != null && !_context.isSystemResource(name, webappUrl)) if (webappUrl != null && !_context.isProtectedResource(name, webappUrl))
resource = webappUrl; resource = webappUrl;
else else
{ {
@ -379,7 +379,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
URL parentUrl = _parent.getResource(name); URL parentUrl = _parent.getResource(name);
if (parentUrl != null && if (parentUrl != null &&
(Boolean.TRUE.equals(__loadServerClasses.get()) || (Boolean.TRUE.equals(__loadServerClasses.get()) ||
!_context.isServerResource(name, parentUrl))) !_context.isHiddenResource(name, parentUrl)))
resource = parentUrl; resource = parentUrl;
// We couldn't find a parent resource, so OK to return a webapp one if it exists // We couldn't find a parent resource, so OK to return a webapp one if it exists
// and we just couldn't see it before // and we just couldn't see it before
@ -425,7 +425,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
throw new ClassNotFoundException("Bad ClassLoader: returned null for loadClass(" + name + ")"); throw new ClassNotFoundException("Bad ClassLoader: returned null for loadClass(" + name + ")");
// If the webapp is allowed to see this class // If the webapp is allowed to see this class
if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parentClass)) if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isHiddenClass(parentClass))
{ {
return parentClass; return parentClass;
} }
@ -473,7 +473,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
{ {
parentClass = _parent.loadClass(name); parentClass = _parent.loadClass(name);
// If the webapp is allowed to see this class // If the webapp is allowed to see this class
if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parentClass)) if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isHiddenClass(parentClass))
{ {
return parentClass; return parentClass;
} }
@ -524,7 +524,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
String path = TypeUtil.toClassReference(name); String path = TypeUtil.toClassReference(name);
URL webappUrl = findResource(path); URL webappUrl = findResource(path);
if (webappUrl != null && (!checkSystemResource || !_context.isSystemResource(name, webappUrl))) if (webappUrl != null && (!checkSystemResource || !_context.isProtectedResource(name, webappUrl)))
{ {
webappClass = this.foundClass(name, webappUrl); webappClass = this.foundClass(name, webappUrl);
@ -605,14 +605,14 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
} }
@Override @Override
public boolean isSystemClass(Class<?> clazz) public boolean isProtectedClass(Class<?> clazz)
{ {
return _context.isSystemClass(clazz); return _context.isProtectedClass(clazz);
} }
@Override @Override
public boolean isServerClass(Class<?> clazz) public boolean isHiddenClass(Class<?> clazz)
{ {
return _context.isServerClass(clazz); return _context.isHiddenClass(clazz);
} }
} }

View File

@ -36,6 +36,7 @@ import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener; import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.ee10.servlet.ErrorHandler; import org.eclipse.jetty.ee10.servlet.ErrorHandler;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
@ -49,6 +50,7 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Deployable; import org.eclipse.jetty.server.Deployable;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler;
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;
@ -80,32 +82,35 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class); static final Logger LOG = LoggerFactory.getLogger(WebAppContext.class);
public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee10/webapp/webdefault-ee10.xml"; public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee10/webapp/webdefault-ee10.xml";
public static final String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses"; /**
public static final String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses"; * @deprecated use {@link WebAppClassLoading#PROTECTED_CLASSES_ATTRIBUTE} instead.
*/
@Deprecated(forRemoval = true, since = "12.0.9")
public static final String SERVER_SYS_CLASSES = WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE;
/**
* @deprecated use {@link WebAppClassLoading#HIDDEN_CLASSES_ATTRIBUTE} instead.
*/
@Deprecated(forRemoval = true, since = "12.0.9")
public static final String SERVER_SRV_CLASSES = WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE;
private static String[] __dftProtectedTargets = {"/WEB-INF", "/META-INF"}; private static final String[] __dftProtectedTargets = {"/WEB-INF", "/META-INF"};
// System classes are classes that cannot be replaced by /**
// the web application, and they are *always* loaded via * @deprecated use {@link WebAppClassLoading#DEFAULT_PROTECTED_CLASSES}
// system classloader. */
public static final ClassMatcher __dftSystemClasses = new ClassMatcher( @Deprecated (forRemoval = true, since = "12.0.9")
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) public static final org.eclipse.jetty.ee10.webapp.ClassMatcher __dftSystemClasses =
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) new org.eclipse.jetty.ee10.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES);
"jakarta.", // Jakarta classes (per servlet spec v5.0 / Section 15.2.1)
"org.xml.", // javax.xml
"org.w3c." // javax.xml
);
// Server classes are classes that are hidden from being /**
// loaded by the web application using system classloader, * @deprecated use {@link WebAppClassLoading#DEFAULT_HIDDEN_CLASSES}
// so if web application needs to load any of such classes, */
// it has to include them in its distribution. @Deprecated (forRemoval = true, since = "12.0.9")
public static final ClassMatcher __dftServerClasses = new ClassMatcher( public static final org.eclipse.jetty.ee10.webapp.ClassMatcher __dftServerClasses =
"org.eclipse.jetty." // hide jetty classes new org.eclipse.jetty.ee10.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES);
);
private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses); private final ClassMatcher _protectedClasses = new ClassMatcher(WebAppClassLoading.getProtectedClasses(ServletContextHandler.ENVIRONMENT));
private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses); private final ClassMatcher _hiddenClasses = new ClassMatcher(WebAppClassLoading.getHiddenClasses(ServletContextHandler.ENVIRONMENT));
private Configurations _configurations; private Configurations _configurations;
private String _defaultsDescriptor = WEB_DEFAULTS_XML; private String _defaultsDescriptor = WEB_DEFAULTS_XML;
@ -420,7 +425,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())
{ {
_serverClasses.include(configuration.getServerClasses().getInclusions()); _hiddenClasses.include(configuration.getServerClasses().getInclusions());
} }
// Setup Configuration classes for this webapp! // Setup Configuration classes for this webapp!
@ -428,8 +433,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
_configurations.sort(); _configurations.sort();
for (Configuration configuration : _configurations) for (Configuration configuration : _configurations)
{ {
_systemClasses.add(configuration.getSystemClasses().getPatterns()); _protectedClasses.add(configuration.getSystemClasses().getPatterns());
_serverClasses.exclude(configuration.getServerClasses().getExclusions()); _hiddenClasses.exclude(configuration.getServerClasses().getExclusions());
} }
// Configure classloader // Configure classloader
@ -484,7 +489,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
protected void doStart() throws Exception protected void doStart() throws Exception
{ {
ClassLoader old = Thread.currentThread().getContextClassLoader(); ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(__environment.getClassLoader()); Thread.currentThread().setContextClassLoader(ServletContextHandler.ENVIRONMENT.getClassLoader());
try try
{ {
_metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
@ -615,107 +620,217 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
} }
/** /**
* Set the server classes patterns. * Set the hidden (aka 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 hiddenClasses
* 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.
* *
* @param serverClasses the server classes pattern * @param hiddenClasses the server classes pattern
*/ */
public void setServerClassMatcher(ClassMatcher serverClasses) public void setHiddenClassMatcher(ClassMatcher hiddenClasses)
{ {
_serverClasses.clear(); _hiddenClasses.clear();
_serverClasses.add(serverClasses.getPatterns()); _hiddenClasses.add(hiddenClasses.getPatterns());
} }
/** /**
* Set the system classes patterns. * Set the protected (aka 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)}.
* *
* @param systemClasses the system classes pattern * @param protectedClasses the system classes pattern
*/ */
public void setSystemClassMatcher(ClassMatcher systemClasses) public void setProtectedClassMatcher(ClassMatcher protectedClasses)
{ {
_systemClasses.clear(); _protectedClasses.clear();
_systemClasses.add(systemClasses.getPatterns()); _protectedClasses.add(protectedClasses.getPatterns());
} }
/** /**
* Add a ClassMatcher for server classes by combining with * Add a ClassMatcher for hidden (server) classes by combining with
* any existing matcher. * any existing matcher.
* *
* @param serverClasses The class matcher of patterns to add to the server ClassMatcher * @param hiddenClasses The class matcher of patterns to add to the server ClassMatcher
*/ */
public void addServerClassMatcher(ClassMatcher serverClasses) public void addHiddenClassMatcher(ClassMatcher hiddenClasses)
{ {
_serverClasses.add(serverClasses.getPatterns()); _hiddenClasses.add(hiddenClasses.getPatterns());
} }
/** /**
* Add a ClassMatcher for system classes by combining with * Add a ClassMatcher for protected (system) classes by combining with
* any existing matcher. * any existing matcher.
* *
* @param systemClasses The class matcher of patterns to add to the system ClassMatcher * @param protectedClasses The class matcher of patterns to add to the system ClassMatcher
*/ */
public void addSystemClassMatcher(ClassMatcher systemClasses) public void addProtectedClassMatcher(ClassMatcher protectedClasses)
{ {
_systemClasses.add(systemClasses.getPatterns()); _protectedClasses.add(protectedClasses.getPatterns());
} }
/** /**
* @return The ClassMatcher used to match System (protected) classes * @return The ClassMatcher used to match System (protected) classes
*/ */
public ClassMatcher getSystemClassMatcher() public ClassMatcher getProtectedClassMatcher()
{ {
return _systemClasses; return _protectedClasses;
} }
/** /**
* @return The ClassMatcher used to match Server (hidden) classes * @return The ClassMatcher used to match Server (hiddenClasses) classes
*/ */
public ClassMatcher getServerClassMatcher() public ClassMatcher getHiddenClassMatcher()
{ {
return _serverClasses; return _hiddenClasses;
} }
@ManagedAttribute(value = "classes and packages protected by context classloader", readonly = true) @ManagedAttribute(value = "classes and packages protected by context classloader", readonly = true)
public String[] getProtectedClasses()
{
return _protectedClasses.getPatterns();
}
@ManagedAttribute(value = "classes and packages hiddenClasses by the context classloader", readonly = true)
public String[] getHiddenClasses()
{
return _hiddenClasses.getPatterns();
}
@Override
public boolean isHiddenClass(Class<?> clazz)
{
return _hiddenClasses.match(clazz);
}
@Override
public boolean isProtectedClass(Class<?> clazz)
{
return _protectedClasses.match(clazz);
}
@Override
public boolean isHiddenResource(String name, URL url)
{
return _hiddenClasses.match(name, url);
}
@Override
public boolean isProtectedResource(String name, URL url)
{
return _protectedClasses.match(name, url);
}
/**
* @deprecated use {@link #setHiddenClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void setServerClassMatcher(ClassMatcher serverClasses)
{
_hiddenClasses.clear();
_hiddenClasses.add(serverClasses.getPatterns());
}
/**
* @deprecated use {@link #setProtectedClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void setSystemClassMatcher(ClassMatcher systemClasses)
{
_protectedClasses.clear();
_protectedClasses.add(systemClasses.getPatterns());
}
/**
* @deprecated use {@link #addHiddenClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void addServerClassMatcher(ClassMatcher serverClasses)
{
_hiddenClasses.add(serverClasses.getPatterns());
}
/**
* @deprecated use {@link #addProtectedClassMatcher(ClassMatcher)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public void addSystemClassMatcher(ClassMatcher systemClasses)
{
_protectedClasses.add(systemClasses.getPatterns());
}
/**
* @deprecated use {@link #getProtectedClassMatcher()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public ClassMatcher getSystemClassMatcher()
{
return _protectedClasses;
}
/**
* @deprecated use {@link #getHiddenClassMatcher()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public ClassMatcher getServerClassMatcher()
{
return _hiddenClasses;
}
/**
* @deprecated use {@link #getProtectedClasses()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public String[] getSystemClasses() public String[] getSystemClasses()
{ {
return _systemClasses.getPatterns(); return _protectedClasses.getPatterns();
} }
@ManagedAttribute(value = "classes and packages hidden by the context classloader", readonly = true) /**
* @deprecated use {@link #getHiddenClasses()}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public String[] getServerClasses() public String[] getServerClasses()
{ {
return _serverClasses.getPatterns(); return _hiddenClasses.getPatterns();
} }
@Override /**
* @deprecated use {@link #isHiddenClass(Class)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isServerClass(Class<?> clazz) public boolean isServerClass(Class<?> clazz)
{ {
return _serverClasses.match(clazz); return _hiddenClasses.match(clazz);
} }
@Override /**
* @deprecated use {@link #isProtectedClass(Class)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isSystemClass(Class<?> clazz) public boolean isSystemClass(Class<?> clazz)
{ {
return _systemClasses.match(clazz); return _protectedClasses.match(clazz);
} }
@Override /**
* @deprecated use {@link #isHiddenResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isServerResource(String name, URL url) public boolean isServerResource(String name, URL url)
{ {
return _serverClasses.match(name, url); return _hiddenClasses.match(name, url);
} }
@Override /**
* @deprecated use {@link #isProtectedResource(String, URL)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public boolean isSystemResource(String name, URL url) public boolean isSystemResource(String name, URL url)
{ {
return _systemClasses.match(name, url); return _protectedClasses.match(name, url);
} }
@Override @Override
@ -724,23 +839,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
super.setServer(server); super.setServer(server);
if (server != null) if (server != null)
{ {
if (__dftSystemClasses.equals(_systemClasses)) _protectedClasses.add(WebAppClassLoading.getProtectedClasses(server).getPatterns());
{ _hiddenClasses.add(WebAppClassLoading.getHiddenClasses(server).getPatterns());
Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
if (systemClasses instanceof String[])
systemClasses = new ClassMatcher((String[])systemClasses);
if (systemClasses instanceof ClassMatcher)
_systemClasses.add(((ClassMatcher)systemClasses).getPatterns());
}
if (__dftServerClasses.equals(_serverClasses))
{
Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
if (serverClasses instanceof String[])
serverClasses = new ClassMatcher((String[])serverClasses);
if (serverClasses instanceof ClassMatcher)
_serverClasses.add(((ClassMatcher)serverClasses).getPatterns());
}
} }
} }
@ -863,16 +963,16 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
public void dump(Appendable out, String indent) throws IOException public void dump(Appendable out, String indent) throws IOException
{ {
List<String> systemClasses = null; List<String> systemClasses = null;
if (_systemClasses != null) if (_protectedClasses != null)
{ {
systemClasses = new ArrayList<>(_systemClasses); systemClasses = new ArrayList<>(_protectedClasses);
Collections.sort(systemClasses); Collections.sort(systemClasses);
} }
List<String> serverClasses = null; List<String> serverClasses = null;
if (_serverClasses != null) if (_hiddenClasses != null)
{ {
serverClasses = new ArrayList<>(_serverClasses); serverClasses = new ArrayList<>(_hiddenClasses);
Collections.sort(serverClasses); Collections.sort(serverClasses);
} }
@ -1411,38 +1511,31 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _metadata; return _metadata;
} }
public static void addServerClasses(Server server, String... pattern) /**
* Add a Server Class pattern to use for all ee9 WebAppContexts.
* @param server The {@link Server} instance to add classes to
* @param patterns the patterns to use
* @see #getHiddenClassMatcher()
* @see #getHiddenClasses()
* @deprecated use {@link WebAppClassLoading#addProtectedClasses(Server, String...)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public static void addServerClasses(Server server, String... patterns)
{ {
addClasses(__dftServerClasses, SERVER_SRV_CLASSES, server, pattern); WebAppClassLoading.addHiddenClasses(server, patterns);
} }
public static void addSystemClasses(Server server, String... pattern) /**
* Add a System Class pattern to use for all ee9 WebAppContexts.
* @param server The {@link Server} instance to add classes to
* @param patterns the patterns to use
* @see #getProtectedClassMatcher()
* @see #getProtectedClasses()
* @deprecated use {@link WebAppClassLoading#addHiddenClasses(Server, String...)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public static void addSystemClasses(Server server, String... patterns)
{ {
addClasses(__dftSystemClasses, SERVER_SYS_CLASSES, server, pattern); WebAppClassLoading.addProtectedClasses(server, patterns);
}
private static void addClasses(ClassMatcher matcher, String attribute, Server server, String... pattern)
{
if (pattern == null || pattern.length == 0)
return;
// look for a Server attribute with the list of System classes
// to apply to every web application. If not present, use our defaults.
Object o = server.getAttribute(attribute);
if (o instanceof ClassMatcher)
{
((ClassMatcher)o).add(pattern);
return;
}
String[] classes;
if (o instanceof String[])
classes = (String[])o;
else
classes = matcher.getPatterns();
int l = classes.length;
classes = Arrays.copyOf(classes, l + pattern.length);
System.arraycopy(pattern, 0, classes, l, pattern.length);
server.setAttribute(attribute, classes);
} }
} }

View File

@ -95,7 +95,7 @@ public class WebDescriptor extends Descriptor
public WebDescriptorParser(boolean validating) throws IOException public WebDescriptorParser(boolean validating) throws IOException
{ {
super(validating); super(validating);
String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.__environment.getName()); String catalogName = "catalog-%s.xml".formatted(ServletContextHandler.ENVIRONMENT.getName());
URL url = WebDescriptor.class.getResource(catalogName); URL url = WebDescriptor.class.getResource(catalogName);
if (url == null) if (url == null)
throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName));

View File

@ -26,9 +26,9 @@ 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.FileSystemPool;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -37,7 +37,6 @@ import org.junit.jupiter.api.Test;
import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;
@ -211,11 +210,11 @@ public class WebAppClassLoaderTest
@Test @Test
public void testExposedClassDeprecated() throws Exception public void testExposedClassDeprecated() throws Exception
{ {
String[] oldSC = _context.getServerClasses(); String[] oldSC = _context.getHiddenClasses();
String[] newSC = new String[oldSC.length + 1]; String[] newSC = new String[oldSC.length + 1];
newSC[0] = "-org.eclipse.jetty.ee10.webapp.Configuration"; newSC[0] = "-org.eclipse.jetty.ee10.webapp.Configuration";
System.arraycopy(oldSC, 0, newSC, 1, oldSC.length); System.arraycopy(oldSC, 0, newSC, 1, oldSC.length);
_context.setServerClassMatcher(new ClassMatcher(newSC)); _context.setHiddenClassMatcher(new ClassMatcher(newSC));
assertCanLoadClass("org.acme.webapp.ClassInJarA"); assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB"); assertCanLoadClass("org.acme.webapp.ClassInJarB");
@ -228,7 +227,7 @@ public class WebAppClassLoaderTest
@Test @Test
public void testExposedClass() throws Exception public void testExposedClass() throws Exception
{ {
_context.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.webapp.Configuration"); _context.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.webapp.Configuration");
assertCanLoadClass("org.acme.webapp.ClassInJarA"); assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB"); assertCanLoadClass("org.acme.webapp.ClassInJarB");
@ -241,18 +240,18 @@ public class WebAppClassLoaderTest
@Test @Test
public void testSystemServerClassDeprecated() throws Exception public void testSystemServerClassDeprecated() throws Exception
{ {
String[] oldServC = _context.getServerClasses(); String[] oldServC = _context.getHiddenClasses();
String[] newServC = new String[oldServC.length + 1]; String[] newServC = new String[oldServC.length + 1];
newServC[0] = "org.eclipse.jetty.ee10.webapp.Configuration"; newServC[0] = "org.eclipse.jetty.ee10.webapp.Configuration";
System.arraycopy(oldServC, 0, newServC, 1, oldServC.length); System.arraycopy(oldServC, 0, newServC, 1, oldServC.length);
_context.setServerClassMatcher(new ClassMatcher(newServC)); _context.setHiddenClassMatcher(new ClassMatcher(newServC));
String[] oldSysC = _context.getSystemClasses(); String[] oldSysC = _context.getProtectedClasses();
String[] newSysC = new String[oldSysC.length + 1]; String[] newSysC = new String[oldSysC.length + 1];
newSysC[0] = "org.eclipse.jetty.ee10.webapp."; newSysC[0] = "org.eclipse.jetty.ee10.webapp.";
System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length); System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length);
_context.setSystemClassMatcher(new ClassMatcher(newSysC)); _context.setProtectedClassMatcher(new ClassMatcher(newSysC));
assertCanLoadClass("org.acme.webapp.ClassInJarA"); assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB"); assertCanLoadClass("org.acme.webapp.ClassInJarB");
@ -260,28 +259,28 @@ public class WebAppClassLoaderTest
assertCantLoadClass("org.eclipse.jetty.ee10.webapp.Configuration"); assertCantLoadClass("org.eclipse.jetty.ee10.webapp.Configuration");
assertCantLoadClass("org.eclipse.jetty.ee10.webapp.JarScanner"); assertCantLoadClass("org.eclipse.jetty.ee10.webapp.JarScanner");
oldSysC = _context.getSystemClasses(); oldSysC = _context.getProtectedClasses();
newSysC = new String[oldSysC.length + 1]; newSysC = new String[oldSysC.length + 1];
newSysC[0] = "org.acme.webapp.ClassInJarA"; newSysC[0] = "org.acme.webapp.ClassInJarA";
System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length); System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length);
_context.setSystemClassMatcher(new ClassMatcher(newSysC)); _context.setProtectedClassMatcher(new ClassMatcher(newSysC));
assertCanLoadResource("org/acme/webapp/ClassInJarA.class"); assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
_context.setSystemClassMatcher(new ClassMatcher(oldSysC)); _context.setProtectedClassMatcher(new ClassMatcher(oldSysC));
oldServC = _context.getServerClasses(); oldServC = _context.getHiddenClasses();
newServC = new String[oldServC.length + 1]; newServC = new String[oldServC.length + 1];
newServC[0] = "org.acme.webapp.ClassInJarA"; newServC[0] = "org.acme.webapp.ClassInJarA";
System.arraycopy(oldServC, 0, newServC, 1, oldServC.length); System.arraycopy(oldServC, 0, newServC, 1, oldServC.length);
_context.setServerClassMatcher(new ClassMatcher(newServC)); _context.setHiddenClassMatcher(new ClassMatcher(newServC));
assertCanLoadResource("org/acme/webapp/ClassInJarA.class"); assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
} }
@Test @Test
public void testSystemServerClass() throws Exception public void testSystemServerClass() throws Exception
{ {
_context.getServerClassMatcher().add("org.eclipse.jetty.ee10.webapp.Configuration"); _context.getHiddenClassMatcher().add("org.eclipse.jetty.ee10.webapp.Configuration");
_context.getSystemClassMatcher().add("org.eclipse.jetty.ee10.webapp."); _context.getProtectedClassMatcher().add("org.eclipse.jetty.ee10.webapp.");
assertCanLoadClass("org.acme.webapp.ClassInJarA"); assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB"); assertCanLoadClass("org.acme.webapp.ClassInJarB");
@ -289,11 +288,11 @@ public class WebAppClassLoaderTest
assertCantLoadClass("org.eclipse.jetty.ee10.webapp.Configuration"); assertCantLoadClass("org.eclipse.jetty.ee10.webapp.Configuration");
assertCantLoadClass("org.eclipse.jetty.ee10.webapp.JarScanner"); assertCantLoadClass("org.eclipse.jetty.ee10.webapp.JarScanner");
_context.getSystemClassMatcher().add("org.acme.webapp.ClassInJarA"); _context.getProtectedClassMatcher().add("org.acme.webapp.ClassInJarA");
assertCanLoadResource("org/acme/webapp/ClassInJarA.class"); assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
_context.getSystemClassMatcher().remove("org.acme.webapp.ClassInJarA"); _context.getProtectedClassMatcher().remove("org.acme.webapp.ClassInJarA");
_context.getServerClassMatcher().add("org.acme.webapp.ClassInJarA"); _context.getHiddenClassMatcher().add("org.acme.webapp.ClassInJarA");
assertCanLoadResource("org/acme/webapp/ClassInJarA.class"); assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
} }
@ -340,11 +339,11 @@ public class WebAppClassLoaderTest
// assertEquals(0,resources.get(1).toString().indexOf("jar:file:")); // assertEquals(0,resources.get(1).toString().indexOf("jar:file:"));
// assertEquals(-1,resources.get(2).toString().indexOf("test-classes")); // assertEquals(-1,resources.get(2).toString().indexOf("test-classes"));
String[] oldServC = _context.getServerClasses(); String[] oldServC = _context.getHiddenClasses();
String[] newServC = new String[oldServC.length + 1]; String[] newServC = new String[oldServC.length + 1];
newServC[0] = "org.acme."; newServC[0] = "org.acme.";
System.arraycopy(oldServC, 0, newServC, 1, oldServC.length); System.arraycopy(oldServC, 0, newServC, 1, oldServC.length);
_context.setServerClassMatcher(new ClassMatcher(newServC)); _context.setHiddenClassMatcher(new ClassMatcher(newServC));
_context.setParentLoaderPriority(true); _context.setParentLoaderPriority(true);
// dump(_context); // dump(_context);
@ -361,12 +360,12 @@ public class WebAppClassLoaderTest
// assertEquals(0,resources.get(0).toString().indexOf("jar:file:")); // assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
// assertEquals(0,resources.get(1).toString().indexOf("file:")); // assertEquals(0,resources.get(1).toString().indexOf("file:"));
_context.setServerClassMatcher(new ClassMatcher(oldServC)); _context.setHiddenClassMatcher(new ClassMatcher(oldServC));
String[] oldSysC = _context.getSystemClasses(); String[] oldSysC = _context.getProtectedClasses();
String[] newSysC = new String[oldSysC.length + 1]; String[] newSysC = new String[oldSysC.length + 1];
newSysC[0] = "org.acme."; newSysC[0] = "org.acme.";
System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length); System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length);
_context.setSystemClassMatcher(new ClassMatcher(newSysC)); _context.setProtectedClassMatcher(new ClassMatcher(newSysC));
_context.setParentLoaderPriority(true); _context.setParentLoaderPriority(true);
// dump(_context); // dump(_context);

View File

@ -36,6 +36,7 @@ import jakarta.servlet.GenericServlet;
import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
@ -80,6 +81,7 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
@ -128,7 +130,7 @@ public class WebAppContextTest
* @param name the name of the war * @param name the name of the war
* @return the Path of the generated war * @return the Path of the generated war
* *
* @throws Exception * @throws Exception if the war could not be created
*/ */
private Path createWar(Path tempDir, String name) throws Exception private Path createWar(Path tempDir, String name) throws Exception
{ {
@ -936,4 +938,62 @@ public class WebAppContextTest
assertThat(handler.getServer(), sameInstance(server)); assertThat(handler.getServer(), sameInstance(server));
} }
@Test
public void testAddServerClasses() throws Exception
{
Server server = newServer();
String testPattern = "org.eclipse.jetty.ee10.webapp.test.";
WebAppContext.addServerClasses(server, testPattern);
WebAppContext context = new WebAppContext();
context.setContextPath("/");
Path testPath = MavenPaths.targetTestDir("testAddServerClasses");
FS.ensureDirExists(testPath);
FS.ensureEmpty(testPath);
Path warPath = createWar(testPath, "test.war");
context.setBaseResource(context.getResourceFactory().newResource(warPath));
server.setHandler(context);
server.start();
List<String> serverClasses = List.of(context.getHiddenClasses());
assertThat("Should have environment specific test pattern", serverClasses, hasItem(testPattern));
assertThat("Should have pattern from defaults", serverClasses, hasItem("org.eclipse.jetty."));
assertThat("Should have pattern from JaasConfiguration", serverClasses, hasItem("-org.eclipse.jetty.security.jaas."));
for (String defaultServerClass: WebAppClassLoading.DEFAULT_HIDDEN_CLASSES)
assertThat("Should have default patterns", serverClasses, hasItem(defaultServerClass));
}
@Test
public void testAddSystemClasses() throws Exception
{
Server server = newServer();
String testPattern = "org.eclipse.jetty.ee10.webapp.test.";
WebAppContext.addSystemClasses(server, testPattern);
WebAppContext context = new WebAppContext();
context.setContextPath("/");
Path testPath = MavenPaths.targetTestDir("testAddServerClasses");
FS.ensureDirExists(testPath);
FS.ensureEmpty(testPath);
Path warPath = createWar(testPath, "test.war");
context.setBaseResource(context.getResourceFactory().newResource(warPath));
server.setHandler(context);
server.start();
List<String> systemClasses = List.of(context.getProtectedClasses());
assertThat("Should have environment specific test pattern", systemClasses, hasItem(testPattern));
assertThat("Should have pattern from defaults", systemClasses, hasItem("javax."));
assertThat("Should have pattern from defaults", systemClasses, hasItem("jakarta."));
assertThat("Should have pattern from JaasConfiguration", systemClasses, hasItem("org.eclipse.jetty.security.jaas."));
for (String defaultSystemClass: WebAppClassLoading.DEFAULT_PROTECTED_CLASSES)
assertThat("Should have default patterns", systemClasses, hasItem(defaultSystemClass));
}
} }

View File

@ -6,7 +6,7 @@
<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.ee8.webapp.ClassMatcher"> <New class="org.eclipse.jetty.util.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

@ -18,6 +18,10 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId> <artifactId>jetty-xml</artifactId>

View File

@ -2,22 +2,21 @@
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://eclipse.dev/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call class="org.eclipse.jetty.ee8.webapp.WebAppContext" name="addSystemClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addProtectedClasses">
<Arg><Ref refid="Environment"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addSystemClasses"/></Arg> <Arg><Property name="jetty.webapp.addProtectedClasses" deprecated="jetty.webapp.addSystemClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
<Call class="org.eclipse.jetty.ee8.webapp.WebAppContext" name="addServerClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addHiddenClasses">
<Arg><Ref refid="Environment"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addServerClasses"/></Arg> <Arg><Property name="jetty.webapp.addHiddenClasses" deprecated="jetty.webapp.addServerClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
</Configure> </Configure>

View File

@ -8,6 +8,7 @@ Without this, only Jetty-specific handlers may be deployed.
ee8 ee8
[depend] [depend]
ee-webapp
ee8-servlet ee8-servlet
ee8-security ee8-security
@ -18,9 +19,9 @@ etc/jetty-ee8-webapp.xml
lib/jetty-ee8-webapp-${jetty.version}.jar lib/jetty-ee8-webapp-${jetty.version}.jar
[ini-template] [ini-template]
## Add to the server wide default jars and packages protected or hidden from webapps. ## Add to the environment wide default jars and packages protected or hidden from webapps.
## System classes are protected and cannot be overridden by a webapp. ## System (aka Protected) classes cannot be overridden by a webapp.
## Server classes are hidden and cannot be seen by a webapp ## Server (aka Hidden) classes cannot be seen by a webapp
## Lists of patterns are comma separated and may be either: ## Lists of patterns are comma separated and may be either:
## + a qualified classname e.g. 'com.acme.Foo' ## + a qualified classname e.g. 'com.acme.Foo'
## + a package name e.g. 'net.example.' ## + a package name e.g. 'net.example.'
@ -30,8 +31,8 @@ lib/jetty-ee8-webapp-${jetty.version}.jar
## ##
## The +=, operator appends to a CSV list with a comma as needed. ## The +=, operator appends to a CSV list with a comma as needed.
## ##
#jetty.webapp.addSystemClasses+=,org.example. #jetty.webapp.addProtectedClasses+=,org.example.
#jetty.webapp.addServerClasses+=,org.example. #jetty.webapp.addHiddenClasses+=,org.example.
[ini] [ini]
contextHandlerClass=org.eclipse.jetty.ee8.webapp.WebAppContext contextHandlerClass=org.eclipse.jetty.ee8.webapp.WebAppContext

View File

@ -6,7 +6,7 @@
<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.ee9.webapp.ClassMatcher"> <New class="org.eclipse.jetty.util.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

@ -232,6 +232,7 @@ public class TestOSGiUtil
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-jndi").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-jndi").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-osgi").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-osgi").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-client").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty").artifactId("jetty-ee").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-security").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-security").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-servlet").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-servlet").versionAsInProject().start());
res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-nested").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-nested").versionAsInProject().start());

View File

@ -42,7 +42,6 @@ import org.eclipse.jetty.session.SessionDataStoreFactory;
import org.eclipse.jetty.session.test.TestSessionDataStoreFactory; import org.eclipse.jetty.session.test.TestSessionDataStoreFactory;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;

View File

@ -17,6 +17,10 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-xml</artifactId> <artifactId>jetty-xml</artifactId>

View File

@ -2,22 +2,21 @@
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call class="org.eclipse.jetty.ee9.webapp.WebAppContext" name="addSystemClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addProtectedClasses">
<Arg><Ref refid="Environment"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addSystemClasses"/></Arg> <Arg><Property name="jetty.webapp.addProtectedClasses" deprecated="jetty.webapp.addSystemClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
<Call class="org.eclipse.jetty.ee9.webapp.WebAppContext" name="addServerClasses"> <Call class="org.eclipse.jetty.ee.WebAppClassLoading" name="addHiddenClasses">
<Arg><Ref refid="Environment"/></Arg> <Arg><Ref refid="Environment"/></Arg>
<Arg> <Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit"> <Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.webapp.addServerClasses"/></Arg> <Arg><Property name="jetty.webapp.addHiddenClasses" deprecated="jetty.webapp.addServerClasses"/></Arg>
</Call> </Call>
</Arg> </Arg>
</Call> </Call>
</Configure> </Configure>

View File

@ -8,6 +8,7 @@ Without this, only Jetty-specific handlers may be deployed.
ee9 ee9
[depend] [depend]
ee-webapp
ee9-servlet ee9-servlet
ee9-security ee9-security
@ -18,9 +19,9 @@ etc/jetty-ee9-webapp.xml
lib/jetty-ee9-webapp-${jetty.version}.jar lib/jetty-ee9-webapp-${jetty.version}.jar
[ini-template] [ini-template]
## Add to the server wide default jars and packages protected or hidden from webapps. ## Add to the environment wide default jars and packages protected or hidden from webapps.
## System classes are protected and cannot be overridden by a webapp. ## System (aka Protected) classes cannot be overridden by a webapp.
## Server classes are hidden and cannot be seen by a webapp ## Server (aka Hidden) classes cannot be seen by a webapp
## Lists of patterns are comma separated and may be either: ## Lists of patterns are comma separated and may be either:
## + a qualified classname e.g. 'com.acme.Foo' ## + a qualified classname e.g. 'com.acme.Foo'
## + a package name e.g. 'net.example.' ## + a package name e.g. 'net.example.'
@ -30,8 +31,8 @@ lib/jetty-ee9-webapp-${jetty.version}.jar
## ##
## The +=, operator appends to a CSV list with a comma as needed. ## The +=, operator appends to a CSV list with a comma as needed.
## ##
#jetty.webapp.addSystemClasses+=,org.example. #jetty.webapp.addProtectedClasses+=,org.example.
#jetty.webapp.addServerClasses+=,org.example. #jetty.webapp.addHiddenClasses+=,org.example.
[ini] [ini]
contextHandlerClass?=org.eclipse.jetty.ee9.webapp.WebAppContext contextHandlerClass?=org.eclipse.jetty.ee9.webapp.WebAppContext

View File

@ -32,6 +32,7 @@ module org.eclipse.jetty.ee9.webapp
requires transitive java.instrument; requires transitive java.instrument;
requires transitive org.eclipse.jetty.ee9.servlet; requires transitive org.eclipse.jetty.ee9.servlet;
requires transitive org.eclipse.jetty.xml; requires transitive org.eclipse.jetty.xml;
requires transitive org.eclipse.jetty.ee;
exports org.eclipse.jetty.ee9.webapp; exports org.eclipse.jetty.ee9.webapp;

View File

@ -13,786 +13,53 @@
package org.eclipse.jetty.ee9.webapp; package org.eclipse.jetty.ee9.webapp;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.eclipse.jetty.util.IncludeExcludeSet; import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.Index;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.URIUtil;
/** /**
* A matcher for classes based on package and/or location and/or module/ * @deprecated Use org.eclipse.jetty.util.ClassMatcher
* <p>
* Performs pattern matching of a class against a set of pattern entries.
* A class pattern is a string of one of the forms:<ul>
* <li>'org.package.SomeClass' will match a specific class
* <li>'org.package.' will match a specific package hierarchy
* <li>'org.package.SomeClass$NestedClass ' will match a nested class exactly otherwise.
* Nested classes are matched by their containing class. (eg. org.example.MyClass
* matches org.example.MyClass$AnyNestedClass)
* <li>'file:///some/location/' - A file system directory from which
* the class was loaded
* <li>'file:///some/location.jar' - The URI of a jar file from which
* the class was loaded
* <li>'jrt:/modulename' - A Java9 module name</li>
* <li>Any of the above patterns preceded by '-' will exclude rather than include the match.
* </ul>
* When class is initialized from a classpath pattern string, entries
* in this string should be separated by ':' (semicolon) or ',' (comma).
*/ */
public class ClassMatcher extends AbstractSet<String> @Deprecated(since = "12.0.8", forRemoval = true)
public class ClassMatcher extends org.eclipse.jetty.util.ClassMatcher
{ {
public static class Entry
{
private final String _pattern;
private final String _name;
private final boolean _inclusive;
protected Entry(String name, boolean inclusive)
{
_name = name;
_inclusive = inclusive;
_pattern = inclusive ? _name : ("-" + _name);
}
public String getPattern()
{
return _pattern;
}
public String getName()
{
return _name;
}
@Override
public String toString()
{
return _pattern;
}
@Override
public int hashCode()
{
return _pattern.hashCode();
}
@Override
public boolean equals(Object o)
{
return (o instanceof Entry) && _pattern.equals(((Entry)o)._pattern);
}
public boolean isInclusive()
{
return _inclusive;
}
}
private static class PackageEntry extends Entry
{
protected PackageEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class ClassEntry extends Entry
{
protected ClassEntry(String name, boolean inclusive)
{
super(name, inclusive);
}
}
private static class LocationEntry extends Entry
{
private final Path _path;
protected LocationEntry(String name, boolean inclusive)
{
super(name, inclusive);
URI uri = URI.create(name);
if (!uri.isAbsolute() && !"file".equalsIgnoreCase(uri.getScheme()))
throw new IllegalArgumentException("Not a valid file URI: " + name);
_path = Paths.get(uri);
}
public Path getPath()
{
return _path;
}
}
private static class ModuleEntry extends Entry
{
private final String _module;
protected ModuleEntry(String name, boolean inclusive)
{
super(name, inclusive);
if (!getName().startsWith("jrt:"))
throw new IllegalArgumentException(name);
_module = getName().split("/")[1];
}
public String getModule()
{
return _module;
}
}
public static class ByPackage extends AbstractSet<Entry> implements Predicate<String>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(String name)
{
return _entries.getBest(name) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean isEmpty()
{
return _entries.isEmpty();
}
@Override
public boolean add(Entry entry)
{
String name = entry.getName();
if (entry instanceof ClassEntry)
name += "$";
else if (!(entry instanceof PackageEntry))
throw new IllegalArgumentException(entry.toString());
else if (".".equals(name))
name = "";
if (_entries.get(name) != null)
return false;
return _entries.put(name, entry);
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
@Override
public void clear()
{
_entries.clear();
}
}
@SuppressWarnings("serial")
public static class ByClass extends HashSet<Entry> implements Predicate<String>
{
private final Map<String, Entry> _entries = new HashMap<>();
@Override
public boolean test(String name)
{
return _entries.containsKey(name);
}
@Override
public Iterator<Entry> iterator()
{
return _entries.values().iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ClassEntry))
throw new IllegalArgumentException(entry.toString());
return _entries.put(entry.getName(), entry) == null;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByPackageOrName extends AbstractSet<Entry> implements Predicate<String>
{
private final ByClass _byClass = new ByClass();
private final ByPackage _byPackage = new ByPackage();
@Override
public boolean test(String name)
{
return _byPackage.test(name) || _byClass.test(name);
}
@Override
public Iterator<Entry> iterator()
{
// by package contains all entries (classes are also $ packages).
return _byPackage.iterator();
}
@Override
public int size()
{
return _byPackage.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof PackageEntry)
return _byPackage.add(entry);
if (entry instanceof ClassEntry)
{
// Add class name to packages also as classes act
// as packages for nested classes.
boolean added = _byPackage.add(entry);
added = _byClass.add(entry) || added;
return added;
}
throw new IllegalArgumentException();
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof Entry))
return false;
boolean removedPackage = _byPackage.remove(o);
boolean removedClass = _byClass.remove(o);
return removedPackage || removedClass;
}
@Override
public void clear()
{
_byPackage.clear();
_byClass.clear();
}
}
@SuppressWarnings("serial")
public static class ByLocation extends HashSet<Entry> implements Predicate<URI>
{
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equals("file"))
return false;
Path path = Paths.get(uri);
for (Entry entry : this)
{
if (!(entry instanceof LocationEntry))
throw new IllegalStateException();
Path entryPath = ((LocationEntry)entry).getPath();
if (Files.isDirectory(entryPath))
{
if (path.startsWith(entryPath))
{
return true;
}
}
else
{
try
{
if (Files.isSameFile(path, entryPath))
{
return true;
}
}
catch (IOException ignore)
{
// this means there is a FileSystem issue preventing comparison.
// Use old technique
if (path.equals(entryPath))
{
return true;
}
}
}
}
return false;
}
}
@SuppressWarnings("serial")
public static class ByModule extends HashSet<Entry> implements Predicate<URI>
{
private final Index.Mutable<Entry> _entries = new Index.Builder<Entry>()
.caseSensitive(true)
.mutable()
.build();
@Override
public boolean test(URI uri)
{
if ((uri == null) || (!uri.isAbsolute()))
return false;
if (!uri.getScheme().equalsIgnoreCase("jrt"))
return false;
String module = uri.getPath();
int end = module.indexOf('/', 1);
if (end < 1)
end = module.length();
return _entries.get(module, 1, end - 1) != null;
}
@Override
public Iterator<Entry> iterator()
{
return _entries.keySet().stream().map(_entries::get).iterator();
}
@Override
public int size()
{
return _entries.size();
}
@Override
public boolean add(Entry entry)
{
if (!(entry instanceof ModuleEntry))
throw new IllegalArgumentException(entry.toString());
String module = ((ModuleEntry)entry).getModule();
if (_entries.get(module) != null)
return false;
_entries.put(module, entry);
return true;
}
@Override
public boolean remove(Object entry)
{
if (!(entry instanceof Entry))
return false;
return _entries.remove(((Entry)entry).getName()) != null;
}
}
public static class ByLocationOrModule extends AbstractSet<Entry> implements Predicate<URI>
{
private final ByLocation _byLocation = new ByLocation();
private final ByModule _byModule = new ByModule();
@Override
public boolean test(URI name)
{
if ((name == null) || (!name.isAbsolute()))
return false;
return _byLocation.test(name) || _byModule.test(name);
}
@Override
public Iterator<Entry> iterator()
{
Set<Entry> entries = new HashSet<>();
entries.addAll(_byLocation);
entries.addAll(_byModule);
return entries.iterator();
}
@Override
public int size()
{
return _byLocation.size() + _byModule.size();
}
@Override
public boolean add(Entry entry)
{
if (entry instanceof LocationEntry)
return _byLocation.add(entry);
if (entry instanceof ModuleEntry)
return _byModule.add(entry);
throw new IllegalArgumentException(entry.toString());
}
@Override
public boolean remove(Object o)
{
if (o instanceof LocationEntry)
return _byLocation.remove(o);
if (o instanceof ModuleEntry)
return _byModule.remove(o);
return false;
}
@Override
public void clear()
{
_byLocation.clear();
_byModule.clear();
}
}
Map<String, Entry> _entries = new HashMap<>();
IncludeExcludeSet<Entry, String> _patterns = new IncludeExcludeSet<>(ByPackageOrName.class);
IncludeExcludeSet<Entry, URI> _locations = new IncludeExcludeSet<>(ByLocationOrModule.class);
public ClassMatcher() public ClassMatcher()
{ {
super();
} }
@SuppressWarnings("CopyConstructorMissesField")
public ClassMatcher(ClassMatcher patterns) public ClassMatcher(ClassMatcher patterns)
{ {
if (patterns != null) super(patterns);
setAll(patterns.getPatterns()); }
public ClassMatcher(org.eclipse.jetty.util.ClassMatcher patterns)
{
super(patterns);
} }
public ClassMatcher(String... patterns) public ClassMatcher(String... patterns)
{ {
if (patterns != null && patterns.length > 0) super(patterns);
setAll(patterns);
} }
public ClassMatcher(String pattern) public ClassMatcher(String pattern)
{ {
add(pattern); super(pattern);
} }
public boolean include(String name) protected ClassMatcher(Map<String, Entry> entries, IncludeExcludeSet<Entry, String> patterns, IncludeExcludeSet<Entry, URI> locations)
{ {
if (name == null) super(entries, patterns, locations);
return false;
return add(newEntry(name, true));
}
public boolean include(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, true)) || added;
}
return added;
}
public boolean exclude(String name)
{
if (name == null)
return false;
return add(newEntry(name, false));
}
public boolean exclude(String... name)
{
boolean added = false;
for (String n : name)
{
if (n != null)
added = add(newEntry(n, false)) || added;
}
return added;
} }
@Override @Override
public boolean add(String pattern) public ClassMatcher asImmutable()
{ {
if (pattern == null) return new ClassMatcher(Map.copyOf(_entries),
return false; _patterns.asImmutable(),
return add(newEntry(pattern)); _locations.asImmutable());
}
public boolean add(String... pattern)
{
boolean added = false;
for (String p : pattern)
{
if (p != null)
added = add(newEntry(p)) || added;
}
return added;
}
protected boolean add(Entry entry)
{
if (_entries.containsKey(entry.getPattern()))
return false;
_entries.put(entry.getPattern(), entry);
if (entry instanceof LocationEntry || entry instanceof ModuleEntry)
{
if (entry.isInclusive())
_locations.include(entry);
else
_locations.exclude(entry);
}
else
{
if (entry.isInclusive())
_patterns.include(entry);
else
_patterns.exclude(entry);
}
return true;
}
protected Entry newEntry(String pattern)
{
if (pattern.startsWith("-"))
return newEntry(pattern.substring(1), false);
return newEntry(pattern, true);
}
protected Entry newEntry(String name, boolean inclusive)
{
if (name.startsWith("-"))
throw new IllegalStateException(name);
if (name.startsWith("file:"))
return new LocationEntry(name, inclusive);
if (name.startsWith("jrt:"))
return new ModuleEntry(name, inclusive);
if (name.endsWith("."))
return new PackageEntry(name, inclusive);
return new ClassEntry(name, inclusive);
}
@Override
public boolean remove(Object o)
{
if (!(o instanceof String))
return false;
String pattern = (String)o;
Entry entry = _entries.remove(pattern);
if (entry == null)
return false;
List<Entry> saved = new ArrayList<>(_entries.values());
clear();
for (Entry e : saved)
{
add(e);
}
return true;
}
@Override
public void clear()
{
_entries.clear();
_patterns.clear();
_locations.clear();
}
@Override
public Iterator<String> iterator()
{
return _entries.keySet().iterator();
}
@Override
public int size()
{
return _entries.size();
}
/**
* Initialize the matcher by parsing each classpath pattern in an array
*
* @param classes array of classpath patterns
*/
private void setAll(String[] classes)
{
_entries.clear();
addAll(classes);
}
/**
* Add array of classpath patterns.
* @param classes array of classpath patterns
*/
private void addAll(String[] classes)
{
if (classes != null)
addAll(Arrays.asList(classes));
}
/**
* @return array of classpath patterns
*/
public String[] getPatterns()
{
return toArray(new String[0]);
}
/**
* @return array of inclusive classpath patterns
*/
public String[] getInclusions()
{
return _entries.values().stream().filter(Entry::isInclusive).map(Entry::getName).toArray(String[]::new);
}
/**
* @return array of excluded classpath patterns (without '-' prefix)
*/
public String[] getExclusions()
{
return _entries.values().stream().filter(e -> !e.isInclusive()).map(Entry::getName).toArray(String[]::new);
}
/**
* Match the class name against the pattern
*
* @param name name of the class to match
* @return true if class matches the pattern
*/
public boolean match(String name)
{
return _patterns.test(name);
}
/**
* Match the class name against the pattern
*
* @param clazz A class to try to match
* @return true if class matches the pattern
*/
public boolean match(Class<?> clazz)
{
try
{
return combine(_patterns, clazz.getName(), _locations, () -> TypeUtil.getLocationOfClass(clazz));
}
catch (Exception ignored)
{
}
return false;
}
public boolean match(String name, URL url)
{
if (url == null)
return false;
// Strip class suffix for name matching
if (name.endsWith(".class"))
name = name.substring(0, name.length() - 6);
// Treat path elements as packages for name matching
name = StringUtil.replace(name, '/', '.');
return combine(_patterns, name, _locations, () ->
{
try
{
return URIUtil.unwrapContainer(url.toURI());
}
catch (URISyntaxException ignored)
{
return null;
}
});
}
/**
* Match a class against inclusions and exclusions by name and location.
* Name based checks are performed before location checks. For a class to match,
* it must not be excluded by either name or location, and must either be explicitly
* included, or for there to be no inclusions. In the case where the location
* of the class is null, it will match if it is included by name, or
* if there are no location exclusions.
*
* @param names configured inclusions and exclusions by name
* @param name the name to check
* @param locations configured inclusions and exclusions by location
* @param location the location of the class (can be null)
* @return true if the class is not excluded but is included, or there are
* no inclusions. False otherwise.
*/
static boolean combine(IncludeExcludeSet<Entry, String> names, String name, IncludeExcludeSet<Entry, URI> locations, Supplier<URI> location)
{
// check the name set
Boolean byName = names.isIncludedAndNotExcluded(name);
// If we excluded by name, then no match
if (Boolean.FALSE == byName)
return false;
// check the location set
URI uri = location.get();
Boolean byLocation = uri == null ? null : locations.isIncludedAndNotExcluded(uri);
// If we excluded by location or couldn't check location exclusion, then no match
if (Boolean.FALSE == byLocation || (locations.hasExcludes() && uri == null))
return false;
// If there are includes, then we must be included to match.
if (names.hasIncludes() || locations.hasIncludes())
return byName == Boolean.TRUE || byLocation == Boolean.TRUE;
// Otherwise there are no includes and it was not excluded, so match
return true;
} }
} }

View File

@ -60,8 +60,8 @@ import org.slf4j.LoggerFactory;
* parent loader. Java2 compliant loading, where the parent loader * parent loader. Java2 compliant loading, where the parent loader
* always has priority, can be selected with the * always has priority, can be selected with the
* {@link WebAppContext#setParentLoaderPriority(boolean)} * {@link WebAppContext#setParentLoaderPriority(boolean)}
* method and influenced with {@link WebAppContext#isServerClass(Class)} and * method and influenced with {@link WebAppContext#isHiddenClass(Class)} and
* {@link WebAppContext#isSystemClass(Class)}. * {@link WebAppContext#isProtectedClass(Class)}.
* <p> * <p>
* If no parent class loader is provided, then the current thread * If no parent class loader is provided, then the current thread
* context classloader will be used. If that is null then the * context classloader will be used. If that is null then the
@ -429,7 +429,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
throw new ClassNotFoundException("Bad ClassLoader: returned null for loadClass(" + name + ")"); throw new ClassNotFoundException("Bad ClassLoader: returned null for loadClass(" + name + ")");
// If the webapp is allowed to see this class // If the webapp is allowed to see this class
if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parentClass)) if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isHiddenClass(parentClass))
{ {
return parentClass; return parentClass;
} }
@ -477,7 +477,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
{ {
parentClass = _parent.loadClass(name); parentClass = _parent.loadClass(name);
// If the webapp is allowed to see this class // If the webapp is allowed to see this class
if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isServerClass(parentClass)) if (Boolean.TRUE.equals(__loadServerClasses.get()) || !_context.isHiddenClass(parentClass))
{ {
return parentClass; return parentClass;
} }
@ -609,14 +609,14 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility
} }
@Override @Override
public boolean isSystemClass(Class<?> clazz) public boolean isProtectedClass(Class<?> clazz)
{ {
return _context.isSystemClass(clazz); return _context.isProtectedClass(clazz);
} }
@Override @Override
public boolean isServerClass(Class<?> clazz) public boolean isHiddenClass(Class<?> clazz)
{ {
return _context.isServerClass(clazz); return _context.isHiddenClass(clazz);
} }
} }

View File

@ -38,6 +38,7 @@ import jakarta.servlet.http.HttpSessionAttributeListener;
import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionBindingListener;
import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionIdListener;
import jakarta.servlet.http.HttpSessionListener; import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.ee9.nested.ContextHandler; import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.nested.ErrorHandler; import org.eclipse.jetty.ee9.nested.ErrorHandler;
import org.eclipse.jetty.ee9.nested.HandlerWrapper; import org.eclipse.jetty.ee9.nested.HandlerWrapper;
@ -54,6 +55,7 @@ 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;
@ -87,32 +89,35 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee9/webapp/webdefault-ee9.xml"; public static final String WEB_DEFAULTS_XML = "org/eclipse/jetty/ee9/webapp/webdefault-ee9.xml";
public static final String ERROR_PAGE = "org.eclipse.jetty.server.error_page"; public static final String ERROR_PAGE = "org.eclipse.jetty.server.error_page";
public static final String SERVER_SYS_CLASSES = "org.eclipse.jetty.ee9.webapp.systemClasses"; /**
public static final String SERVER_SRV_CLASSES = "org.eclipse.jetty.ee9.webapp.serverClasses"; * @deprecated use {@link WebAppClassLoading#PROTECTED_CLASSES_ATTRIBUTE} instead.
*/
@Deprecated(forRemoval = true, since = "12.0.9")
public static final String SERVER_SYS_CLASSES = WebAppClassLoading.PROTECTED_CLASSES_ATTRIBUTE;
/**
* @deprecated use {@link WebAppClassLoading#HIDDEN_CLASSES_ATTRIBUTE} instead.
*/
@Deprecated(forRemoval = true, since = "12.0.9")
public static final String SERVER_SRV_CLASSES = WebAppClassLoading.HIDDEN_CLASSES_ATTRIBUTE;
private static String[] __dftProtectedTargets = {"/WEB-INF", "/META-INF"}; private static final String[] __dftProtectedTargets = {"/WEB-INF", "/META-INF"};
// System classes are classes that cannot be replaced by /**
// the web application, and they are *always* loaded via * @deprecated use {@link WebAppClassLoading#DEFAULT_PROTECTED_CLASSES}
// system classloader. */
public static final ClassMatcher __dftSystemClasses = new ClassMatcher( @Deprecated(forRemoval = true, since = "12.0.9")
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) public static final org.eclipse.jetty.ee9.webapp.ClassMatcher __dftSystemClasses =
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) new org.eclipse.jetty.ee9.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES);
"jakarta.", // Jakarta classes (per servlet spec v5.0 / Section 15.2.1)
"org.xml.", // javax.xml
"org.w3c." // javax.xml
);
// Server classes are classes that are hidden from being /**
// loaded by the web application using system classloader, * @deprecated use {@link WebAppClassLoading#DEFAULT_HIDDEN_CLASSES}
// so if web application needs to load any of such classes, */
// it has to include them in its distribution. @Deprecated(forRemoval = true, since = "12.0.9")
public static final ClassMatcher __dftServerClasses = new ClassMatcher( public static final org.eclipse.jetty.ee9.webapp.ClassMatcher __dftServerClasses =
"org.eclipse.jetty." // hide jetty classes new org.eclipse.jetty.ee9.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES);
);
private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses); private final ClassMatcher _systemClasses = new ClassMatcher(WebAppClassLoading.getProtectedClasses(ServletContextHandler.ENVIRONMENT));
private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses); private final ClassMatcher _serverClasses = new ClassMatcher(WebAppClassLoading.getHiddenClasses(ServletContextHandler.ENVIRONMENT));
private Configurations _configurations; private Configurations _configurations;
private String _defaultsDescriptor = WEB_DEFAULTS_XML; private String _defaultsDescriptor = WEB_DEFAULTS_XML;
@ -746,13 +751,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
} }
@Override @Override
public boolean isServerClass(Class<?> clazz) public boolean isHiddenClass(Class<?> clazz)
{ {
return _serverClasses.match(clazz); return _serverClasses.match(clazz);
} }
@Override @Override
public boolean isSystemClass(Class<?> clazz) public boolean isProtectedClass(Class<?> clazz)
{ {
return _systemClasses.match(clazz); return _systemClasses.match(clazz);
} }
@ -775,23 +780,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
super.setServer(server); super.setServer(server);
if (server != null) if (server != null)
{ {
if (__dftSystemClasses.equals(_systemClasses)) _systemClasses.add(WebAppClassLoading.getProtectedClasses(server).getPatterns());
{ _serverClasses.add(WebAppClassLoading.getHiddenClasses(server).getPatterns());
Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
if (systemClasses instanceof String[])
systemClasses = new ClassMatcher((String[])systemClasses);
if (systemClasses instanceof ClassMatcher)
_systemClasses.add(((ClassMatcher)systemClasses).getPatterns());
}
if (__dftServerClasses.equals(_serverClasses))
{
Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
if (serverClasses instanceof String[])
serverClasses = new ClassMatcher((String[])serverClasses);
if (serverClasses instanceof ClassMatcher)
_serverClasses.add(((ClassMatcher)serverClasses).getPatterns());
}
} }
} }
@ -1486,38 +1476,31 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
return _metadata; return _metadata;
} }
public static void addServerClasses(Attributes attributes, String... pattern) /**
* Add a Server Class pattern to use for all ee9 WebAppContexts.
* @param attributes The {@link Server} instance to add classes to
* @param patterns the patterns to use
* @see #getServerClassMatcher()
* @see #getServerClasses()
* @deprecated use {@link WebAppClassLoading#addProtectedClasses(Server, String...)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public static void addServerClasses(Attributes attributes, String... patterns)
{ {
addClasses(__dftServerClasses, SERVER_SRV_CLASSES, attributes, pattern); WebAppClassLoading.addHiddenClasses(attributes, patterns);
} }
public static void addSystemClasses(Attributes attributes, String... pattern) /**
* Add a System Class pattern to use for all ee9 WebAppContexts.
* @param attributes The {@link Server} instance to add classes to
* @param patterns the patterns to use
* @see #getSystemClassMatcher()
* @see #getSystemClasses()
* @deprecated use {@link WebAppClassLoading#addHiddenClasses(Server, String...)}
*/
@Deprecated(since = "12.0.8", forRemoval = true)
public static void addSystemClasses(Attributes attributes, String... patterns)
{ {
addClasses(__dftSystemClasses, SERVER_SYS_CLASSES, attributes, pattern); WebAppClassLoading.addProtectedClasses(attributes, patterns);
}
private static void addClasses(ClassMatcher matcher, String attribute, Attributes attributes, String... pattern)
{
if (pattern == null || pattern.length == 0)
return;
// look for a Server attribute with the list of System classes
// to apply to every web application. If not present, use our defaults.
Object o = attributes.getAttribute(attribute);
if (o instanceof ClassMatcher)
{
((ClassMatcher)o).add(pattern);
return;
}
String[] classes;
if (o instanceof String[])
classes = (String[])o;
else
classes = matcher.getPatterns();
int l = classes.length;
classes = Arrays.copyOf(classes, l + pattern.length);
System.arraycopy(pattern, 0, classes, l, pattern.length);
attributes.setAttribute(attribute, classes);
} }
} }

View File

@ -1,307 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.ee9.webapp;
import java.net.URI;
import java.util.Arrays;
import java.util.function.Supplier;
import org.eclipse.jetty.ee9.webapp.ClassMatcher.ByLocationOrModule;
import org.eclipse.jetty.ee9.webapp.ClassMatcher.ByPackageOrName;
import org.eclipse.jetty.ee9.webapp.ClassMatcher.Entry;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.TypeUtil;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ClassMatcherTest
{
private final ClassMatcher _pattern = new ClassMatcher();
protected static Supplier<URI> NULL_SUPPLIER = () -> null;
@BeforeEach
public void before()
{
_pattern.clear();
_pattern.add("org.package.");
_pattern.add("-org.excluded.");
_pattern.add("org.example.FooBar");
_pattern.add("-org.example.Excluded");
_pattern.addAll(Arrays.asList(
"-org.example.Nested$Minus",
"org.example.Nested",
"org.example.Nested$Something"));
assertThat(_pattern, Matchers.containsInAnyOrder(
"org.package.",
"-org.excluded.",
"org.example.FooBar",
"-org.example.Excluded",
"-org.example.Nested$Minus",
"org.example.Nested",
"org.example.Nested$Something"
));
}
@Test
public void testClassMatch()
{
assertTrue(_pattern.match("org.example.FooBar"));
assertTrue(_pattern.match("org.example.Nested"));
assertFalse(_pattern.match("org.example.Unknown"));
assertFalse(_pattern.match("org.example.FooBar.Unknown"));
}
@Test
public void testPackageMatch()
{
assertTrue(_pattern.match("org.package.Something"));
assertTrue(_pattern.match("org.package.other.Something"));
assertFalse(_pattern.match("org.example.Unknown"));
assertFalse(_pattern.match("org.example.FooBar.Unknown"));
assertFalse(_pattern.match("org.example.FooBarElse"));
}
@Test
public void testExplicitNestedMatch()
{
assertTrue(_pattern.match("org.example.Nested$Something"));
assertFalse(_pattern.match("org.example.Nested$Minus"));
assertTrue(_pattern.match("org.example.Nested$Other"));
}
@Test
public void testImplicitNestedMatch()
{
assertTrue(_pattern.match("org.example.FooBar$Other"));
assertTrue(_pattern.match("org.example.Nested$Other"));
}
@Test
public void testDoubledNested()
{
assertTrue(_pattern.match("org.example.Nested$Something$Else"));
assertFalse(_pattern.match("org.example.Nested$Minus$Else"));
}
@Test
public void testMatchAll()
{
_pattern.clear();
_pattern.add(".");
assertTrue(_pattern.match("org.example.Anything"));
assertTrue(_pattern.match("org.example.Anything$Else"));
}
@Test
public void testCopy()
{
ClassMatcher copy = new ClassMatcher(_pattern);
assertThat(copy.toString(), is(_pattern.toString()));
}
@Test
public void testMatchFundamentalExcludeSpecific()
{
_pattern.clear();
_pattern.add("jakarta.");
_pattern.add("-jakarta.ws.rs.", "-jakarta.inject.");
assertFalse(_pattern.match("org.example.Anything"));
assertTrue(_pattern.match("jakarta.servlet.HttpServlet"));
assertFalse(_pattern.match("jakarta.ws.rs.ProcessingException"));
}
@SuppressWarnings("restriction")
@Test
public void testIncludedLocations() throws Exception
{
// jar from JVM classloader
URI locString = TypeUtil.getLocationOfClass(String.class);
// a jar from maven repo jar
URI locJunit = TypeUtil.getLocationOfClass(Test.class);
// class file
URI locTest = TypeUtil.getLocationOfClass(ClassMatcherTest.class);
ClassMatcher pattern = new ClassMatcher();
pattern.include("something");
assertThat(pattern.match(String.class), is(false));
assertThat(pattern.match(Test.class), is(false));
assertThat(pattern.match(ClassMatcherTest.class), is(false));
// Add directory for both JVM classes
pattern.include(locString.toASCIIString());
// Add jar for individual class and classes directory
pattern.include(locJunit.toString(), locTest.toString());
assertThat(pattern.match(String.class), is(true));
assertThat(pattern.match(Test.class), is(true));
assertThat(pattern.match(ClassMatcherTest.class), is(true));
pattern.add("-java.lang.String");
assertThat(pattern.match(String.class), is(false));
assertThat(pattern.match(Test.class), is(true));
assertThat(pattern.match(ClassMatcherTest.class), is(true));
}
@SuppressWarnings("restriction")
@Test
public void testIncludedLocationsOrModule() throws Exception
{
// jar from JVM classloader
URI modString = TypeUtil.getLocationOfClass(String.class);
// System.err.println(modString);
// a jar from maven repo jar
URI locJunit = TypeUtil.getLocationOfClass(Test.class);
// System.err.println(locJunit);
// class file
URI locTest = TypeUtil.getLocationOfClass(ClassMatcherTest.class);
// System.err.println(locTest);
ClassMatcher pattern = new ClassMatcher();
pattern.include("something");
assertThat(pattern.match(String.class), is(false));
assertThat(pattern.match(Test.class), is(false));
assertThat(pattern.match(ClassMatcherTest.class), is(false));
// Add module for all JVM base classes
pattern.include("jrt:/java.base");
// Add jar for individual class and classes directory
pattern.include(locJunit.toString(), locTest.toString());
assertThat(pattern.match(String.class), is(true));
assertThat(pattern.match(Test.class), is(true));
assertThat(pattern.match(ClassMatcherTest.class), is(true));
pattern.add("-java.lang.String");
assertThat(pattern.match(String.class), is(false));
assertThat(pattern.match(Test.class), is(true));
assertThat(pattern.match(ClassMatcherTest.class), is(true));
}
@SuppressWarnings("restriction")
@Test
public void testExcludeLocationsOrModule() throws Exception
{
// jar from JVM classloader
URI modString = TypeUtil.getLocationOfClass(String.class);
assertNotNull(modString);
// a jar from maven repo jar
URI locJunit = TypeUtil.getLocationOfClass(Test.class);
assertNotNull(locJunit);
// class file
URI locTest = TypeUtil.getLocationOfClass(ClassMatcherTest.class);
assertNotNull(locTest);
ClassMatcher pattern = new ClassMatcher();
// include everything
pattern.include(".");
assertThat(pattern.match(String.class), is(true));
assertThat(pattern.match(Test.class), is(true));
assertThat(pattern.match(ClassMatcherTest.class), is(true));
// Add directory for both JVM classes
pattern.exclude("jrt:/java.base/");
// Add jar for individual class and classes directory
pattern.exclude(locJunit.toASCIIString(), locTest.toASCIIString());
assertThat(pattern.match(String.class), is(false));
assertThat(pattern.match(Test.class), is(false));
assertThat(pattern.match(ClassMatcherTest.class), is(false));
}
@Test
public void testWithNullLocation() throws Exception
{
ClassMatcher matcher = new ClassMatcher();
IncludeExcludeSet<Entry, String> names = new IncludeExcludeSet<>(ByPackageOrName.class);
IncludeExcludeSet<Entry, URI> locations = new IncludeExcludeSet<>(ByLocationOrModule.class);
//Test no name or location includes or excludes - should match
assertThat(ClassMatcher.combine(names, "a.b.c", locations, NULL_SUPPLIER), is(true));
names.include(matcher.newEntry("a.b.", true));
names.exclude(matcher.newEntry("d.e.", false));
//Test explicit include by name no locations - should match
assertThat(ClassMatcher.combine(names, "a.b.c", locations, NULL_SUPPLIER), is(true));
//Test explicit exclude by name no locations - should not match
assertThat(ClassMatcher.combine(names, "d.e.f", locations, NULL_SUPPLIER), is(false));
//Test include by name with location includes - should match
locations.include(matcher.newEntry("file:/foo/bar", true));
assertThat(ClassMatcher.combine(names, "a.b.c", locations, NULL_SUPPLIER), is(true));
//Test include by name but with location exclusions - should not match
locations.clear();
locations.exclude(matcher.newEntry("file:/high/low", false));
assertThat(ClassMatcher.combine(names, "a.b.c", locations, NULL_SUPPLIER), is(false));
//Test neither included or excluded by name, but with location exclusions - should not match
assertThat(ClassMatcher.combine(names, "g.b.r", locations, NULL_SUPPLIER), is(false));
//Test neither included nor excluded by name, but with location inclusions - should not match
locations.clear();
locations.include(matcher.newEntry("file:/foo/bar", true));
assertThat(ClassMatcher.combine(names, "g.b.r", locations, NULL_SUPPLIER), is(false));
}
@Test
public void testLarge()
{
ClassMatcher pattern = new ClassMatcher();
for (int i = 0; i < 500; i++)
{
assertTrue(pattern.add("n" + i + "." + Integer.toHexString(100 + i) + ".Name"));
}
for (int i = 0; i < 500; i++)
{
assertTrue(pattern.match("n" + i + "." + Integer.toHexString(100 + i) + ".Name"));
}
}
@Test
public void testJvmModule()
{
URI uri = TypeUtil.getLocationOfClass(String.class);
assertThat(uri, notNullValue());
assertThat(uri.getScheme(), is("jrt"));
assertThat(uri.getPath(), is("/java.base"));
}
}

View File

@ -26,9 +26,9 @@ 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.FileSystemPool;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -37,7 +37,6 @@ import org.junit.jupiter.api.Test;
import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered; import static org.eclipse.jetty.toolchain.test.ExtraMatchers.ordered;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.nullValue;

View File

@ -34,6 +34,7 @@ import jakarta.servlet.GenericServlet;
import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import jakarta.servlet.ServletResponse;
import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.ee9.nested.ContextHandler; import org.eclipse.jetty.ee9.nested.ContextHandler;
import org.eclipse.jetty.ee9.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.ee9.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.ee9.servlet.ServletContextHandler; import org.eclipse.jetty.ee9.servlet.ServletContextHandler;
@ -79,6 +80,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
@ -116,7 +118,7 @@ public class WebAppContextTest
* @param name the name of the war * @param name the name of the war
* @return the Path of the generated war * @return the Path of the generated war
* *
* @throws Exception * @throws Exception if the war cannot be created
*/ */
private Path createWar(Path tempDir, String name) throws Exception private Path createWar(Path tempDir, String name) throws Exception
{ {
@ -847,4 +849,63 @@ public class WebAppContextTest
extLibs = extLibs.toAbsolutePath(); extLibs = extLibs.toAbsolutePath();
assertThat("URL[0]", urls[0].toURI(), is(extLibs.toUri())); assertThat("URL[0]", urls[0].toURI(), is(extLibs.toUri()));
} }
@Test
public void testAddServerClasses() throws Exception
{
Server server = newServer();
String testPattern = "org.eclipse.jetty.ee9.webapp.test.";
WebAppContext.addServerClasses(server, testPattern);
WebAppContext context = new WebAppContext();
context.setContextPath("/");
Path testPath = MavenPaths.targetTestDir("testAddServerClasses");
FS.ensureDirExists(testPath);
FS.ensureEmpty(testPath);
Path warPath = createWar(testPath, "test.war");
context.setBaseResource(context.getResourceFactory().newResource(warPath));
server.setHandler(context);
server.start();
List<String> serverClasses = List.of(context.getServerClasses());
assertThat("Should have environment specific test pattern", serverClasses, hasItem(testPattern));
assertThat("Should have pattern from defaults", serverClasses, hasItem("org.eclipse.jetty."));
assertThat("Should have pattern from JaasConfiguration", serverClasses, hasItem("-org.eclipse.jetty.security.jaas."));
for (String defaultServerClass: WebAppClassLoading.DEFAULT_HIDDEN_CLASSES)
assertThat("Should have default patterns", serverClasses, hasItem(defaultServerClass));
}
@Test
public void testAddSystemClasses() throws Exception
{
Server server = newServer();
String testPattern = "org.eclipse.jetty.ee9.webapp.test.";
WebAppContext.addSystemClasses(server, testPattern);
WebAppContext context = new WebAppContext();
context.setContextPath("/");
Path testPath = MavenPaths.targetTestDir("testAddServerClasses");
FS.ensureDirExists(testPath);
FS.ensureEmpty(testPath);
Path warPath = createWar(testPath, "test.war");
context.setBaseResource(context.getResourceFactory().newResource(warPath));
server.setHandler(context);
server.start();
List<String> systemClasses = List.of(context.getSystemClasses());
assertThat("Should have environment specific test pattern", systemClasses, hasItem(testPattern));
assertThat("Should have pattern from defaults", systemClasses, hasItem("javax."));
assertThat("Should have pattern from defaults", systemClasses, hasItem("jakarta."));
assertThat("Should have pattern from JaasConfiguration", systemClasses, hasItem("org.eclipse.jetty.security.jaas."));
for (String defaultSystemClass : WebAppClassLoading.DEFAULT_PROTECTED_CLASSES)
{
assertThat("Should have default patterns", systemClasses, hasItem(defaultSystemClass));
}
}
} }

View File

@ -39,6 +39,10 @@
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId> <artifactId>jetty-deploy</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-ee</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-hazelcast</artifactId> <artifactId>jetty-hazelcast</artifactId>

View File

@ -21,5 +21,5 @@ basehome:modules/logging/jetty
lib/logging/jetty-slf4j-impl-${jetty.version}.jar lib/logging/jetty-slf4j-impl-${jetty.version}.jar
[ini] [ini]
jetty.webapp.addServerClasses+=,org.eclipse.jetty.logging. jetty.webapp.addHiddenClasses+=,org.eclipse.jetty.logging.
jetty.webapp.addServerClasses+=,${jetty.home.uri}/lib/logging/ jetty.webapp.addHiddenClasses+=,${jetty.home.uri}/lib/logging/

View File

@ -31,7 +31,7 @@ lib/logging/log4j-${log4j.version}.jar
[ini] [ini]
log4j.version?=1.2.17 log4j.version?=1.2.17
jetty.webapp.addServerClasses+=,org.apache.log4j. jetty.webapp.addHiddenClasses+=,org.apache.log4j.
[license] [license]

View File

@ -28,7 +28,7 @@ lib/logging/log4j-core-${log4j2.version}.jar
[ini] [ini]
log4j2.version?=@log4j2.version@ log4j2.version?=@log4j2.version@
jetty.webapp.addServerClasses+=,org.apache.logging.log4j. jetty.webapp.addHiddenClasses+=,org.apache.logging.log4j.
[license] [license]
Log4j is released under the Apache 2.0 license. Log4j is released under the Apache 2.0 license.

View File

@ -25,7 +25,7 @@ lib/logging/logback-core-${logback.version}.jar
[ini] [ini]
logback.version?=@logback.version@ logback.version?=@logback.version@
jetty.webapp.addServerClasses+=,ch.qos.logback. jetty.webapp.addHiddenClasses+=,ch.qos.logback.
[license] [license]
Logback: the reliable, generic, fast and flexible logging framework. Logback: the reliable, generic, fast and flexible logging framework.

View File

@ -16,4 +16,4 @@ lib/logging/slf4j-api-${slf4j.version}.jar
[ini] [ini]
slf4j.version?=@slf4j.version@ slf4j.version?=@slf4j.version@
jetty.webapp.addServerClasses+=,org.slf4j. jetty.webapp.addHiddenClasses+=,org.slf4j.

View File

@ -17,4 +17,4 @@ http://www.apache.org/licenses/LICENSE-2.0.html
[ini] [ini]
## Hide the gcloud libraries from deployed webapps ## Hide the gcloud libraries from deployed webapps
jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/gcloud/ jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/gcloud/

View File

@ -10,4 +10,4 @@ internal
[ini] [ini]
## Hide the infinispan libraries from deployed webapps ## Hide the infinispan libraries from deployed webapps
jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/infinispan/

View File

@ -9,4 +9,4 @@ infinispan
[ini] [ini]
## Hide the infinispan libraries from deployed webapps ## Hide the infinispan libraries from deployed webapps
jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/infinispan/

View File

@ -101,7 +101,7 @@ public class DemoModulesTests extends AbstractJettyHomeTest
@ParameterizedTest @ParameterizedTest
@MethodSource("provideEnvironmentsToTest") @MethodSource("provideEnvironmentsToTest")
public void testDemoAddServerClasses(String env) throws Exception public void testDemoAddHiddenClasses(String env) throws Exception
{ {
Path jettyBase = newTestJettyBaseDirectory(); Path jettyBase = newTestJettyBaseDirectory();
String jettyVersion = System.getProperty("jettyVersion"); String jettyVersion = System.getProperty("jettyVersion");
@ -124,14 +124,14 @@ public class DemoModulesTests extends AbstractJettyHomeTest
assertTrue(runListConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); assertTrue(runListConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, runListConfig.getExitValue(), "Exit value"); assertEquals(0, runListConfig.getExitValue(), "Exit value");
// Example of what we expect // Example of what we expect
// jetty.webapp.addServerClasses = org.eclipse.jetty.logging.,${jetty.home.uri}/lib/logging/,org.slf4j.,${jetty.base.uri}/lib/bouncycastle/ // jetty.webapp.addHiddenClasses = org.eclipse.jetty.logging.,${jetty.home.uri}/lib/logging/,org.slf4j.,${jetty.base.uri}/lib/bouncycastle/
String addServerKey = " jetty.webapp.addServerClasses = "; String addServerKey = " jetty.webapp.addHiddenClasses = ";
String addServerClasses = runListConfig.getLogs().stream() String addServerClasses = runListConfig.getLogs().stream()
.filter(s -> s.startsWith(addServerKey)) .filter(s -> s.startsWith(addServerKey))
.findFirst() .findFirst()
.orElseThrow(() -> .orElseThrow(() ->
new NoSuchElementException("Unable to find [" + addServerKey + "]")); new NoSuchElementException("Unable to find [" + addServerKey + "]"));
assertThat("'jetty.webapp.addServerClasses' entry count", assertThat("'jetty.webapp.addHiddenClasses' entry count",
addServerClasses.split(",").length, addServerClasses.split(",").length,
greaterThan(1)); greaterThan(1));
} }