diff --git a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/modules.adoc b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/modules.adoc index a9207b77b63..03ef41e8645 100644 --- a/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/modules.adoc +++ b/documentation/jetty-documentation/src/main/asciidoc/operations-guide/modules/modules.adoc @@ -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: + ---- -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. diff --git a/jetty-core/jetty-ee/pom.xml b/jetty-core/jetty-ee/pom.xml new file mode 100644 index 00000000000..8ff96f24bd7 --- /dev/null +++ b/jetty-core/jetty-ee/pom.xml @@ -0,0 +1,61 @@ + + + + 4.0.0 + + org.eclipse.jetty + jetty-core + 12.0.9-SNAPSHOT + + jetty-ee + Core :: EE Common + + + ${project.groupId}.ee + org.eclipse.jetty.ee.* + + + + + org.eclipse.jetty + jetty-server + + + org.slf4j + slf4j-api + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + org.eclipse.jetty.tests + jetty-test-multipart + ${project.version} + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + org.apache.maven.plugins + maven-surefire-plugin + + @{argLine} ${jetty.surefire.argLine} --add-reads org.eclipse.jetty.ee=org.eclipse.jetty.logging + + + + + diff --git a/jetty-core/jetty-ee/src/main/config/etc/jetty-ee-webapp.xml b/jetty-core/jetty-ee/src/main/config/etc/jetty-ee-webapp.xml new file mode 100644 index 00000000000..0ba8f34ec72 --- /dev/null +++ b/jetty-core/jetty-ee/src/main/config/etc/jetty-ee-webapp.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jetty-core/jetty-ee/src/main/config/modules/ee-webapp.mod b/jetty-core/jetty-ee/src/main/config/modules/ee-webapp.mod new file mode 100644 index 00000000000..db2420cf4f2 --- /dev/null +++ b/jetty-core/jetty-ee/src/main/config/modules/ee-webapp.mod @@ -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[] + diff --git a/jetty-core/jetty-ee/src/main/java/module-info.java b/jetty-core/jetty-ee/src/main/java/module-info.java new file mode 100644 index 00000000000..90f777dc572 --- /dev/null +++ b/jetty-core/jetty-ee/src/main/java/module-info.java @@ -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; +} diff --git a/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/WebAppClassLoading.java b/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/WebAppClassLoading.java new file mode 100644 index 00000000000..eaf13e52a64 --- /dev/null +++ b/jetty-core/jetty-ee/src/main/java/org/eclipse/jetty/ee/WebAppClassLoading.java @@ -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: + * + *

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. + * + *

+ * 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. + *

+ *

+ * 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. + *

+ *

+ * 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. + *

+ */ +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; + } + +} diff --git a/jetty-core/jetty-ee/src/test/java/orge/eclipse/jetty/ee/WebAppClassLoadingTest.java b/jetty-core/jetty-ee/src/test/java/orge/eclipse/jetty/ee/WebAppClassLoadingTest.java new file mode 100644 index 00000000000..cf429998606 --- /dev/null +++ b/jetty-core/jetty-ee/src/test/java/orge/eclipse/jetty/ee/WebAppClassLoadingTest.java @@ -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)); + } + +} diff --git a/jetty-core/jetty-keystore/src/main/config/modules/test-keystore.mod b/jetty-core/jetty-keystore/src/main/config/modules/test-keystore.mod index 0ff1996dbfe..1c1b98fb3dc 100644 --- a/jetty-core/jetty-keystore/src/main/config/modules/test-keystore.mod +++ b/jetty-core/jetty-keystore/src/main/config/modules/test-keystore.mod @@ -25,7 +25,7 @@ etc/jetty-test-keystore.xml [ini] 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.keyStoreType?=PKCS12 jetty.sslContext.keyStorePassword?=OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4 diff --git a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionData.java b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionData.java index a5445c76cca..c33682cb432 100644 --- a/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionData.java +++ b/jetty-core/jetty-session/src/main/java/org/eclipse/jetty/session/SessionData.java @@ -89,7 +89,7 @@ public class SessionData implements Serializable //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). ClassVisibilityChecker checker = (ClassVisibilityChecker)(contextLoader); - isContextLoader = (checker.isSystemClass(clazz) && !(checker.isServerClass(clazz))); + isContextLoader = (checker.isProtectedClass(clazz) && !(checker.isHiddenClass(clazz))); } else { diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassMatcher.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassMatcher.java new file mode 100644 index 00000000000..2fe9fe754c8 --- /dev/null +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassMatcher.java @@ -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/ + *

+ * Performs pattern matching of a class against a set of pattern entries. + * A class pattern is a string of one of the forms:

+ * 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 +{ + 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 implements Predicate + { + private final Index.Mutable _entries = new Index.Builder() + .caseSensitive(true) + .mutable() + .build(); + + @Override + public boolean test(String name) + { + return _entries.getBest(name) != null; + } + + @Override + public Iterator 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 implements Predicate + { + private final Map _entries = new HashMap<>(); + + @Override + public boolean test(String name) + { + return _entries.containsKey(name); + } + + @Override + public Iterator 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 implements Predicate + { + 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 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 implements Predicate + { + @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 implements Predicate + { + private final Index.Mutable _entries = new Index.Builder() + .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 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 implements Predicate + { + 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 iterator() + { + Set 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 _entries; + protected final IncludeExcludeSet _patterns; + protected final IncludeExcludeSet _locations; + + protected ClassMatcher(Map entries, IncludeExcludeSet patterns, IncludeExcludeSet locations) + { + _entries = entries; + _patterns = patterns == null ? new IncludeExcludeSet<>(ByPackageOrName.class) : patterns; + _locations = locations == null ? new IncludeExcludeSet<>(ByLocationOrModule.class) : locations; + } + + private ClassMatcher(Map 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 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 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 names, String name, IncludeExcludeSet locations, Supplier 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; + } +} diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassVisibilityChecker.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassVisibilityChecker.java index 3167d9b82d3..12bfaec2322 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassVisibilityChecker.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/ClassVisibilityChecker.java @@ -14,13 +14,33 @@ package org.eclipse.jetty.util; /** - * ClassVisibilityChecker - * * Interface to be implemented by classes capable of checking class visibility * for a context. */ 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. @@ -30,8 +50,13 @@ public interface ClassVisibilityChecker * * @param clazz The fully qualified name of the 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. @@ -42,6 +67,11 @@ public interface ClassVisibilityChecker * * @param clazz The fully qualified name of the 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); + } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ClassMatcherTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ClassMatcherTest.java similarity index 97% rename from jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ClassMatcherTest.java rename to jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ClassMatcherTest.java index 069b4df78ab..e29c994f011 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/ClassMatcherTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/ClassMatcherTest.java @@ -11,17 +11,15 @@ // ======================================================================== // -package org.eclipse.jetty.ee10.webapp; +package org.eclipse.jetty.util; import java.net.URI; import java.util.Arrays; import java.util.function.Supplier; -import org.eclipse.jetty.ee10.webapp.ClassMatcher.ByLocationOrModule; -import org.eclipse.jetty.ee10.webapp.ClassMatcher.ByPackageOrName; -import org.eclipse.jetty.ee10.webapp.ClassMatcher.Entry; -import org.eclipse.jetty.util.IncludeExcludeSet; -import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.ClassMatcher.ByLocationOrModule; +import org.eclipse.jetty.util.ClassMatcher.ByPackageOrName; +import org.eclipse.jetty.util.ClassMatcher.Entry; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/jetty-core/pom.xml b/jetty-core/pom.xml index 7790a54c5e8..97f9ff5401c 100644 --- a/jetty-core/pom.xml +++ b/jetty-core/pom.xml @@ -17,6 +17,7 @@ jetty-client jetty-demos jetty-deploy + jetty-ee jetty-fcgi jetty-http jetty-http-spi diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java index ca442da47c1..78cebbd08ba 100644 --- a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/ResourceAnnotationHandler.java @@ -158,7 +158,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH //try environment scope next if (!bound) - bound = NamingEntryUtil.bindToENC(ServletContextHandler.__environment.getName(), name, mappedName); + bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName); //try Server scope next if (!bound) @@ -313,7 +313,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH //try the environment's scope if (!bound) - bound = NamingEntryUtil.bindToENC(ServletContextHandler.__environment.getName(), name, mappedName); + bound = NamingEntryUtil.bindToENC(ServletContextHandler.ENVIRONMENT.getName(), name, mappedName); //try the server's scope if (!bound) diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml index 94c1864b1c3..5a92efcbfaf 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/src/main/webapp/WEB-INF/jetty-web.xml @@ -9,13 +9,13 @@ org.eclipse.jetty.ee10.servlet.WebApplicationContext object --> - + - org.eclipse.jetty.util. - org.eclipse.jetty.ee10.servlets. - + org.eclipse.jetty.util. + org.eclipse.jetty.ee10.servlets. + diff --git a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java index 63562373118..79390bd4e4b 100644 --- a/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java +++ b/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-proxy-webapp/src/test/java/org/eclipse/jetty/ee10/demos/ProxyWebAppTest.java @@ -54,7 +54,7 @@ public class ProxyWebAppTest // This is a pieced together WebApp. // 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. - 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.setExtraClasspath(MavenTestingUtils.getTargetPath().resolve("test-classes").toString()); server.setHandler(webapp); diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml index effcf47f4cf..9214a93f9c3 100644 --- a/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml +++ b/jetty-ee10/jetty-ee10-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml @@ -6,7 +6,7 @@ - + -org.eclipse.jetty.util.Decorator diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java index ba27aba4088..971e14827bc 100644 --- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java +++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestOSGiUtil.java @@ -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-osgi").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-webapp").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee10").artifactId("jetty-ee10-servlets").versionAsInProject().start()); diff --git a/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/EnvConfiguration.java b/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/EnvConfiguration.java index 6e4df6f9a23..486ad1edd20 100644 --- a/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/EnvConfiguration.java +++ b/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/EnvConfiguration.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.ee10.plus.webapp; -import java.util.Map; import java.util.Set; import javax.naming.Context; import javax.naming.InitialContext; @@ -208,8 +207,8 @@ public class EnvConfiguration extends AbstractConfiguration LOG.debug("Binding env entries from the server scope"); doBindings(envCtx, context.getServer()); - LOG.debug("Binding env entries from environment {} scope", ServletContextHandler.__environment.getName()); - doBindings(envCtx, ServletContextHandler.__environment.getName()); + LOG.debug("Binding env entries from environment {} scope", ServletContextHandler.ENVIRONMENT.getName()); + doBindings(envCtx, ServletContextHandler.ENVIRONMENT.getName()); LOG.debug("Binding env entries from the context scope"); doBindings(envCtx, context); diff --git a/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusConfiguration.java b/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusConfiguration.java index 92d6cc21b69..d6aec5135c0 100644 --- a/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusConfiguration.java +++ b/jetty-ee10/jetty-ee10-plus/src/main/java/org/eclipse/jetty/ee10/plus/webapp/PlusConfiguration.java @@ -83,13 +83,13 @@ public class PlusConfiguration extends AbstractConfiguration { try { - Transaction.bindTransactionToENC(ServletContextHandler.__environment.getName()); + Transaction.bindTransactionToENC(ServletContextHandler.ENVIRONMENT.getName()); } catch (NameNotFoundException e) { try { - org.eclipse.jetty.plus.jndi.Transaction.bindTransactionToENC(ServletContextHandler.__environment.getName()); + org.eclipse.jetty.plus.jndi.Transaction.bindTransactionToENC(ServletContextHandler.ENVIRONMENT.getName()); } catch (NameNotFoundException x) { diff --git a/jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessorTest.java b/jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessorTest.java index 242e28940db..b31ce7297a6 100644 --- a/jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessorTest.java +++ b/jetty-ee10/jetty-ee10-plus/src/test/java/org/eclipse/jetty/ee10/plus/webapp/PlusDescriptorProcessorTest.java @@ -129,7 +129,7 @@ public class PlusDescriptorProcessorTest context.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()}); context.preConfigure(); 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(); Thread.currentThread().setContextClassLoader(context.getClassLoader()); Context icontext = new InitialContext(); diff --git a/jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/TestQuickStart.java b/jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/TestQuickStart.java index 86657db1723..dfc86ee2f00 100644 --- a/jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/TestQuickStart.java +++ b/jetty-ee10/jetty-ee10-quickstart/src/test/java/org/eclipse/jetty/ee10/quickstart/TestQuickStart.java @@ -89,7 +89,7 @@ public class TestQuickStart WebAppContext webapp = new WebAppContext(); webapp.setBaseResourceAsPath(testDir.toPath()); 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); //add in the servlet webapp.getServletHandler().addServlet(fooHolder); @@ -139,7 +139,7 @@ public class TestQuickStart webapp.addConfiguration(new QuickStartConfiguration()); webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setBaseResourceAsPath(testDir.toPath()); - webapp.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); + webapp.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); server.setHandler(webapp); server.setDryRun(false); @@ -180,7 +180,7 @@ public class TestQuickStart webapp.addConfiguration(new QuickStartConfiguration()); webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setBaseResourceAsPath(testDir.toPath()); - webapp.getServerClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); + webapp.getHiddenClassMatcher().exclude("org.eclipse.jetty.ee10.quickstart."); server.setHandler(webapp); server.setDryRun(false); @@ -255,7 +255,7 @@ public class TestQuickStart //a freshly applied context xml quickstart = new WebAppContext(); //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.setWar(testDir.toURI().toURL().toExternalForm()); quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath()); diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java index 47a18744aad..8494ab7ff42 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java @@ -131,7 +131,12 @@ import static jakarta.servlet.ServletContext.TEMPDIR; public class ServletContextHandler extends ContextHandler { 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 = { ServletContextListener.class, diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/EmbeddedWeldTest.java b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/EmbeddedWeldTest.java index 46f0360f371..c3fa25dfd25 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/EmbeddedWeldTest.java +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-cdi/src/test/java/org/eclipse/jetty/ee10/cdi/tests/EmbeddedWeldTest.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.Assumptions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -204,8 +203,8 @@ public class EmbeddedWeldTest webapp.addServletContainerInitializer(new org.jboss.weld.environment.servlet.EnhancedListener()); String pkg = EmbeddedWeldTest.class.getPackage().getName(); - webapp.getServerClassMatcher().add("-" + pkg + "."); - webapp.getSystemClassMatcher().add(pkg + "."); + webapp.getHiddenClassMatcher().add("-" + pkg + "."); + webapp.getProtectedClassMatcher().add(pkg + "."); webapp.addServlet(GreetingsServlet.class, "/greet"); 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 String pkg = EmbeddedWeldTest.class.getPackage().getName(); - webapp.getServerClassMatcher().add("-" + pkg + "."); - webapp.getSystemClassMatcher().add(pkg + "."); + webapp.getHiddenClassMatcher().add("-" + pkg + "."); + webapp.getProtectedClassMatcher().add(pkg + "."); webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class)); webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); diff --git a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml index edf732d1a9c..bf4a0135326 100644 --- a/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml +++ b/jetty-ee10/jetty-ee10-tests/jetty-ee10-test-integration/src/test/resources/webapp-contexts/RFC2616/rfc2616-webapp.xml @@ -3,11 +3,11 @@ /rfc2616-webapp /ee10-test-rfc2616.war - + org.slf4j. org.eclipse.jetty.logging. - + -org.slf4j. -org.eclipse.jetty.logging. diff --git a/jetty-ee10/jetty-ee10-webapp/pom.xml b/jetty-ee10/jetty-ee10-webapp/pom.xml index 144b7302df2..1c1671c896b 100644 --- a/jetty-ee10/jetty-ee10-webapp/pom.xml +++ b/jetty-ee10/jetty-ee10-webapp/pom.xml @@ -17,6 +17,10 @@ + + org.eclipse.jetty + jetty-ee + org.eclipse.jetty jetty-session diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-webapp.xml b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-webapp.xml index 40d2e023fe6..2a964d7eeae 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-webapp.xml +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/etc/jetty-ee10-webapp.xml @@ -2,22 +2,21 @@ - - + + - + - - + + - + - diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-webapp.mod b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-webapp.mod index 298e6b98ef1..229c107f3f3 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-webapp.mod +++ b/jetty-ee10/jetty-ee10-webapp/src/main/config/modules/ee10-webapp.mod @@ -9,6 +9,7 @@ This module enables deployment of Java Servlet web applications. ee10 [depend] +ee-webapp ee10-servlet ee10-security @@ -20,9 +21,9 @@ lib/jetty-ee10-webapp-${jetty.version}.jar [ini-template] # tag::ini-template[] -## Add to the server wide default jars and packages protected or hidden from webapps. -## System classes are protected and cannot be overridden by a webapp. -## Server classes are hidden and cannot be seen by a webapp +## Add to the environment 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.' @@ -32,8 +33,8 @@ lib/jetty-ee10-webapp-${jetty.version}.jar ## ## The +=, operator appends to a CSV list with a comma as needed. ## -#jetty.webapp.addSystemClasses+=,org.example. -#jetty.webapp.addServerClasses+=,org.example. +#jetty.webapp.addProtectedClasses+=,org.example. +#jetty.webapp.addHiddenClasses+=,org.example. # end::ini-template[] [ini] diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/module-info.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/module-info.java index e9f4dd7200e..cb475ecbdab 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/module-info.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/module-info.java @@ -33,6 +33,7 @@ module org.eclipse.jetty.ee10.webapp requires transitive org.eclipse.jetty.session; requires transitive org.eclipse.jetty.ee10.servlet; requires transitive org.eclipse.jetty.xml; + requires transitive org.eclipse.jetty.ee; exports org.eclipse.jetty.ee10.webapp; diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ClassMatcher.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ClassMatcher.java index a79a8783fb9..23a625dcd1c 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ClassMatcher.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/ClassMatcher.java @@ -13,804 +13,53 @@ package org.eclipse.jetty.ee10.webapp; -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; 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/ - *

- * Performs pattern matching of a class against a set of pattern entries. - * A class pattern is a string of one of the forms:

    - *
  • 'org.package.SomeClass' will match a specific class - *
  • 'org.package.' will match a specific package hierarchy - *
  • '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) - *
  • 'file:///some/location/' - A file system directory from which - * the class was loaded - *
  • 'file:///some/location.jar' - The URI of a jar file from which - * the class was loaded - *
  • 'jrt:/modulename' - A Java9 module name
  • - *
  • Any of the above patterns preceded by '-' will exclude rather than include the match. - *
- * When class is initialized from a classpath pattern string, entries - * in this string should be separated by ':' (semicolon) or ',' (comma). + * @deprecated Use org.eclipse.jetty.util.ClassMatcher */ -public class ClassMatcher extends AbstractSet +@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 implements Predicate - { - private final Index.Mutable _entries = new Index.Builder() - .caseSensitive(true) - .mutable() - .build(); - - @Override - public boolean test(String name) - { - return _entries.getBest(name) != null; - } - - @Override - public Iterator 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 implements Predicate - { - private final Map _entries = new HashMap<>(); - - @Override - public boolean test(String name) - { - return _entries.containsKey(name); - } - - @Override - public Iterator 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 implements Predicate - { - 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 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 implements Predicate - { - @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 implements Predicate - { - private final Index.Mutable _entries = new Index.Builder() - .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 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 implements Predicate - { - 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 iterator() - { - Set 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 _entries; - private final IncludeExcludeSet _patterns; - private final IncludeExcludeSet _locations; - - private ClassMatcher(Map entries, IncludeExcludeSet patterns, IncludeExcludeSet locations) - { - _entries = entries; - _patterns = patterns == null ? new IncludeExcludeSet<>(ByPackageOrName.class) : patterns; - _locations = locations == null ? new IncludeExcludeSet<>(ByLocationOrModule.class) : locations; - } - - private ClassMatcher(Map entries) - { - this(entries, null, null); - } - public ClassMatcher() { - this(new HashMap<>()); + super(); } public ClassMatcher(ClassMatcher patterns) { - this(new HashMap<>()); - if (patterns != null) - setAll(patterns.getPatterns()); + super(patterns); + } + + public ClassMatcher(org.eclipse.jetty.util.ClassMatcher patterns) + { + super(patterns); } public ClassMatcher(String... patterns) { - this(new HashMap<>()); - if (patterns != null && patterns.length > 0) - setAll(patterns); + super(patterns); } public ClassMatcher(String pattern) { - this(new HashMap<>()); - add(pattern); + super(pattern); } + protected ClassMatcher(Map entries, IncludeExcludeSet patterns, IncludeExcludeSet locations) + { + super(entries, patterns, locations); + } + + @Override 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 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 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 names, String name, IncludeExcludeSet locations, Supplier 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; - } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoader.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoader.java index e5ef90f7d97..a1ad3d32d9a 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoader.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoader.java @@ -60,8 +60,8 @@ import org.slf4j.LoggerFactory; * parent loader. Java2 compliant loading, where the parent loader * always has priority, can be selected with the * {@link WebAppContext#setParentLoaderPriority(boolean)} - * method and influenced with {@link WebAppContext#isServerClass(Class)} and - * {@link WebAppContext#isSystemClass(Class)}. + * method and influenced with {@link WebAppContext#isHiddenClass(Class)} and + * {@link WebAppContext#isProtectedClass(Class)}. *

* If no parent class loader is provided, then the current thread * context classloader will be used. If that is null then the @@ -115,9 +115,9 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility List 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()) { 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); } @@ -310,7 +310,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility while (urls != null && urls.hasMoreElements()) { URL url = urls.nextElement(); - if (!_context.isSystemResource(name, url) || fromParent.isEmpty()) + if (!_context.isProtectedResource(name, url) || fromParent.isEmpty()) 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 if (parentUrl != null && (Boolean.TRUE.equals(__loadServerClasses.get()) || - !_context.isServerResource(name, parentUrl))) + !_context.isHiddenResource(name, parentUrl))) resource = parentUrl; else { @@ -370,7 +370,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility { URL webappUrl = this.findResource(name); - if (webappUrl != null && !_context.isSystemResource(name, webappUrl)) + if (webappUrl != null && !_context.isProtectedResource(name, webappUrl)) resource = webappUrl; else { @@ -379,7 +379,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility URL parentUrl = _parent.getResource(name); if (parentUrl != null && (Boolean.TRUE.equals(__loadServerClasses.get()) || - !_context.isServerResource(name, parentUrl))) + !_context.isHiddenResource(name, parentUrl))) resource = parentUrl; // 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 @@ -425,7 +425,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility throw new ClassNotFoundException("Bad ClassLoader: returned null for loadClass(" + name + ")"); // 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; } @@ -473,7 +473,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility { parentClass = _parent.loadClass(name); // 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; } @@ -524,7 +524,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility String path = TypeUtil.toClassReference(name); 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); @@ -605,14 +605,14 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility } @Override - public boolean isSystemClass(Class clazz) + public boolean isProtectedClass(Class clazz) { - return _context.isSystemClass(clazz); + return _context.isProtectedClass(clazz); } @Override - public boolean isServerClass(Class clazz) + public boolean isHiddenClass(Class clazz) { - return _context.isServerClass(clazz); + return _context.isHiddenClass(clazz); } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 18e02c1f8c6..473eca9595b 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -36,6 +36,7 @@ import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionListener; +import org.eclipse.jetty.ee.WebAppClassLoading; import org.eclipse.jetty.ee10.servlet.ErrorHandler; import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; 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.Server; import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; 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); 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 - // system classloader. - public static final ClassMatcher __dftSystemClasses = 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 - ); + /** + * @deprecated use {@link WebAppClassLoading#DEFAULT_PROTECTED_CLASSES} + */ + @Deprecated (forRemoval = true, since = "12.0.9") + public static final org.eclipse.jetty.ee10.webapp.ClassMatcher __dftSystemClasses = + new org.eclipse.jetty.ee10.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES); - // Server classes are classes that are hidden from being - // loaded by the web application using system classloader, - // so if web application needs to load any of such classes, - // it has to include them in its distribution. - public static final ClassMatcher __dftServerClasses = new ClassMatcher( - "org.eclipse.jetty." // hide jetty classes - ); + /** + * @deprecated use {@link WebAppClassLoading#DEFAULT_HIDDEN_CLASSES} + */ + @Deprecated (forRemoval = true, since = "12.0.9") + public static final org.eclipse.jetty.ee10.webapp.ClassMatcher __dftServerClasses = + new org.eclipse.jetty.ee10.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES); - private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses); - private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses); + private final ClassMatcher _protectedClasses = new ClassMatcher(WebAppClassLoading.getProtectedClasses(ServletContextHandler.ENVIRONMENT)); + private final ClassMatcher _hiddenClasses = new ClassMatcher(WebAppClassLoading.getHiddenClasses(ServletContextHandler.ENVIRONMENT)); private Configurations _configurations; 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 for (Configuration configuration : Configurations.getKnown()) { - _serverClasses.include(configuration.getServerClasses().getInclusions()); + _hiddenClasses.include(configuration.getServerClasses().getInclusions()); } // Setup Configuration classes for this webapp! @@ -428,8 +433,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _configurations.sort(); for (Configuration configuration : _configurations) { - _systemClasses.add(configuration.getSystemClasses().getPatterns()); - _serverClasses.exclude(configuration.getServerClasses().getExclusions()); + _protectedClasses.add(configuration.getSystemClasses().getPatterns()); + _hiddenClasses.exclude(configuration.getServerClasses().getExclusions()); } // Configure classloader @@ -484,7 +489,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL protected void doStart() throws Exception { ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(__environment.getClassLoader()); + Thread.currentThread().setContextClassLoader(ServletContextHandler.ENVIRONMENT.getClassLoader()); try { _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. *

- * 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 * 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(); - _serverClasses.add(serverClasses.getPatterns()); + _hiddenClasses.clear(); + _hiddenClasses.add(hiddenClasses.getPatterns()); } /** - * Set the system classes patterns. + * Set the protected (aka system) classes patterns. *

- * System classes/packages are classes provided by the JVM and that + * These classes/packages are provided by the JVM and * cannot be replaced by classes of the same name from WEB-INF, * regardless of the value of {@link #setParentLoaderPriority(boolean)}. * - * @param systemClasses the system classes pattern + * @param protectedClasses the system classes pattern */ - public void setSystemClassMatcher(ClassMatcher systemClasses) + public void setProtectedClassMatcher(ClassMatcher protectedClasses) { - _systemClasses.clear(); - _systemClasses.add(systemClasses.getPatterns()); + _protectedClasses.clear(); + _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. * - * @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. * - * @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 */ - 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) + 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() { - 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() { - return _serverClasses.getPatterns(); + return _hiddenClasses.getPatterns(); } - @Override + /** + * @deprecated use {@link #isHiddenClass(Class)} + */ + @Deprecated(since = "12.0.8", forRemoval = true) 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) { - 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) { - 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) { - return _systemClasses.match(name, url); + return _protectedClasses.match(name, url); } @Override @@ -724,23 +839,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL super.setServer(server); if (server != null) { - if (__dftSystemClasses.equals(_systemClasses)) - { - 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()); - } + _protectedClasses.add(WebAppClassLoading.getProtectedClasses(server).getPatterns()); + _hiddenClasses.add(WebAppClassLoading.getHiddenClasses(server).getPatterns()); } } @@ -863,16 +963,16 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL public void dump(Appendable out, String indent) throws IOException { List systemClasses = null; - if (_systemClasses != null) + if (_protectedClasses != null) { - systemClasses = new ArrayList<>(_systemClasses); + systemClasses = new ArrayList<>(_protectedClasses); Collections.sort(systemClasses); } List serverClasses = null; - if (_serverClasses != null) + if (_hiddenClasses != null) { - serverClasses = new ArrayList<>(_serverClasses); + serverClasses = new ArrayList<>(_hiddenClasses); Collections.sort(serverClasses); } @@ -1411,38 +1511,31 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL 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); - } - - 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); + WebAppClassLoading.addProtectedClasses(server, patterns); } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java index 49fca834aba..3bb42c9fbbe 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebDescriptor.java @@ -95,7 +95,7 @@ public class WebDescriptor extends Descriptor public WebDescriptorParser(boolean validating) throws IOException { 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); if (url == null) throw new IllegalStateException("Catalog not found: %s/%s".formatted(WebDescriptor.class.getPackageName(), catalogName)); diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderTest.java index 59b78202a3b..c08c71724ea 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppClassLoaderTest.java @@ -26,9 +26,9 @@ import java.util.List; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.AfterEach; 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.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -211,11 +210,11 @@ public class WebAppClassLoaderTest @Test public void testExposedClassDeprecated() throws Exception { - String[] oldSC = _context.getServerClasses(); + String[] oldSC = _context.getHiddenClasses(); String[] newSC = new String[oldSC.length + 1]; newSC[0] = "-org.eclipse.jetty.ee10.webapp.Configuration"; 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.ClassInJarB"); @@ -228,7 +227,7 @@ public class WebAppClassLoaderTest @Test 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.ClassInJarB"); @@ -241,18 +240,18 @@ public class WebAppClassLoaderTest @Test public void testSystemServerClassDeprecated() throws Exception { - String[] oldServC = _context.getServerClasses(); + String[] oldServC = _context.getHiddenClasses(); String[] newServC = new String[oldServC.length + 1]; newServC[0] = "org.eclipse.jetty.ee10.webapp.Configuration"; 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]; newSysC[0] = "org.eclipse.jetty.ee10.webapp."; 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.ClassInJarB"); @@ -260,28 +259,28 @@ public class WebAppClassLoaderTest assertCantLoadClass("org.eclipse.jetty.ee10.webapp.Configuration"); assertCantLoadClass("org.eclipse.jetty.ee10.webapp.JarScanner"); - oldSysC = _context.getSystemClasses(); + oldSysC = _context.getProtectedClasses(); newSysC = new String[oldSysC.length + 1]; newSysC[0] = "org.acme.webapp.ClassInJarA"; System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length); - _context.setSystemClassMatcher(new ClassMatcher(newSysC)); + _context.setProtectedClassMatcher(new ClassMatcher(newSysC)); 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[0] = "org.acme.webapp.ClassInJarA"; System.arraycopy(oldServC, 0, newServC, 1, oldServC.length); - _context.setServerClassMatcher(new ClassMatcher(newServC)); + _context.setHiddenClassMatcher(new ClassMatcher(newServC)); assertCanLoadResource("org/acme/webapp/ClassInJarA.class"); } @Test public void testSystemServerClass() throws Exception { - _context.getServerClassMatcher().add("org.eclipse.jetty.ee10.webapp.Configuration"); - _context.getSystemClassMatcher().add("org.eclipse.jetty.ee10.webapp."); + _context.getHiddenClassMatcher().add("org.eclipse.jetty.ee10.webapp.Configuration"); + _context.getProtectedClassMatcher().add("org.eclipse.jetty.ee10.webapp."); assertCanLoadClass("org.acme.webapp.ClassInJarA"); 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.JarScanner"); - _context.getSystemClassMatcher().add("org.acme.webapp.ClassInJarA"); + _context.getProtectedClassMatcher().add("org.acme.webapp.ClassInJarA"); 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"); } @@ -340,11 +339,11 @@ public class WebAppClassLoaderTest // assertEquals(0,resources.get(1).toString().indexOf("jar:file:")); // assertEquals(-1,resources.get(2).toString().indexOf("test-classes")); - String[] oldServC = _context.getServerClasses(); + String[] oldServC = _context.getHiddenClasses(); String[] newServC = new String[oldServC.length + 1]; newServC[0] = "org.acme."; System.arraycopy(oldServC, 0, newServC, 1, oldServC.length); - _context.setServerClassMatcher(new ClassMatcher(newServC)); + _context.setHiddenClassMatcher(new ClassMatcher(newServC)); _context.setParentLoaderPriority(true); // dump(_context); @@ -361,12 +360,12 @@ public class WebAppClassLoaderTest // assertEquals(0,resources.get(0).toString().indexOf("jar:file:")); // assertEquals(0,resources.get(1).toString().indexOf("file:")); - _context.setServerClassMatcher(new ClassMatcher(oldServC)); - String[] oldSysC = _context.getSystemClasses(); + _context.setHiddenClassMatcher(new ClassMatcher(oldServC)); + String[] oldSysC = _context.getProtectedClasses(); String[] newSysC = new String[oldSysC.length + 1]; newSysC[0] = "org.acme."; System.arraycopy(oldSysC, 0, newSysC, 1, oldSysC.length); - _context.setSystemClassMatcher(new ClassMatcher(newSysC)); + _context.setProtectedClassMatcher(new ClassMatcher(newSysC)); _context.setParentLoaderPriority(true); // dump(_context); diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java index 29012707a5a..03b6cb835bf 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java @@ -36,6 +36,7 @@ import jakarta.servlet.GenericServlet; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; +import org.eclipse.jetty.ee.WebAppClassLoading; import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.ee10.servlet.ServletContextHandler; 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.empty; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -128,7 +130,7 @@ public class WebAppContextTest * @param name the name of the 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 { @@ -936,4 +938,62 @@ public class WebAppContextTest 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 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 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)); + } } diff --git a/jetty-ee8/jetty-ee8-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml b/jetty-ee8/jetty-ee8-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml index c250a889238..f7b99769fd8 100644 --- a/jetty-ee8/jetty-ee8-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml +++ b/jetty-ee8/jetty-ee8-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml @@ -6,7 +6,7 @@ - + -org.eclipse.jetty.util.Decorator diff --git a/jetty-ee8/jetty-ee8-webapp/pom.xml b/jetty-ee8/jetty-ee8-webapp/pom.xml index d937047dbf0..da6bd706a80 100644 --- a/jetty-ee8/jetty-ee8-webapp/pom.xml +++ b/jetty-ee8/jetty-ee8-webapp/pom.xml @@ -18,6 +18,10 @@ + + org.eclipse.jetty + jetty-ee + org.eclipse.jetty jetty-xml diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-webapp.xml b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-webapp.xml index cd3cb99317f..d041fcdb3f8 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-webapp.xml +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/etc/jetty-ee8-webapp.xml @@ -2,22 +2,21 @@ - + - + - + - + - diff --git a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-webapp.mod b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-webapp.mod index b4a2f2fef2d..f1bd99183c8 100644 --- a/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-webapp.mod +++ b/jetty-ee8/jetty-ee8-webapp/src/main/config/modules/ee8-webapp.mod @@ -8,6 +8,7 @@ Without this, only Jetty-specific handlers may be deployed. ee8 [depend] +ee-webapp ee8-servlet ee8-security @@ -18,9 +19,9 @@ etc/jetty-ee8-webapp.xml lib/jetty-ee8-webapp-${jetty.version}.jar [ini-template] -## Add to the server wide default jars and packages protected or hidden from webapps. -## System classes are protected and cannot be overridden by a webapp. -## Server classes are hidden and cannot be seen by a webapp +## Add to the environment wide default jars and packages protected or hidden from webapps. +## System (aka Protected) classes cannot be overridden by a webapp. +## Server (aka Hidden) 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.' @@ -30,8 +31,8 @@ lib/jetty-ee8-webapp-${jetty.version}.jar ## ## The +=, operator appends to a CSV list with a comma as needed. ## -#jetty.webapp.addSystemClasses+=,org.example. -#jetty.webapp.addServerClasses+=,org.example. +#jetty.webapp.addProtectedClasses+=,org.example. +#jetty.webapp.addHiddenClasses+=,org.example. [ini] contextHandlerClass=org.eclipse.jetty.ee8.webapp.WebAppContext diff --git a/jetty-ee9/jetty-ee9-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml b/jetty-ee9/jetty-ee9-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml index aad5850fc82..cfc55f41dfb 100644 --- a/jetty-ee9/jetty-ee9-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml +++ b/jetty-ee9/jetty-ee9-maven-plugin/src/it/jetty-cdi-start-forked/src/main/jetty/jetty-context.xml @@ -6,7 +6,7 @@ - + -org.eclipse.jetty.util.Decorator diff --git a/jetty-ee9/jetty-ee9-osgi/test-jetty-ee9-osgi/src/test/java/org/eclipse/jetty/ee9/osgi/test/TestOSGiUtil.java b/jetty-ee9/jetty-ee9-osgi/test-jetty-ee9-osgi/src/test/java/org/eclipse/jetty/ee9/osgi/test/TestOSGiUtil.java index 19669562c0c..45cd52bc2a6 100644 --- a/jetty-ee9/jetty-ee9-osgi/test-jetty-ee9-osgi/src/test/java/org/eclipse/jetty/ee9/osgi/test/TestOSGiUtil.java +++ b/jetty-ee9/jetty-ee9-osgi/test-jetty-ee9-osgi/src/test/java/org/eclipse/jetty/ee9/osgi/test/TestOSGiUtil.java @@ -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-osgi").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-servlet").versionAsInProject().start()); res.add(mavenBundle().groupId("org.eclipse.jetty.ee9").artifactId("jetty-ee9-nested").versionAsInProject().start()); diff --git a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-sessions/jetty-ee9-test-sessions-common/src/test/java/org/eclipse/jetty/ee9/session/CreationTest.java b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-sessions/jetty-ee9-test-sessions-common/src/test/java/org/eclipse/jetty/ee9/session/CreationTest.java index 9563b964c86..2cdabe42f32 100644 --- a/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-sessions/jetty-ee9-test-sessions-common/src/test/java/org/eclipse/jetty/ee9/session/CreationTest.java +++ b/jetty-ee9/jetty-ee9-tests/jetty-ee9-test-sessions/jetty-ee9-test-sessions-common/src/test/java/org/eclipse/jetty/ee9/session/CreationTest.java @@ -42,7 +42,6 @@ import org.eclipse.jetty.session.SessionDataStoreFactory; import org.eclipse.jetty.session.test.TestSessionDataStoreFactory; import org.eclipse.jetty.util.StringUtil; import org.hamcrest.Matchers; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/jetty-ee9/jetty-ee9-webapp/pom.xml b/jetty-ee9/jetty-ee9-webapp/pom.xml index 754dafa6136..bc3a6f7d095 100644 --- a/jetty-ee9/jetty-ee9-webapp/pom.xml +++ b/jetty-ee9/jetty-ee9-webapp/pom.xml @@ -17,6 +17,10 @@ + + org.eclipse.jetty + jetty-ee + org.eclipse.jetty jetty-xml diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-webapp.xml b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-webapp.xml index f726687eabb..2a964d7eeae 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-webapp.xml +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/etc/jetty-ee9-webapp.xml @@ -2,22 +2,21 @@ - + - + - + - + - diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-webapp.mod b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-webapp.mod index 37e3a46627f..56301513d11 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-webapp.mod +++ b/jetty-ee9/jetty-ee9-webapp/src/main/config/modules/ee9-webapp.mod @@ -8,6 +8,7 @@ Without this, only Jetty-specific handlers may be deployed. ee9 [depend] +ee-webapp ee9-servlet ee9-security @@ -18,9 +19,9 @@ etc/jetty-ee9-webapp.xml lib/jetty-ee9-webapp-${jetty.version}.jar [ini-template] -## Add to the server wide default jars and packages protected or hidden from webapps. -## System classes are protected and cannot be overridden by a webapp. -## Server classes are hidden and cannot be seen by a webapp +## Add to the environment wide default jars and packages protected or hidden from webapps. +## System (aka Protected) classes cannot be overridden by a webapp. +## Server (aka Hidden) 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.' @@ -30,8 +31,8 @@ lib/jetty-ee9-webapp-${jetty.version}.jar ## ## The +=, operator appends to a CSV list with a comma as needed. ## -#jetty.webapp.addSystemClasses+=,org.example. -#jetty.webapp.addServerClasses+=,org.example. +#jetty.webapp.addProtectedClasses+=,org.example. +#jetty.webapp.addHiddenClasses+=,org.example. [ini] contextHandlerClass?=org.eclipse.jetty.ee9.webapp.WebAppContext diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/module-info.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/module-info.java index 1d9d3fa8b22..6a249f98de0 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/module-info.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/module-info.java @@ -32,6 +32,7 @@ module org.eclipse.jetty.ee9.webapp requires transitive java.instrument; requires transitive org.eclipse.jetty.ee9.servlet; requires transitive org.eclipse.jetty.xml; + requires transitive org.eclipse.jetty.ee; exports org.eclipse.jetty.ee9.webapp; diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/ClassMatcher.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/ClassMatcher.java index 84d05c2a9cf..d382b4cf4ac 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/ClassMatcher.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/ClassMatcher.java @@ -13,786 +13,53 @@ package org.eclipse.jetty.ee9.webapp; -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; 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/ - *

- * Performs pattern matching of a class against a set of pattern entries. - * A class pattern is a string of one of the forms:

    - *
  • 'org.package.SomeClass' will match a specific class - *
  • 'org.package.' will match a specific package hierarchy - *
  • '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) - *
  • 'file:///some/location/' - A file system directory from which - * the class was loaded - *
  • 'file:///some/location.jar' - The URI of a jar file from which - * the class was loaded - *
  • 'jrt:/modulename' - A Java9 module name
  • - *
  • Any of the above patterns preceded by '-' will exclude rather than include the match. - *
- * When class is initialized from a classpath pattern string, entries - * in this string should be separated by ':' (semicolon) or ',' (comma). + * @deprecated Use org.eclipse.jetty.util.ClassMatcher */ -public class ClassMatcher extends AbstractSet +@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 implements Predicate - { - private final Index.Mutable _entries = new Index.Builder() - .caseSensitive(true) - .mutable() - .build(); - - @Override - public boolean test(String name) - { - return _entries.getBest(name) != null; - } - - @Override - public Iterator 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 implements Predicate - { - private final Map _entries = new HashMap<>(); - - @Override - public boolean test(String name) - { - return _entries.containsKey(name); - } - - @Override - public Iterator 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 implements Predicate - { - 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 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 implements Predicate - { - @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 implements Predicate - { - private final Index.Mutable _entries = new Index.Builder() - .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 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 implements Predicate - { - 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 iterator() - { - Set 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 _entries = new HashMap<>(); - IncludeExcludeSet _patterns = new IncludeExcludeSet<>(ByPackageOrName.class); - IncludeExcludeSet _locations = new IncludeExcludeSet<>(ByLocationOrModule.class); - public ClassMatcher() { + super(); } - @SuppressWarnings("CopyConstructorMissesField") public ClassMatcher(ClassMatcher patterns) { - if (patterns != null) - setAll(patterns.getPatterns()); + super(patterns); + } + + public ClassMatcher(org.eclipse.jetty.util.ClassMatcher patterns) + { + super(patterns); } public ClassMatcher(String... patterns) { - if (patterns != null && patterns.length > 0) - setAll(patterns); + super(patterns); } public ClassMatcher(String pattern) { - add(pattern); + super(pattern); } - public boolean include(String name) + protected ClassMatcher(Map entries, IncludeExcludeSet patterns, IncludeExcludeSet locations) { - 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; + super(entries, patterns, locations); } @Override - public boolean add(String pattern) + public ClassMatcher asImmutable() { - 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)) - return false; - String pattern = (String)o; - - Entry entry = _entries.remove(pattern); - if (entry == null) - return false; - - List 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 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 names, String name, IncludeExcludeSet locations, Supplier 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; + return new ClassMatcher(Map.copyOf(_entries), + _patterns.asImmutable(), + _locations.asImmutable()); } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoader.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoader.java index b0f141fe7da..f474fa96ced 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoader.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoader.java @@ -60,8 +60,8 @@ import org.slf4j.LoggerFactory; * parent loader. Java2 compliant loading, where the parent loader * always has priority, can be selected with the * {@link WebAppContext#setParentLoaderPriority(boolean)} - * method and influenced with {@link WebAppContext#isServerClass(Class)} and - * {@link WebAppContext#isSystemClass(Class)}. + * method and influenced with {@link WebAppContext#isHiddenClass(Class)} and + * {@link WebAppContext#isProtectedClass(Class)}. *

* If no parent class loader is provided, then the current thread * 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 + ")"); // 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; } @@ -477,7 +477,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility { parentClass = _parent.loadClass(name); // 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; } @@ -609,14 +609,14 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility } @Override - public boolean isSystemClass(Class clazz) + public boolean isProtectedClass(Class clazz) { - return _context.isSystemClass(clazz); + return _context.isProtectedClass(clazz); } @Override - public boolean isServerClass(Class clazz) + public boolean isHiddenClass(Class clazz) { - return _context.isServerClass(clazz); + return _context.isHiddenClass(clazz); } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index c508d3a0212..574cd0969a6 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -38,6 +38,7 @@ import jakarta.servlet.http.HttpSessionAttributeListener; import jakarta.servlet.http.HttpSessionBindingListener; import jakarta.servlet.http.HttpSessionIdListener; import jakarta.servlet.http.HttpSessionListener; +import org.eclipse.jetty.ee.WebAppClassLoading; import org.eclipse.jetty.ee9.nested.ContextHandler; import org.eclipse.jetty.ee9.nested.ErrorHandler; 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.Server; import org.eclipse.jetty.util.Attributes; +import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.StringUtil; @@ -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 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 - // system classloader. - public static final ClassMatcher __dftSystemClasses = 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 - ); + /** + * @deprecated use {@link WebAppClassLoading#DEFAULT_PROTECTED_CLASSES} + */ + @Deprecated(forRemoval = true, since = "12.0.9") + public static final org.eclipse.jetty.ee9.webapp.ClassMatcher __dftSystemClasses = + new org.eclipse.jetty.ee9.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_PROTECTED_CLASSES); - // Server classes are classes that are hidden from being - // loaded by the web application using system classloader, - // so if web application needs to load any of such classes, - // it has to include them in its distribution. - public static final ClassMatcher __dftServerClasses = new ClassMatcher( - "org.eclipse.jetty." // hide jetty classes - ); + /** + * @deprecated use {@link WebAppClassLoading#DEFAULT_HIDDEN_CLASSES} + */ + @Deprecated(forRemoval = true, since = "12.0.9") + public static final org.eclipse.jetty.ee9.webapp.ClassMatcher __dftServerClasses = + new org.eclipse.jetty.ee9.webapp.ClassMatcher(WebAppClassLoading.DEFAULT_HIDDEN_CLASSES); - private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses); - private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses); + private final ClassMatcher _systemClasses = new ClassMatcher(WebAppClassLoading.getProtectedClasses(ServletContextHandler.ENVIRONMENT)); + private final ClassMatcher _serverClasses = new ClassMatcher(WebAppClassLoading.getHiddenClasses(ServletContextHandler.ENVIRONMENT)); private Configurations _configurations; private String _defaultsDescriptor = WEB_DEFAULTS_XML; @@ -746,13 +751,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } @Override - public boolean isServerClass(Class clazz) + public boolean isHiddenClass(Class clazz) { return _serverClasses.match(clazz); } @Override - public boolean isSystemClass(Class clazz) + public boolean isProtectedClass(Class clazz) { return _systemClasses.match(clazz); } @@ -775,23 +780,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL super.setServer(server); if (server != null) { - if (__dftSystemClasses.equals(_systemClasses)) - { - 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()); - } + _systemClasses.add(WebAppClassLoading.getProtectedClasses(server).getPatterns()); + _serverClasses.add(WebAppClassLoading.getHiddenClasses(server).getPatterns()); } } @@ -1486,38 +1476,31 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL 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); - } - - 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); + WebAppClassLoading.addProtectedClasses(attributes, patterns); } } diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/ClassMatcherTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/ClassMatcherTest.java deleted file mode 100644 index 1e112bb5ac9..00000000000 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/ClassMatcherTest.java +++ /dev/null @@ -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 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 names = new IncludeExcludeSet<>(ByPackageOrName.class); - IncludeExcludeSet 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")); - } -} diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoaderTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoaderTest.java index e7b93249b01..af4e50031f4 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoaderTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppClassLoaderTest.java @@ -26,9 +26,9 @@ import java.util.List; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.ClassMatcher; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.AfterEach; 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.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java index 676bf39760a..9e9ee0bd01b 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java @@ -34,6 +34,7 @@ import jakarta.servlet.GenericServlet; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; +import org.eclipse.jetty.ee.WebAppClassLoading; import org.eclipse.jetty.ee9.nested.ContextHandler; import org.eclipse.jetty.ee9.servlet.ErrorPageErrorHandler; 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.empty; import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -116,7 +118,7 @@ public class WebAppContextTest * @param name the name of the 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 { @@ -847,4 +849,63 @@ public class WebAppContextTest extLibs = extLibs.toAbsolutePath(); 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 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 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)); + } + } } diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index e41957fab8b..b47247f302a 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -39,6 +39,10 @@ org.eclipse.jetty jetty-deploy + + org.eclipse.jetty + jetty-ee + org.eclipse.jetty jetty-hazelcast diff --git a/jetty-home/src/main/resources/modules/logging-jetty.mod b/jetty-home/src/main/resources/modules/logging-jetty.mod index e6aa3e3a631..c068913f00f 100644 --- a/jetty-home/src/main/resources/modules/logging-jetty.mod +++ b/jetty-home/src/main/resources/modules/logging-jetty.mod @@ -21,5 +21,5 @@ basehome:modules/logging/jetty lib/logging/jetty-slf4j-impl-${jetty.version}.jar [ini] -jetty.webapp.addServerClasses+=,org.eclipse.jetty.logging. -jetty.webapp.addServerClasses+=,${jetty.home.uri}/lib/logging/ +jetty.webapp.addHiddenClasses+=,org.eclipse.jetty.logging. +jetty.webapp.addHiddenClasses+=,${jetty.home.uri}/lib/logging/ diff --git a/jetty-home/src/main/resources/modules/logging-log4j1.mod b/jetty-home/src/main/resources/modules/logging-log4j1.mod index 653e33eb93c..70b8a1e607a 100644 --- a/jetty-home/src/main/resources/modules/logging-log4j1.mod +++ b/jetty-home/src/main/resources/modules/logging-log4j1.mod @@ -31,7 +31,7 @@ lib/logging/log4j-${log4j.version}.jar [ini] log4j.version?=1.2.17 -jetty.webapp.addServerClasses+=,org.apache.log4j. +jetty.webapp.addHiddenClasses+=,org.apache.log4j. [license] diff --git a/jetty-home/src/main/resources/modules/logging-log4j2.mod b/jetty-home/src/main/resources/modules/logging-log4j2.mod index 2fed6c18a4d..42e93e4eca5 100644 --- a/jetty-home/src/main/resources/modules/logging-log4j2.mod +++ b/jetty-home/src/main/resources/modules/logging-log4j2.mod @@ -28,7 +28,7 @@ lib/logging/log4j-core-${log4j2.version}.jar [ini] log4j2.version?=@log4j2.version@ -jetty.webapp.addServerClasses+=,org.apache.logging.log4j. +jetty.webapp.addHiddenClasses+=,org.apache.logging.log4j. [license] Log4j is released under the Apache 2.0 license. diff --git a/jetty-home/src/main/resources/modules/logging-logback.mod b/jetty-home/src/main/resources/modules/logging-logback.mod index 40538f43ac0..aa70e7f9a2e 100644 --- a/jetty-home/src/main/resources/modules/logging-logback.mod +++ b/jetty-home/src/main/resources/modules/logging-logback.mod @@ -25,7 +25,7 @@ lib/logging/logback-core-${logback.version}.jar [ini] logback.version?=@logback.version@ -jetty.webapp.addServerClasses+=,ch.qos.logback. +jetty.webapp.addHiddenClasses+=,ch.qos.logback. [license] Logback: the reliable, generic, fast and flexible logging framework. diff --git a/jetty-home/src/main/resources/modules/logging/slf4j.mod b/jetty-home/src/main/resources/modules/logging/slf4j.mod index 4ebb74a2a87..059e02da365 100644 --- a/jetty-home/src/main/resources/modules/logging/slf4j.mod +++ b/jetty-home/src/main/resources/modules/logging/slf4j.mod @@ -16,4 +16,4 @@ lib/logging/slf4j-api-${slf4j.version}.jar [ini] slf4j.version?=@slf4j.version@ -jetty.webapp.addServerClasses+=,org.slf4j. +jetty.webapp.addHiddenClasses+=,org.slf4j. diff --git a/jetty-integrations/jetty-gcloud/jetty-gcloud-session-manager/src/main/config-template/modules/gcloud.mod b/jetty-integrations/jetty-gcloud/jetty-gcloud-session-manager/src/main/config-template/modules/gcloud.mod index 2dc51d64d74..4d2a7e18f5f 100644 --- a/jetty-integrations/jetty-gcloud/jetty-gcloud-session-manager/src/main/config-template/modules/gcloud.mod +++ b/jetty-integrations/jetty-gcloud/jetty-gcloud-session-manager/src/main/config-template/modules/gcloud.mod @@ -17,4 +17,4 @@ http://www.apache.org/licenses/LICENSE-2.0.html [ini] ## Hide the gcloud libraries from deployed webapps -jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/gcloud/ +jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/gcloud/ diff --git a/jetty-integrations/jetty-infinispan/jetty-infinispan-embedded/src/main/config-template/modules/sessions/infinispan/embedded/infinispan-embedded-serverclasses.mod b/jetty-integrations/jetty-infinispan/jetty-infinispan-embedded/src/main/config-template/modules/sessions/infinispan/embedded/infinispan-embedded-serverclasses.mod index 6c369eb73e9..2c4d48f3cd1 100644 --- a/jetty-integrations/jetty-infinispan/jetty-infinispan-embedded/src/main/config-template/modules/sessions/infinispan/embedded/infinispan-embedded-serverclasses.mod +++ b/jetty-integrations/jetty-infinispan/jetty-infinispan-embedded/src/main/config-template/modules/sessions/infinispan/embedded/infinispan-embedded-serverclasses.mod @@ -10,4 +10,4 @@ internal [ini] ## Hide the infinispan libraries from deployed webapps -jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ +jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/infinispan/ diff --git a/jetty-integrations/jetty-infinispan/jetty-infinispan-remote/src/main/config-template/modules/sessions/infinispan/remote/infinispan-remote-serverclasses.mod b/jetty-integrations/jetty-infinispan/jetty-infinispan-remote/src/main/config-template/modules/sessions/infinispan/remote/infinispan-remote-serverclasses.mod index b36937e1c6c..c6e9e48f485 100644 --- a/jetty-integrations/jetty-infinispan/jetty-infinispan-remote/src/main/config-template/modules/sessions/infinispan/remote/infinispan-remote-serverclasses.mod +++ b/jetty-integrations/jetty-infinispan/jetty-infinispan-remote/src/main/config-template/modules/sessions/infinispan/remote/infinispan-remote-serverclasses.mod @@ -9,4 +9,4 @@ infinispan [ini] ## Hide the infinispan libraries from deployed webapps -jetty.webapp.addServerClasses+=,${jetty.base.uri}/lib/infinispan/ +jetty.webapp.addHiddenClasses+=,${jetty.base.uri}/lib/infinispan/ diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java index 8120451450e..6f6bfc2366f 100644 --- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java +++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DemoModulesTests.java @@ -101,7 +101,7 @@ public class DemoModulesTests extends AbstractJettyHomeTest @ParameterizedTest @MethodSource("provideEnvironmentsToTest") - public void testDemoAddServerClasses(String env) throws Exception + public void testDemoAddHiddenClasses(String env) throws Exception { Path jettyBase = newTestJettyBaseDirectory(); String jettyVersion = System.getProperty("jettyVersion"); @@ -124,14 +124,14 @@ public class DemoModulesTests extends AbstractJettyHomeTest assertTrue(runListConfig.awaitFor(START_TIMEOUT, TimeUnit.SECONDS)); assertEquals(0, runListConfig.getExitValue(), "Exit value"); // Example of what we expect - // jetty.webapp.addServerClasses = org.eclipse.jetty.logging.,${jetty.home.uri}/lib/logging/,org.slf4j.,${jetty.base.uri}/lib/bouncycastle/ - String addServerKey = " jetty.webapp.addServerClasses = "; + // jetty.webapp.addHiddenClasses = org.eclipse.jetty.logging.,${jetty.home.uri}/lib/logging/,org.slf4j.,${jetty.base.uri}/lib/bouncycastle/ + String addServerKey = " jetty.webapp.addHiddenClasses = "; String addServerClasses = runListConfig.getLogs().stream() .filter(s -> s.startsWith(addServerKey)) .findFirst() .orElseThrow(() -> new NoSuchElementException("Unable to find [" + addServerKey + "]")); - assertThat("'jetty.webapp.addServerClasses' entry count", + assertThat("'jetty.webapp.addHiddenClasses' entry count", addServerClasses.split(",").length, greaterThan(1)); }