Skip to content

Commit

Permalink
Document hidden and protected classes
Browse files Browse the repository at this point in the history
Fix #12106 by documenting hidden and protected classes.
  • Loading branch information
gregw committed Nov 10, 2024
1 parent 0aa9965 commit c2249dd
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1286,23 +1286,23 @@ include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/ht
[[handler-use-webapp-context-class-loading]]
.WebAppContext Class Loading

The Servlet specification requires that a web application class loader must load the web application classes from `WEB-INF/classes` and `WEB_INF/lib`.
The web application class loader is special because it behaves differently from typical class loaders: where typical class loaders first delegate to their parent class loader and then try to find the class locally, the web application class loader first tries to find the class locally and then delegates to the parent class loader.
The typical class loading model, parent-first, is _inverted_ for web application class loaders, as they use a child-first model.
The Servlet specification requires that a web application class loader must load classes found in the web application `WEB-INF/classes` and `WEB_INF/lib` in preference to using the parent class loader.
This is significantly different to typical java class loaders: where loaders first delegate to their parent class loader.
For Servlet specification compliant web applications, the typical class loading model, parent-first, is _inverted_.

Furthermore, the Servlet specification requires that web applications cannot load or otherwise access the Servlet container implementation classes, also called _server classes_.
Furthermore, the Servlet specification requires that web applications cannot load or otherwise access the Servlet container implementation classes, also called _hidden classes_ (previously _server classes).
Web applications receive the HTTP request object as an instance of the `jakarta.servlet.http.HttpServletRequest` interface, and cannot downcast it to the Jetty specific implementation of that interface to access Jetty specific features -- this ensures maximum web application portability across Servlet container implementations.

Lastly, the Servlet specification requires that other classes, also called _system classes_, such as `jakarta.servlet.http.HttpServletRequest` or JDK classes such as `java.lang.String` or `java.sql.Connection` cannot be modified by web applications by putting, for example, modified versions of those classes in `WEB-INF/classes` so that they are loaded first by the web application class loader (instead of the class-path class loader where they are normally loaded from).
Lastly, the Servlet specification requires that specific classes are _protected classes (previously called _system classes_), in that they cannot be replaced by the web application loader and normal class loader priority applies. These classes such as `jakarta.servlet.http.HttpServletRequest`; JDK classes such as `java.lang.String` or `java.sql.Connection`.

`WebAppContext` implements this class loader logic using a single class loader, `WebAppClassLoader`, with filtering capabilities: when it loads a class, it checks whether the class is a _system class_ or a _server class_ and acts according to the Servlet specification.
`WebAppContext` implements this class loader logic using a single class loader, `WebAppClassLoader`, with filtering capabilities: when it loads a class, it checks whether the class is a _protected class_ or a _hidden class_ by delegating to the `ClassVisibilityChecker` of the context.

When `WebAppClassLoader` is asked to load a class, it first tries to find the class locally (since it must use the inverted child-first model); if the class is found, and it is not a _system class_, the class is loaded; otherwise the class is not found locally.
When `WebAppClassLoader` is asked to load a class, it first tries to find the class locally (since it must use the inverted child-first model); if the class is found, and it is not a _protected class_, the class is loaded; otherwise the class is not found locally.
If the class is not found locally, the parent class loader is asked to load the class; the parent class loader uses the standard parent-first model, so it delegates the class loading to its parent, and so on.
If the class is found, and it is not a _server class_, the class is loaded; otherwise the class is not found and a `ClassNotFoundException` is thrown.
If the class is found, and it is not a _hidden class_, the class is loaded; otherwise the class is not found and a `ClassNotFoundException` is thrown.

Unfortunately, the Servlet specification does not define exactly which classes are _system classes_ and which classes are _server classes_.
However, Jetty picks good defaults and allows server applications to customize _system classes_ and _server classes_ in `WebAppContext`.
Unfortunately, the Servlet specification does not define exactly which classes are _hidden classes_ and which classes are _protected classes_.
However, Jetty picks good defaults and allows server applications to customize _hidden classes_ and _protected classes_ in `WebAppContext`.

// TODO: add a section on parentLoaderPriority.
// TODO: add a code example about how to set system/server classes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,13 @@
* Specializes URLClassLoader with some utility and file mapping
* methods.
* <p>
* This loader defaults to the 2.3 servlet spec behavior where non
* system classes are loaded from the classpath in preference to the
* 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#isHiddenClass(Class)} and
* {@link WebAppContext#isProtectedClass(Class)}.
* This loader implements the Servlet specification behavior that may invert the normal Java classloader parent priority
* behaviour. The {@link ClassVisibilityChecker} API of the {@link WebAppClassLoader.Context} implementation is
* used to determine which classes from the parent classloader are hidden from the context, and which are protected
* from being overridden by the context.
* <p>
* If no parent class loader is provided, then the current thread
* context classloader will be used. If that is null then the
* classloader that loaded this class is used as the parent.
* Java compliant loading, where the parent loader always has priority, can be selected with the
* {@link WebAppContext#setParentLoaderPriority(boolean)} method.
*/
public class WebAppClassLoader extends URLClassLoader implements ClassVisibilityChecker
{
Expand Down Expand Up @@ -169,6 +165,9 @@ public static <T> T runWithServerClassAccess(PrivilegedExceptionAction<T> action

/**
* Constructor.
* <p>
* The {@link Thread#currentThread()} {@link Thread#getContextClassLoader()} will be used as the parent loader,
* unless {@code null}, in which case the classloader that loaded this class is used as the parent.
*
* @param context the context for this classloader
*/
Expand All @@ -180,7 +179,9 @@ public WebAppClassLoader(Context context)
/**
* Constructor.
*
* @param parent the parent classloader
* @param parent the parent classloader; if {@code null} then the {@link Thread#currentThread()}
* {@link Thread#getContextClassLoader()} will be used as the parent loader,
* unless also {@code null}, in which case the classloader that loaded this class is used as the parent.
* @param context the context for this classloader
*/
public WebAppClassLoader(ClassLoader parent, Context context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
* The handlers are configured by pluggable configuration classes, with
* the default being {@link WebXmlConfiguration} and
* {@link JettyWebXmlConfiguration}.
*
* </p>
* <p>The class implements {@link WebAppClassLoader.Context} and thus the {@link org.eclipse.jetty.util.ClassVisibilityChecker}
* API, which is used by any {@link WebAppClassLoader} to control visibility of classes to the context.</p>
*/
@ManagedObject("Web Application ContextHandler")
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context, Deployable
Expand Down Expand Up @@ -629,9 +631,9 @@ public PermissionCollection getPermissions()
/**
* Set the hidden (server) classes patterns.
* <p>
* These classes/packages are used to implement the server and are hidden
* from the context. If the context needs to load these classes, it must have its
* own copy of them in WEB-INF/lib or WEB-INF/classes.
* This {@link ClassMatcher} is used to implement the {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract
* for the context by determining which classes and resources from the server and environment classloader are hidden
* from the context. The context may have its own copy of these classes/resources in WEB-INF/lib or WEB-INF/classes.
*
* @param hiddenClasses the server classes pattern
*/
Expand All @@ -644,9 +646,10 @@ public void setHiddenClassMatcher(ClassMatcher hiddenClasses)
/**
* Set the protected (system) classes patterns.
* <p>
* These classes/packages are provided by the JVM and
* cannot be replaced by classes of the same name from WEB-INF,
* regardless of the value of {@link #setParentLoaderPriority(boolean)}.
*
* This {@link ClassMatcher} is used to implement the {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract
* for the context by determining which classes and resources from the server and environment classloader may not be
* overridden by the context. The context may not have its own copy of these classes/resources.
*
* @param protectedClasses the system classes pattern
*/
Expand All @@ -661,6 +664,8 @@ public void setProtectedClassMatcher(ClassMatcher protectedClasses)
* any existing matcher.
*
* @param hiddenClasses The class matcher of patterns to add to the server ClassMatcher
* @see org.eclipse.jetty.util.ClassVisibilityChecker
* @see #setHiddenClassMatcher(ClassMatcher)
*/
public void addHiddenClassMatcher(ClassMatcher hiddenClasses)
{
Expand All @@ -672,22 +677,28 @@ public void addHiddenClassMatcher(ClassMatcher hiddenClasses)
* any existing matcher.
*
* @param protectedClasses The class matcher of patterns to add to the system ClassMatcher
* @see org.eclipse.jetty.util.ClassVisibilityChecker
* @see #setProtectedClassMatcher(ClassMatcher)
*/
public void addProtectedClassMatcher(ClassMatcher protectedClasses)
{
_protectedClasses.add(protectedClasses.getPatterns());
}

/**
* @return The ClassMatcher used to match System (protected) classes
* @return The ClassMatcher used to match System (protected) classes to implement the
* {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract.
* @see #setProtectedClassMatcher(ClassMatcher)
*/
public ClassMatcher getProtectedClassMatcher()
{
return _protectedClasses;
}

/**
* @return The ClassMatcher used to match Server (hidden) classes
* @return The ClassMatcher used to match Server (hidden) classes to implement the
* {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract.
* @see #setHiddenClassMatcher(ClassMatcher)
*/
public ClassMatcher getHiddenClassMatcher()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,13 @@
* Specializes URLClassLoader with some utility and file mapping
* methods.
* <p>
* This loader defaults to the 2.3 servlet spec behavior where non
* system classes are loaded from the classpath in preference to the
* 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#isHiddenClass(Class)} and
* {@link WebAppContext#isProtectedClass(Class)}.
* This loader implements the Servlet specification behavior that may invert the normal Java classloader parent priority
* behaviour. The {@link ClassVisibilityChecker} API of the {@link WebAppClassLoader.Context} implementation is
* used to determine which classes from the parent classloader are hidden from the context, and which are protected
* from being overridden by the context.
* <p>
* If no parent class loader is provided, then the current thread
* context classloader will be used. If that is null then the
* classloader that loaded this class is used as the parent.
* Java compliant loading, where the parent loader always has priority, can be selected with the
* {@link WebAppContext#setParentLoaderPriority(boolean)} method.
*/
public class WebAppClassLoader extends URLClassLoader implements ClassVisibilityChecker
{
Expand Down Expand Up @@ -148,6 +144,9 @@ public static <T> T runWithHiddenClassAccess(PrivilegedExceptionAction<T> action

/**
* Constructor.
* <p>
* The {@link Thread#currentThread()} {@link Thread#getContextClassLoader()} will be used as the parent loader,
* unless {@code null}, in which case the classloader that loaded this class is used as the parent.
*
* @param context the context for this classloader
*/
Expand All @@ -159,7 +158,9 @@ public WebAppClassLoader(Context context)
/**
* Constructor.
*
* @param parent the parent classloader
* @param parent the parent classloader; if {@code null} then the {@link Thread#currentThread()}
* {@link Thread#getContextClassLoader()} will be used as the parent loader,
* unless also {@code null}, in which case the classloader that loaded this class is used as the parent.
* @param context the context for this classloader
*/
public WebAppClassLoader(ClassLoader parent, Context context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
* The handlers are configured by pluggable configuration classes, with
* the default being {@link WebXmlConfiguration} and
* {@link JettyWebXmlConfiguration}.
*
* </p>
* <p>The class implements {@link WebAppClassLoader.Context} and thus the {@link org.eclipse.jetty.util.ClassVisibilityChecker}
* API, which is used by any {@link WebAppClassLoader} to control visibility of classes to the context.</p>
*/
@ManagedObject("Web Application ContextHandler")
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context, Deployable
Expand Down Expand Up @@ -611,9 +613,9 @@ public PermissionCollection getPermissions()
/**
* Set the hidden (server) classes patterns.
* <p>
* These classes/packages are used to implement the server and are hidden
* from the context. If the context needs to load these classes, it must have its
* own copy of them in WEB-INF/lib or WEB-INF/classes.
* This {@link ClassMatcher} is used to implement the {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract
* for the context by determining which classes and resources from the server and environment classloader are hidden
* from the context. The context may have its own copy of these classes/resources in WEB-INF/lib or WEB-INF/classes.
*
* @param hiddenClasses the server classes pattern
*/
Expand All @@ -626,9 +628,10 @@ public void setHiddenClassMatcher(ClassMatcher hiddenClasses)
/**
* Set the protected (system) classes patterns.
* <p>
* These classes/packages are provided by the JVM and
* cannot be replaced by classes of the same name from WEB-INF,
* regardless of the value of {@link #setParentLoaderPriority(boolean)}.
*
* This {@link ClassMatcher} is used to implement the {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract
* for the context by determining which classes and resources from the server and environment classloader may not be
* overridden by the context. The context may not have its own copy of these classes/resources.
*
* @param protectedClasses the system classes pattern
*/
Expand All @@ -643,6 +646,8 @@ public void setProtectedClassMatcher(ClassMatcher protectedClasses)
* any existing matcher.
*
* @param hiddenClasses The class matcher of patterns to add to the server ClassMatcher
* @see org.eclipse.jetty.util.ClassVisibilityChecker
* @see #setHiddenClassMatcher(ClassMatcher)
*/
public void addHiddenClassMatcher(ClassMatcher hiddenClasses)
{
Expand All @@ -654,22 +659,28 @@ public void addHiddenClassMatcher(ClassMatcher hiddenClasses)
* any existing matcher.
*
* @param protectedClasses The class matcher of patterns to add to the system ClassMatcher
* @see org.eclipse.jetty.util.ClassVisibilityChecker
* @see #setProtectedClassMatcher(ClassMatcher)
*/
public void addProtectedClassMatcher(ClassMatcher protectedClasses)
{
_protectedClasses.add(protectedClasses.getPatterns());
}

/**
* @return The ClassMatcher used to match System (protected) classes
* @return The ClassMatcher used to match System (protected) classes to implement the
* {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract.
* @see #setProtectedClassMatcher(ClassMatcher)
*/
public ClassMatcher getProtectedClassMatcher()
{
return _protectedClasses;
}

/**
* @return The ClassMatcher used to match Server (hidden) classes
* @return The ClassMatcher used to match Server (hidden) classes to implement the
* {@link org.eclipse.jetty.util.ClassVisibilityChecker} contract.
* @see #setHiddenClassMatcher(ClassMatcher)
*/
public ClassMatcher getHiddenClassMatcher()
{
Expand Down

0 comments on commit c2249dd

Please sign in to comment.