* Fixes #6184 - JEP-411 will deprecate/remove the SecurityManager from the JVM. Removed usages of `SecurityManager` and `AccessControlller.doPrivileged()`. In places where they are still necessary, now using reflection via newly introduced `SecurityUtils` class. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
795315f6ff
commit
2c61011de1
|
@ -23,6 +23,7 @@ import javax.security.auth.message.config.AuthConfigFactory;
|
||||||
import javax.security.auth.message.config.AuthConfigProvider;
|
import javax.security.auth.message.config.AuthConfigProvider;
|
||||||
import javax.security.auth.message.config.RegistrationListener;
|
import javax.security.auth.message.config.RegistrationListener;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -60,9 +61,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||||
@Override
|
@Override
|
||||||
public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description)
|
public String registerConfigProvider(String className, Map properties, String layer, String appContext, String description)
|
||||||
{
|
{
|
||||||
SecurityManager sm = System.getSecurityManager();
|
checkPermission("registerAuthConfigProvider");
|
||||||
if (sm != null)
|
|
||||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
|
||||||
|
|
||||||
String key = getKey(layer, appContext);
|
String key = getKey(layer, appContext);
|
||||||
AuthConfigProvider configProvider = createConfigProvider(className, properties);
|
AuthConfigProvider configProvider = createConfigProvider(className, properties);
|
||||||
|
@ -76,9 +75,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||||
@Override
|
@Override
|
||||||
public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description)
|
public String registerConfigProvider(AuthConfigProvider provider, String layer, String appContext, String description)
|
||||||
{
|
{
|
||||||
SecurityManager sm = System.getSecurityManager();
|
checkPermission("registerAuthConfigProvider");
|
||||||
if (sm != null)
|
|
||||||
sm.checkPermission(new AuthPermission("registerAuthConfigProvider"));
|
|
||||||
|
|
||||||
String key = getKey(layer, appContext);
|
String key = getKey(layer, appContext);
|
||||||
DefaultRegistrationContext context = new DefaultRegistrationContext(provider, layer, appContext, description, false);
|
DefaultRegistrationContext context = new DefaultRegistrationContext(provider, layer, appContext, description, false);
|
||||||
|
@ -91,9 +88,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||||
@Override
|
@Override
|
||||||
public boolean removeRegistration(String registrationID)
|
public boolean removeRegistration(String registrationID)
|
||||||
{
|
{
|
||||||
SecurityManager sm = System.getSecurityManager();
|
checkPermission("removeAuthRegistration");
|
||||||
if (sm != null)
|
|
||||||
sm.checkPermission(new AuthPermission("removeAuthRegistration"));
|
|
||||||
|
|
||||||
DefaultRegistrationContext registrationContext = _registrations.remove(registrationID);
|
DefaultRegistrationContext registrationContext = _registrations.remove(registrationID);
|
||||||
if (registrationContext == null)
|
if (registrationContext == null)
|
||||||
|
@ -106,9 +101,7 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||||
@Override
|
@Override
|
||||||
public String[] detachListener(RegistrationListener listener, String layer, String appContext)
|
public String[] detachListener(RegistrationListener listener, String layer, String appContext)
|
||||||
{
|
{
|
||||||
SecurityManager sm = System.getSecurityManager();
|
checkPermission("detachAuthListener");
|
||||||
if (sm != null)
|
|
||||||
sm.checkPermission(new AuthPermission("detachAuthListener"));
|
|
||||||
|
|
||||||
List<String> registrationIds = new ArrayList<>();
|
List<String> registrationIds = new ArrayList<>();
|
||||||
for (DefaultRegistrationContext registration : _registrations.values())
|
for (DefaultRegistrationContext registration : _registrations.values())
|
||||||
|
@ -145,13 +138,16 @@ public class DefaultAuthConfigFactory extends AuthConfigFactory
|
||||||
@Override
|
@Override
|
||||||
public void refresh()
|
public void refresh()
|
||||||
{
|
{
|
||||||
SecurityManager sm = System.getSecurityManager();
|
checkPermission("refreshAuth");
|
||||||
if (sm != null)
|
|
||||||
sm.checkPermission(new AuthPermission("refreshAuth"));
|
|
||||||
|
|
||||||
// TODO: maybe we should re-construct providers created from classname.
|
// TODO: maybe we should re-construct providers created from classname.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkPermission(String permission)
|
||||||
|
{
|
||||||
|
SecurityUtils.checkPermission(new AuthPermission(permission));
|
||||||
|
}
|
||||||
|
|
||||||
private static String getKey(String layer, String appContext)
|
private static String getKey(String layer, String appContext)
|
||||||
{
|
{
|
||||||
return layer + "/" + appContext;
|
return layer + "/" + appContext;
|
||||||
|
|
|
@ -20,8 +20,6 @@ import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -87,6 +85,7 @@ import org.eclipse.jetty.util.component.DumpableCollection;
|
||||||
import org.eclipse.jetty.util.component.Graceful;
|
import org.eclipse.jetty.util.component.Graceful;
|
||||||
import org.eclipse.jetty.util.component.LifeCycle;
|
import org.eclipse.jetty.util.component.LifeCycle;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -219,7 +218,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
private int _maxFormKeys = Integer.getInteger(MAX_FORM_KEYS_KEY, DEFAULT_MAX_FORM_KEYS);
|
private int _maxFormKeys = Integer.getInteger(MAX_FORM_KEYS_KEY, DEFAULT_MAX_FORM_KEYS);
|
||||||
private int _maxFormContentSize = Integer.getInteger(MAX_FORM_CONTENT_SIZE_KEY, DEFAULT_MAX_FORM_CONTENT_SIZE);
|
private int _maxFormContentSize = Integer.getInteger(MAX_FORM_CONTENT_SIZE_KEY, DEFAULT_MAX_FORM_CONTENT_SIZE);
|
||||||
private boolean _compactPath = false;
|
private boolean _compactPath = false;
|
||||||
private boolean _usingSecurityManager = System.getSecurityManager() != null;
|
private boolean _usingSecurityManager = getSecurityManager() != null;
|
||||||
|
|
||||||
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
|
private final List<EventListener> _programmaticListeners = new CopyOnWriteArrayList<>();
|
||||||
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
|
private final List<ServletContextListener> _servletContextListeners = new CopyOnWriteArrayList<>();
|
||||||
|
@ -326,7 +325,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
|
|
||||||
public void setUsingSecurityManager(boolean usingSecurityManager)
|
public void setUsingSecurityManager(boolean usingSecurityManager)
|
||||||
{
|
{
|
||||||
if (usingSecurityManager && System.getSecurityManager() == null)
|
if (usingSecurityManager && getSecurityManager() == null)
|
||||||
throw new IllegalStateException("No security manager");
|
throw new IllegalStateException("No security manager");
|
||||||
_usingSecurityManager = usingSecurityManager;
|
_usingSecurityManager = usingSecurityManager;
|
||||||
}
|
}
|
||||||
|
@ -2114,6 +2113,11 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
_aliasChecks.clear();
|
_aliasChecks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Object getSecurityManager()
|
||||||
|
{
|
||||||
|
return SecurityUtils.getSecurityManager();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context.
|
* Context.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -2561,11 +2565,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
{
|
{
|
||||||
// check to see if the classloader of the caller is the same as the context
|
// check to see if the classloader of the caller is the same as the context
|
||||||
// classloader, or a parent of it, as required by the javadoc specification.
|
// classloader, or a parent of it, as required by the javadoc specification.
|
||||||
|
ClassLoader callerLoader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
|
||||||
// Wrap in a PrivilegedAction so that only Jetty code will require the
|
.getCallerClass()
|
||||||
// "createSecurityManager" permission, not also application code that calls this method.
|
.getClassLoader();
|
||||||
Caller caller = AccessController.doPrivileged((PrivilegedAction<Caller>)Caller::new);
|
|
||||||
ClassLoader callerLoader = caller.getCallerClassLoader(2);
|
|
||||||
while (callerLoader != null)
|
while (callerLoader != null)
|
||||||
{
|
{
|
||||||
if (callerLoader == _classLoader)
|
if (callerLoader == _classLoader)
|
||||||
|
@ -2573,7 +2575,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
else
|
else
|
||||||
callerLoader = callerLoader.getParent();
|
callerLoader = callerLoader.getParent();
|
||||||
}
|
}
|
||||||
System.getSecurityManager().checkPermission(new RuntimePermission("getClassLoader"));
|
SecurityUtils.checkPermission(new RuntimePermission("getClassLoader"));
|
||||||
return _classLoader;
|
return _classLoader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3103,17 +3105,4 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
*/
|
*/
|
||||||
void exitScope(Context context, Request request);
|
void exitScope(Context context, Request request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Caller extends SecurityManager
|
|
||||||
{
|
|
||||||
public ClassLoader getCallerClassLoader(int depth)
|
|
||||||
{
|
|
||||||
if (depth < 0)
|
|
||||||
return null;
|
|
||||||
Class<?>[] classContext = getClassContext();
|
|
||||||
if (classContext.length <= depth)
|
|
||||||
return null;
|
|
||||||
return classContext[depth].getClassLoader();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,6 @@ package org.eclipse.jetty.logging;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
@ -161,29 +159,26 @@ public class JettyLoggerConfiguration
|
||||||
*/
|
*/
|
||||||
public JettyLoggerConfiguration load(ClassLoader loader)
|
public JettyLoggerConfiguration load(ClassLoader loader)
|
||||||
{
|
{
|
||||||
return AccessController.doPrivileged((PrivilegedAction<JettyLoggerConfiguration>)() ->
|
// First see if the jetty-logging.properties object exists in the classpath.
|
||||||
|
// * This is an optional feature used by embedded mode use, and test cases to allow for early
|
||||||
|
// * configuration of the Log class in situations where access to the System.properties are
|
||||||
|
// * either too late or just impossible.
|
||||||
|
load(readProperties(loader, "jetty-logging.properties"));
|
||||||
|
|
||||||
|
// Next see if an OS specific jetty-logging.properties object exists in the classpath.
|
||||||
|
// This really for setting up test specific logging behavior based on OS.
|
||||||
|
String osName = System.getProperty("os.name");
|
||||||
|
if (osName != null && osName.length() > 0)
|
||||||
{
|
{
|
||||||
// First see if the jetty-logging.properties object exists in the classpath.
|
// NOTE: cannot use jetty-util's StringUtil.replace() as it may initialize logging itself.
|
||||||
// * This is an optional feature used by embedded mode use, and test cases to allow for early
|
osName = osName.toLowerCase(Locale.ENGLISH).replace(' ', '-');
|
||||||
// * configuration of the Log class in situations where access to the System.properties are
|
load(readProperties(loader, "jetty-logging-" + osName + ".properties"));
|
||||||
// * either too late or just impossible.
|
}
|
||||||
load(readProperties(loader, "jetty-logging.properties"));
|
|
||||||
|
|
||||||
// Next see if an OS specific jetty-logging.properties object exists in the classpath.
|
// Now load the System.properties as-is into the properties,
|
||||||
// This really for setting up test specific logging behavior based on OS.
|
// these values will override any key conflicts in properties.
|
||||||
String osName = System.getProperty("os.name");
|
load(System.getProperties());
|
||||||
if (osName != null && osName.length() > 0)
|
return this;
|
||||||
{
|
|
||||||
// NOTE: cannot use jetty-util's StringUtil.replace() as it may initialize logging itself.
|
|
||||||
osName = osName.toLowerCase(Locale.ENGLISH).replace(' ', '-');
|
|
||||||
load(readProperties(loader, "jetty-logging-" + osName + ".properties"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now load the System.properties as-is into the properties,
|
|
||||||
// these values will override any key conflicts in properties.
|
|
||||||
load(System.getProperties());
|
|
||||||
return this;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getString(String key, String defValue)
|
public String getString(String key, String defValue)
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util;
|
package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryUtils provides an abstraction over memory properties and operations.
|
* MemoryUtils provides an abstraction over memory properties and operations.
|
||||||
*/
|
*/
|
||||||
|
@ -25,18 +22,11 @@ public class MemoryUtils
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
final int defaultValue = 64;
|
int defaultValue = 64;
|
||||||
int value = defaultValue;
|
int value = defaultValue;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
value = Integer.parseInt(AccessController.doPrivileged(new PrivilegedAction<String>()
|
value = Integer.parseInt(System.getProperty("org.eclipse.jetty.util.cacheLineBytes", String.valueOf(defaultValue)));
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public String run()
|
|
||||||
{
|
|
||||||
return System.getProperty("org.eclipse.jetty.util.cacheLineBytes", String.valueOf(defaultValue));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
catch (Exception ignored)
|
catch (Exception ignored)
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,9 +26,7 @@ import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.CodeSource;
|
import java.security.CodeSource;
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.security.ProtectionDomain;
|
import java.security.ProtectionDomain;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -671,7 +669,7 @@ public class TypeUtil
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProtectionDomain domain = AccessController.doPrivileged((PrivilegedAction<ProtectionDomain>)() -> clazz.getProtectionDomain());
|
ProtectionDomain domain = clazz.getProtectionDomain();
|
||||||
if (domain != null)
|
if (domain != null)
|
||||||
{
|
{
|
||||||
CodeSource source = domain.getCodeSource();
|
CodeSource source = domain.getCodeSource();
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.security;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.security.Permission;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Collections of utility methods to deal with the scheduled removal
|
||||||
|
* of the security classes defined by <a href="https://openjdk.org/jeps/411">JEP 411</a>.</p>
|
||||||
|
*/
|
||||||
|
public class SecurityUtils
|
||||||
|
{
|
||||||
|
private static final MethodHandle doPrivileged = lookup();
|
||||||
|
|
||||||
|
private static MethodHandle lookup()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use reflection to work with Java versions that have and don't have AccessController.
|
||||||
|
Class<?> klass = ClassLoader.getPlatformClassLoader().loadClass("java.security.AccessController");
|
||||||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||||
|
return lookup.findStatic(klass, "doPrivileged", MethodType.methodType(Object.class, PrivilegedAction.class));
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current security manager, if available
|
||||||
|
*/
|
||||||
|
public static Object getSecurityManager()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use reflection to work with Java versions that have and don't have SecurityManager.
|
||||||
|
return System.class.getMethod("getSecurityManager").invoke(null);
|
||||||
|
}
|
||||||
|
catch (Throwable ignored)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Checks the given permission, if the {@link #getSecurityManager() security manager}
|
||||||
|
* is set.</p>
|
||||||
|
*
|
||||||
|
* @param permission the permission to check
|
||||||
|
* @throws SecurityException if the permission check fails
|
||||||
|
*/
|
||||||
|
public static void checkPermission(Permission permission) throws SecurityException
|
||||||
|
{
|
||||||
|
Object securityManager = SecurityUtils.getSecurityManager();
|
||||||
|
if (securityManager == null)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
securityManager.getClass().getMethod("checkPermission")
|
||||||
|
.invoke(securityManager, permission);
|
||||||
|
}
|
||||||
|
catch (SecurityException x)
|
||||||
|
{
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
catch (Throwable ignored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Runs the given action with the calling context restricted
|
||||||
|
* to just the calling frame, not all the frames in the stack.</p>
|
||||||
|
*
|
||||||
|
* @param action the action to run
|
||||||
|
* @return the result of running the action
|
||||||
|
* @param <T> the type of the result
|
||||||
|
*/
|
||||||
|
public static <T> T doPrivileged(PrivilegedAction<T> action)
|
||||||
|
{
|
||||||
|
// Keep this method short and inlineable.
|
||||||
|
MethodHandle methodHandle = doPrivileged;
|
||||||
|
if (methodHandle == null)
|
||||||
|
return action.run();
|
||||||
|
return doPrivileged(methodHandle, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T doPrivileged(MethodHandle doPrivileged, PrivilegedAction<T> action)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (T)doPrivileged.invoke(action);
|
||||||
|
}
|
||||||
|
catch (RuntimeException | Error x)
|
||||||
|
{
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecurityUtils()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,39 +13,46 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util.thread;
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.security.SecurityUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience class to ensure that a new Thread is created
|
* <p>Convenience {@link Thread} factory that ensure threads are
|
||||||
* inside a privileged block.
|
* created without referencing any web application {@link ClassLoader}.</p>
|
||||||
*
|
* <p>Up to Java 17, the {@code Thread} constructor was taking a
|
||||||
* This prevents the Thread constructor
|
* snapshot of the calling context, which may contain a {@link ProtectionDomain}
|
||||||
* from pinning the caller's context classloader. This happens
|
* that references a web application {@code ClassLoader}
|
||||||
* when the Thread constructor takes a snapshot of the current
|
* (for example if the creation of the {@code Thread} was triggered
|
||||||
* calling context - which contains ProtectionDomains that may
|
* by some operation performed by the web application).
|
||||||
* reference the context classloader - and remembers it for the
|
* The {@code Thread} might then be pooled and prevent the
|
||||||
* lifetime of the Thread.
|
* web application {@code ClassLoader} to be garbage collected
|
||||||
|
* when the web application is undeployed.
|
||||||
|
* For this reason, {@code Thread}s must be created in a privileged
|
||||||
|
* action, which restricts the calling context to just the caller
|
||||||
|
* frame, not all the frames in the stack.</p>
|
||||||
|
* <p>Since Java 18 and the removal of the Java security manager
|
||||||
|
* and related classes by JEP 411, {@code Thread}s do not retain
|
||||||
|
* the calling context, so there is no need to create them in a
|
||||||
|
* privileged action.</p>
|
||||||
*/
|
*/
|
||||||
class PrivilegedThreadFactory
|
class PrivilegedThreadFactory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Use a Supplier to make a new thread, calling it within
|
* <p>Creates a new {@link Thread} from the given {@link Supplier},
|
||||||
* a privileged block to prevent classloader pinning.
|
* without retaining the calling context.</p>
|
||||||
*
|
*
|
||||||
* @param newThreadSupplier a Supplier to create a fresh thread
|
* @param creator the action that creates the {@link Thread}
|
||||||
* @return a new thread, protected from classloader pinning.
|
* @return a new {@link Thread} without retaining the calling context
|
||||||
*/
|
*/
|
||||||
static <T extends Thread> T newThread(Supplier<T> newThreadSupplier)
|
static <T extends Thread> T newThread(PrivilegedAction<T> creator)
|
||||||
|
{
|
||||||
|
return SecurityUtils.doPrivileged(creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PrivilegedThreadFactory()
|
||||||
{
|
{
|
||||||
return AccessController.doPrivileged(new PrivilegedAction<T>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public T run()
|
|
||||||
{
|
|
||||||
return newThreadSupplier.get();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,11 @@ import org.slf4j.LoggerFactory;
|
||||||
public class ShutdownThread extends Thread
|
public class ShutdownThread extends Thread
|
||||||
{
|
{
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ShutdownThread.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ShutdownThread.class);
|
||||||
private static final ShutdownThread _thread = PrivilegedThreadFactory.newThread(() ->
|
private static final ShutdownThread _thread = PrivilegedThreadFactory.newThread(ShutdownThread::new);
|
||||||
{
|
|
||||||
return new ShutdownThread();
|
|
||||||
});
|
|
||||||
|
|
||||||
private final AutoLock _lock = new AutoLock();
|
private final AutoLock _lock = new AutoLock();
|
||||||
private boolean _hooked;
|
private boolean _hooked;
|
||||||
private final List<LifeCycle> _lifeCycles = new CopyOnWriteArrayList<LifeCycle>();
|
private final List<LifeCycle> _lifeCycles = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor for the singleton
|
* Default constructor for the singleton
|
||||||
|
|
|
@ -30,8 +30,6 @@ import java.net.URL;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -493,7 +491,7 @@ public class XmlConfiguration
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Config error {} at {} in {}", e.toString(), node, _configuration);
|
LOG.warn("Config error {} at {} in {}", e, node, _configuration);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1815,86 +1813,81 @@ public class XmlConfiguration
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AccessController.doPrivileged((PrivilegedExceptionAction<Void>)() ->
|
Properties properties = new Properties();
|
||||||
|
properties.putAll(System.getProperties());
|
||||||
|
|
||||||
|
// For all arguments, load properties
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("args={}", Arrays.asList(args));
|
||||||
|
for (String arg : args)
|
||||||
{
|
{
|
||||||
Properties properties = new Properties();
|
if (arg.indexOf('=') >= 0)
|
||||||
properties.putAll(System.getProperties());
|
|
||||||
|
|
||||||
// For all arguments, load properties
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("args={}", Arrays.asList(args));
|
|
||||||
for (String arg : args)
|
|
||||||
{
|
{
|
||||||
if (arg.indexOf('=') >= 0)
|
int i = arg.indexOf('=');
|
||||||
|
properties.put(arg.substring(0, i), arg.substring(i + 1));
|
||||||
|
}
|
||||||
|
else if (arg.toLowerCase(Locale.ENGLISH).endsWith(".properties"))
|
||||||
|
{
|
||||||
|
try (InputStream inputStream = Resource.newResource(arg).getInputStream())
|
||||||
{
|
{
|
||||||
int i = arg.indexOf('=');
|
properties.load(inputStream);
|
||||||
properties.put(arg.substring(0, i), arg.substring(i + 1));
|
|
||||||
}
|
|
||||||
else if (arg.toLowerCase(Locale.ENGLISH).endsWith(".properties"))
|
|
||||||
{
|
|
||||||
try (InputStream inputStream = Resource.newResource(arg).getInputStream())
|
|
||||||
{
|
|
||||||
properties.load(inputStream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For all arguments, parse XMLs
|
// For all arguments, parse XMLs
|
||||||
XmlConfiguration last = null;
|
XmlConfiguration last = null;
|
||||||
List<Object> objects = new ArrayList<>(args.length);
|
List<Object> objects = new ArrayList<>(args.length);
|
||||||
for (String arg : args)
|
for (String arg : args)
|
||||||
|
{
|
||||||
|
if (!arg.toLowerCase(Locale.ENGLISH).endsWith(".properties") && (arg.indexOf('=') < 0))
|
||||||
{
|
{
|
||||||
if (!arg.toLowerCase(Locale.ENGLISH).endsWith(".properties") && (arg.indexOf('=') < 0))
|
XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(arg));
|
||||||
|
if (last != null)
|
||||||
|
configuration.getIdMap().putAll(last.getIdMap());
|
||||||
|
if (properties.size() > 0)
|
||||||
{
|
{
|
||||||
XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(arg));
|
Map<String, String> props = new HashMap<>();
|
||||||
if (last != null)
|
properties.forEach((key, value) -> props.put(key.toString(),
|
||||||
configuration.getIdMap().putAll(last.getIdMap());
|
String.valueOf(value)));
|
||||||
if (properties.size() > 0)
|
configuration.getProperties().putAll(props);
|
||||||
{
|
|
||||||
Map<String, String> props = new HashMap<>();
|
|
||||||
properties.entrySet().stream()
|
|
||||||
.forEach(objectObjectEntry -> props.put(objectObjectEntry.getKey().toString(),
|
|
||||||
String.valueOf(objectObjectEntry.getValue())));
|
|
||||||
configuration.getProperties().putAll(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object obj = configuration.configure();
|
|
||||||
if (obj != null && !objects.contains(obj))
|
|
||||||
objects.add(obj);
|
|
||||||
last = configuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object obj = configuration.configure();
|
||||||
|
if (obj != null && !objects.contains(obj))
|
||||||
|
objects.add(obj);
|
||||||
|
last = configuration;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("objects={}", objects);
|
LOG.debug("objects={}", objects);
|
||||||
|
|
||||||
// For all objects created by XmlConfigurations, start them if they are lifecycles.
|
// For all objects created by XmlConfigurations, start them if they are lifecycles.
|
||||||
List<LifeCycle> started = new ArrayList<>(objects.size());
|
List<LifeCycle> started = new ArrayList<>(objects.size());
|
||||||
for (Object obj : objects)
|
for (Object obj : objects)
|
||||||
|
{
|
||||||
|
if (obj instanceof LifeCycle)
|
||||||
{
|
{
|
||||||
if (obj instanceof LifeCycle)
|
LifeCycle lc = (LifeCycle)obj;
|
||||||
|
if (!lc.isRunning())
|
||||||
{
|
{
|
||||||
LifeCycle lc = (LifeCycle)obj;
|
lc.start();
|
||||||
if (!lc.isRunning())
|
if (lc.isStarted())
|
||||||
|
started.add(lc);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
lc.start();
|
// Failed to start a component, so stop all started components
|
||||||
if (lc.isStarted())
|
Collections.reverse(started);
|
||||||
started.add(lc);
|
for (LifeCycle slc : started)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// Failed to start a component, so stop all started components
|
slc.stop();
|
||||||
Collections.reverse(started);
|
|
||||||
for (LifeCycle slc : started)
|
|
||||||
{
|
|
||||||
slc.stop();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (Error | Exception e)
|
catch (Error | Exception e)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue