Jetty web application support for Jakarta EE 10 with full servlet specification compliance
—
The Jetty EE10 WebApp module provides specialized ClassLoader implementations that offer isolation, visibility controls, and performance optimizations for web applications. The system includes WebAppClassLoader for general use and CachingWebAppClassLoader for improved performance.
The primary ClassLoader implementation for web applications with customizable class loading behavior.
public class WebAppClassLoader extends URLClassLoader
implements ClassVisibilityChecker {
// Constructors
public WebAppClassLoader(Context context);
public WebAppClassLoader(ClassLoader parent, Context context);
// Naming and identification
public String getName();
public void setName(String name);
public Context getContext();
// Classpath management
public void addClassPath(Resource resource);
public void addClassPath(String classPathList) throws IOException;
public void addJars(Resource libs);
// Resource loading
public Enumeration<URL> getResources(String name) throws IOException;
public URL getResource(String name);
// Security and permissions
public PermissionCollection getPermissions(CodeSource cs);
// Class transformation
public void addTransformer(ClassFileTransformer transformer);
public boolean removeTransformer(ClassFileTransformer transformer);
// Visibility controls
public boolean isProtectedClass(Class<?> clazz);
public boolean isHiddenClass(Class<?> clazz);
// Resource management
public void close() throws IOException;
// Static utility methods
public static <T> T runWithServerClassAccess(
PrivilegedExceptionAction<T> action) throws Exception;
}An optimized ClassLoader that caches resource lookup results for improved performance.
public class CachingWebAppClassLoader extends WebAppClassLoader {
// Constructors
public CachingWebAppClassLoader(Context context) throws IOException;
public CachingWebAppClassLoader(ClassLoader parent, Context context)
throws IOException;
// Overridden methods with caching
public URL getResource(String name);
public Class<?> loadClass(String name) throws ClassNotFoundException;
// Cache management
public void clearCache();
}The Context interface provides services that the ClassLoader needs from its WebAppContext.
public interface Context {
// Resource creation
Resource newResource(String urlOrPath) throws IOException;
// Security
PermissionCollection getPermissions();
// Loading behavior
boolean isParentLoaderPriority();
List<Resource> getExtraClasspath();
// Visibility controls
boolean isHiddenResource(String name, URL parentUrl);
boolean isProtectedResource(String name, URL webappUrl);
}Interface for checking class visibility in web applications.
public interface ClassVisibilityChecker {
boolean isProtectedClass(Class<?> clazz);
boolean isHiddenClass(Class<?> clazz);
}import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
// Create ClassLoader with WebAppContext as context
WebAppContext context = new WebAppContext();
WebAppClassLoader classLoader = new WebAppClassLoader(context);
classLoader.setName("MyAppClassLoader");
// Add to context
context.setClassLoader(classLoader);import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.util.resource.ResourceFactory;
WebAppContext context = new WebAppContext();
WebAppClassLoader classLoader = new WebAppClassLoader(context);
// Add individual JAR files
ResourceFactory rf = ResourceFactory.of(context);
classLoader.addClassPath(rf.newResource("/path/to/lib/custom.jar"));
classLoader.addClassPath(rf.newResource("/path/to/classes/"));
// Add multiple paths at once
classLoader.addClassPath("/path/to/lib1.jar:/path/to/lib2.jar");
// Add all JARs from a directory
classLoader.addJars(rf.newResource("/path/to/lib/"));import org.eclipse.jetty.ee10.webapp.CachingWebAppClassLoader;
WebAppContext context = new WebAppContext();
try {
CachingWebAppClassLoader classLoader =
new CachingWebAppClassLoader(context);
context.setClassLoader(classLoader);
// Later, clear cache if needed
classLoader.clearCache();
} catch (IOException e) {
// Handle initialization error
}import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.util.ClassMatcher;
WebAppContext context = new WebAppContext();
// Configure protected classes (visible to webapp)
ClassMatcher protectedClasses = new ClassMatcher();
protectedClasses.add("org.eclipse.jetty.http.");
protectedClasses.add("jakarta.servlet.");
context.setProtectedClassMatcher(protectedClasses);
// Configure hidden classes (not visible to webapp)
ClassMatcher hiddenClasses = new ClassMatcher();
hiddenClasses.add("org.eclipse.jetty.util.log.");
hiddenClasses.add("org.eclipse.jetty.webapp.");
context.setHiddenClassMatcher(hiddenClasses);
WebAppClassLoader classLoader = new WebAppClassLoader(context);
// Check visibility
Class<?> servletClass = Class.forName("jakarta.servlet.Servlet");
boolean isProtected = classLoader.isProtectedClass(servletClass);
boolean isHidden = classLoader.isHiddenClass(servletClass);import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import java.lang.instrument.ClassFileTransformer;
WebAppClassLoader classLoader = new WebAppClassLoader(context);
// Add a transformer for bytecode modification
ClassFileTransformer transformer = new ClassFileTransformer() {
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
// Transform bytecode here
return classfileBuffer; // Return modified or original bytecode
}
};
classLoader.addTransformer(transformer);
// Later remove if needed
boolean removed = classLoader.removeTransformer(transformer);import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import java.security.PrivilegedExceptionAction;
// Execute code with access to server classes
String result = WebAppClassLoader.runWithServerClassAccess(
new PrivilegedExceptionAction<String>() {
@Override
public String run() throws Exception {
// Code here can access server classes normally hidden from webapps
return "Server operation completed";
}
}
);import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
WebAppContext context = new WebAppContext();
context.setParentLoaderPriority(true); // Parent-first loading
WebAppClassLoader classLoader = new WebAppClassLoader(context);
// This will check parent classloader first
URL resource = classLoader.getResource("config.properties");
Enumeration<URL> resources = classLoader.getResources("META-INF/services/myservice");import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
public class CustomContext implements WebAppClassLoader.Context {
@Override
public Resource newResource(String urlOrPath) throws IOException {
// Custom resource creation logic
return ResourceFactory.root().newResource(urlOrPath);
}
@Override
public PermissionCollection getPermissions() {
// Return custom permissions
return new Permissions();
}
@Override
public boolean isParentLoaderPriority() {
return false; // Child-first loading
}
@Override
public List<Resource> getExtraClasspath() {
// Return additional classpath resources
return new ArrayList<>();
}
@Override
public boolean isHiddenResource(String name, URL parentUrl) {
// Custom logic for hiding resources from webapp
return name.startsWith("META-INF/internal/");
}
@Override
public boolean isProtectedResource(String name, URL webappUrl) {
// Custom logic for protecting resources
return name.startsWith("WEB-INF/");
}
}
// Use custom context
CustomContext context = new CustomContext();
WebAppClassLoader classLoader = new WebAppClassLoader(context);ClassLoader operations can encounter various exceptions:
IOException - I/O errors when accessing JAR files or resourcesClassNotFoundException - Class cannot be found or loadedSecurityException - Security manager denies accessIllegalArgumentException - Invalid classpath entriesCommon error scenarios:
// Properly close ClassLoader to free resources
try (WebAppClassLoader classLoader = new WebAppClassLoader(context)) {
// Use classloader
} // Automatically closed
// Or manually close
WebAppClassLoader classLoader = new WebAppClassLoader(context);
try {
// Use classloader
} finally {
classLoader.close();
}CachingWebAppClassLoader classLoader = new CachingWebAppClassLoader(context);
// Clear cache when needed (e.g., after hot deployment)
classLoader.clearCache();
// Monitor cache effectiveness through logs or JMXInstall with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty-ee10--jetty-ee10-webapp