ARTEMIS-4766 - validate type before newInstance calls

This commit is contained in:
Gary Tully 2024-05-09 11:30:57 +01:00
parent 2f77ca2abb
commit f5973d53e6
17 changed files with 95 additions and 79 deletions

View File

@ -39,6 +39,7 @@ import org.apache.activemq.artemis.dto.ManagementContextDTO;
import org.apache.activemq.artemis.integration.Broker;
import org.apache.activemq.artemis.integration.bootstrap.ActiveMQBootstrapLogger;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.activemq.artemis.utils.ReusableLatch;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@ -139,8 +140,7 @@ public class Run extends LockAbstract {
"system-" + systemWebPropertyPrefix, System.getProperties(), systemWebPropertyPrefix);
for (ComponentDTO componentDTO : broker.components) {
Class clazz = this.getClass().getClassLoader().loadClass(componentDTO.componentClassName);
ExternalComponent component = (ExternalComponent) clazz.getDeclaredConstructor(null).newInstance();
ExternalComponent component = (ExternalComponent) ClassloadingUtil.getInstanceWithTypeCheck(componentDTO.componentClassName, ExternalComponent.class, this.getClass().getClassLoader());
component.configure(componentDTO, getBrokerInstance(), getBrokerHome());
server.getServer().addExternalComponent(component, true);
assert component.isStarted();

View File

@ -25,6 +25,7 @@ import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.cli.factory.serialize.MessageSerializer;
import org.apache.activemq.artemis.cli.factory.serialize.XMLMessageSerializer;
import org.apache.activemq.artemis.jms.client.ActiveMQDestination;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import picocli.CommandLine.Option;
public class DestAbstract extends ConnectionAbstract {
@ -53,7 +54,7 @@ public class DestAbstract extends ConnectionAbstract {
protected MessageSerializer getMessageSerializer() {
if (serializer != null) {
try {
return (MessageSerializer) Class.forName(serializer).getConstructor().newInstance();
return (MessageSerializer) ClassloadingUtil.getInstanceWithTypeCheck(serializer, MessageSerializer.class, this.getClass().getClassLoader());
} catch (Exception e) {
getActionContext().err.println("Error: unable to instantiate serializer class: " + serializer);
getActionContext().err.println("Defaulting to: " + XMLMessageSerializer.class.getName());

View File

@ -39,6 +39,6 @@ public class SecurityManagerHandler implements SecurityHandler {
properties.put(property.key, property.value);
}
}
return AccessController.doPrivileged((PrivilegedAction<ActiveMQSecurityManager>) () -> (ActiveMQSecurityManager) ClassloadingUtil.newInstanceFromClassLoader(SecurityManagerHandler.class, clazz)).init(properties);
return AccessController.doPrivileged((PrivilegedAction<ActiveMQSecurityManager>) () -> (ActiveMQSecurityManager) ClassloadingUtil.newInstanceFromClassLoader(SecurityManagerHandler.class, clazz, ActiveMQSecurityManager.class)).init(properties);
}
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.activemq.artemis.utils;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Properties;
@ -36,15 +37,16 @@ public final class ClassloadingUtil {
private static final String INSTANTIATION_EXCEPTION_MESSAGE = "Your class must have a constructor without arguments. If it is an inner class, it must be static!";
public static Object newInstanceFromClassLoader(final String className) {
return newInstanceFromClassLoader(ClassloadingUtil.class, className);
public static Object newInstanceFromClassLoader(final String className, Class<?> expectedType) {
return newInstanceFromClassLoader(ClassloadingUtil.class, className, expectedType);
}
public static Object newInstanceFromClassLoader(final Class<?> classOwner, final String className) {
public static Object newInstanceFromClassLoader(final Class<?> classOwner,
final String className,
Class<?> expectedType) {
ClassLoader loader = classOwner.getClassLoader();
try {
Class<?> clazz = loader.loadClass(className);
return clazz.newInstance();
return getInstanceWithTypeCheck(className, expectedType, loader);
} catch (Throwable t) {
if (t instanceof InstantiationException) {
System.out.println(INSTANTIATION_EXCEPTION_MESSAGE);
@ -54,48 +56,39 @@ public final class ClassloadingUtil {
throw new RuntimeException("No local context classloader", t);
try {
return loader.loadClass(className).newInstance();
return getInstanceWithTypeCheck(className, expectedType, loader);
} catch (InstantiationException e) {
throw new RuntimeException(INSTANTIATION_EXCEPTION_MESSAGE + " " + className, e);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
public static Object newInstanceFromClassLoader(final String className, Object... objs) {
return newInstanceFromClassLoader(ClassloadingUtil.class, className, objs);
public static Object getInstanceWithTypeCheck(String className,
Class<?> expectedType,
ClassLoader loader) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
final Class<?> clazz = loadWithCheck(className, expectedType, loader);
return clazz.getDeclaredConstructor().newInstance();
}
public static Object newInstanceFromClassLoader(final Class<?> classOwner, final String className, Object... objs) {
ClassLoader loader = classOwner.getClassLoader();
try {
Class<?>[] parametersType = new Class<?>[objs.length];
for (int i = 0; i < objs.length; i++) {
parametersType[i] = objs[i].getClass();
public static Object getInstanceForParamsWithTypeCheck(String className,
Class<?> expectedType,
ClassLoader loader, Class<?>[] parameterTypes, Object... params) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
final Class<?> clazz = loadWithCheck(className, expectedType, loader);
return clazz.getDeclaredConstructor(parameterTypes).newInstance(params);
}
private static Class<?> loadWithCheck(String className,
Class<?> expectedType,
ClassLoader loader) throws ClassNotFoundException {
Class<?> clazz = loader.loadClass(className);
return clazz.getConstructor(parametersType).newInstance(objs);
} catch (Throwable t) {
if (t instanceof InstantiationException) {
System.out.println(INSTANTIATION_EXCEPTION_MESSAGE);
}
loader = Thread.currentThread().getContextClassLoader();
if (loader == null)
throw new RuntimeException("No local context classloader", t);
try {
return loader.loadClass(className).newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(INSTANTIATION_EXCEPTION_MESSAGE + " " + className, e);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
if (!expectedType.isAssignableFrom(clazz)) {
throw new IllegalStateException("clazz [" + className + "] is not assignable from expected type: " + expectedType);
}
return clazz;
}
public static URL findResource(final String resourceName) {

View File

@ -1153,7 +1153,7 @@ public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal, C
return AccessController.doPrivileged(new PrivilegedAction<ConnectorFactory>() {
@Override
public ConnectorFactory run() {
return (ConnectorFactory) ClassloadingUtil.newInstanceFromClassLoader(ClientSessionFactoryImpl.class, connectorFactoryClassName);
return (ConnectorFactory) ClassloadingUtil.newInstanceFromClassLoader(ClientSessionFactoryImpl.class, connectorFactoryClassName, ConnectorFactory.class);
}
});
}

View File

@ -256,7 +256,7 @@ public final class ServerLocatorImpl implements ServerLocatorInternal, Discovery
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
loadBalancingPolicy = (ConnectionLoadBalancingPolicy) ClassloadingUtil.newInstanceFromClassLoader(ServerLocatorImpl.class, config.connectionLoadBalancingPolicyClassName);
loadBalancingPolicy = (ConnectionLoadBalancingPolicy) ClassloadingUtil.newInstanceFromClassLoader(ServerLocatorImpl.class, config.connectionLoadBalancingPolicyClassName, ConnectionLoadBalancingPolicy.class);
return null;
}
});
@ -1949,7 +1949,7 @@ public final class ServerLocatorImpl implements ServerLocatorInternal, Discovery
String[] arrayInterceptor = interceptorList.split(",");
for (String strValue : arrayInterceptor) {
Interceptor interceptor = (Interceptor) ClassloadingUtil.newInstanceFromClassLoader(ServerLocatorImpl.class, strValue.trim());
Interceptor interceptor = (Interceptor) ClassloadingUtil.newInstanceFromClassLoader(ServerLocatorImpl.class, strValue.trim(), Interceptor.class);
interceptors.add(interceptor);
}
return null;

View File

@ -25,6 +25,7 @@ import org.apache.activemq.artemis.core.config.federation.FederationPolicy;
import org.apache.activemq.artemis.core.config.federation.FederationStreamConfiguration;
import org.apache.activemq.artemis.core.config.federation.FederationTransformerConfiguration;
import org.apache.activemq.artemis.core.protocol.core.impl.PacketImpl;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.activemq.artemis.utils.Preconditions;
public abstract class FederationStreamConnectMessage <T extends FederationStreamConfiguration> extends PacketImpl {
@ -148,7 +149,7 @@ public abstract class FederationStreamConnectMessage <T extends FederationStream
private FederationPolicy getFederationPolicy(String clazz) {
try {
return (FederationPolicy) Class.forName(clazz).getConstructor((Class<?>[]) null).newInstance();
return (FederationPolicy) ClassloadingUtil.getInstanceWithTypeCheck(clazz, FederationPolicy.class, this.getClass().getClassLoader());
} catch (Exception e) {
throw new IllegalStateException("Error. Unable to instantiate FederationPolicy: " + e.getMessage(), e);
}

View File

@ -46,7 +46,7 @@ public class TransportConfigurationUtil {
}
if (!DEFAULTS.containsKey(className)) {
Object object = instantiateObject(className);
Object object = instantiateObject(className, TransportConfigurationHelper.class);
if (object != null && object instanceof TransportConfigurationHelper) {
DEFAULTS.put(className, ((TransportConfigurationHelper) object).getDefaults());
@ -60,12 +60,12 @@ public class TransportConfigurationUtil {
return cloneDefaults(DEFAULTS.get(className));
}
private static Object instantiateObject(final String className) {
private static Object instantiateObject(final String className, final Class expectedType) {
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return ClassloadingUtil.newInstanceFromClassLoader(TransportConfigurationUtil.class, className);
return ClassloadingUtil.newInstanceFromClassLoader(TransportConfigurationUtil.class, className, expectedType);
} catch (IllegalStateException e) {
return null;
}

View File

@ -278,7 +278,7 @@ public class SSLSupport {
private TrustManagerFactory loadTrustManagerFactory() throws Exception {
if (trustManagerFactoryPlugin != null) {
return AccessController.doPrivileged((PrivilegedAction<TrustManagerFactory>) () -> ((TrustManagerFactoryPlugin) ClassloadingUtil.newInstanceFromClassLoader(SSLSupport.class, trustManagerFactoryPlugin)).getTrustManagerFactory());
return AccessController.doPrivileged((PrivilegedAction<TrustManagerFactory>) () -> ((TrustManagerFactoryPlugin) ClassloadingUtil.newInstanceFromClassLoader(SSLSupport.class, trustManagerFactoryPlugin, TrustManagerFactoryPlugin.class)).getTrustManagerFactory());
} else if (trustAll) {
//This is useful for testing but not should be used outside of that purpose
return InsecureTrustManagerFactory.INSTANCE;

View File

@ -17,6 +17,7 @@
package org.apache.activemq.artemis.jdbc.store.drivers;
import org.apache.activemq.artemis.journal.ActiveMQJournalLogger;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.commons.beanutils.PropertyUtils;
import javax.sql.DataSource;
@ -32,7 +33,7 @@ public class JDBCDataSourceUtils {
.map(key -> key + "=" + (key.equalsIgnoreCase("password") ? "****" : dataSourceProperties.get(key)))
.collect(Collectors.joining(", ", "{", "}")));
try {
DataSource dataSource = (DataSource) Class.forName(dataSourceClassName).getDeclaredConstructor().newInstance();
DataSource dataSource = (DataSource) ClassloadingUtil.getInstanceWithTypeCheck(dataSourceClassName, DataSource.class, JDBCDataSourceUtils.class.getClassLoader());
for (Map.Entry<String, Object> entry : dataSourceProperties.entrySet()) {
PropertyUtils.setProperty(dataSource, entry.getKey(), entry.getValue());
}

View File

@ -158,7 +158,7 @@ public class ActiveMQConnectionFactory extends JNDIStorable implements Connectio
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
ClientProtocolManagerFactory protocolManagerFactory = (ClientProtocolManagerFactory) ClassloadingUtil.newInstanceFromClassLoader(ActiveMQConnectionFactory.class, protocolManagerFactoryStr);
ClientProtocolManagerFactory protocolManagerFactory = (ClientProtocolManagerFactory) ClassloadingUtil.newInstanceFromClassLoader(ActiveMQConnectionFactory.class, protocolManagerFactoryStr, ClientProtocolManagerFactory.class );
serverLocator.setProtocolManagerFactory(protocolManagerFactory);
return null;
}

View File

@ -30,4 +30,12 @@
<properties>
<activemq.basedir>${project.basedir}/../..</activemq.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>artemis-commons</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -21,10 +21,16 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
public interface DistributedLockManager extends AutoCloseable {
static DistributedLockManager newInstanceOf(String className, Map<String, String> properties) throws Exception {
return (DistributedLockManager) Class.forName(className).getDeclaredConstructor(Map.class).newInstance(properties);
return (DistributedLockManager) ClassloadingUtil.getInstanceForParamsWithTypeCheck(className,
DistributedLockManager.class,
DistributedLockManager.class.getClassLoader(),
new Class[]{Map.class},
properties);
}
@FunctionalInterface

View File

@ -115,6 +115,7 @@ import org.apache.activemq.artemis.json.JsonArrayBuilder;
import org.apache.activemq.artemis.json.JsonObject;
import org.apache.activemq.artemis.json.JsonObjectBuilder;
import org.apache.activemq.artemis.utils.ByteUtil;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.activemq.artemis.utils.Env;
import org.apache.activemq.artemis.utils.JsonLoader;
import org.apache.activemq.artemis.utils.ObjectInputStreamWithClassLoader;
@ -800,8 +801,7 @@ public class ConfigurationImpl implements Configuration, Serializable {
if (type != String.class && possibleDotClassValue.endsWith(DOT_CLASS)) {
final String clazzName = possibleDotClassValue.substring(0, possibleDotClassValue.length() - DOT_CLASS.length());
try {
Class clazzType = this.getClass().getClassLoader().loadClass(clazzName);
newValue = clazzType.getDeclaredConstructor().newInstance();
newValue = ClassloadingUtil.getInstanceWithTypeCheck(clazzName, type, this.getClass().getClassLoader());
} catch (Exception e) {
throw new InvocationTargetException(e, " for dot class value: " + possibleDotClassValue + ", on: " + bean);
}
@ -2352,6 +2352,11 @@ public class ConfigurationImpl implements Configuration, Serializable {
return brokerPlugins;
}
// for properties type inference
public void addBrokerPlugin(ActiveMQServerBasePlugin type) {
registerBrokerPlugin(type);
}
@Override
public List<ActiveMQServerConnectionPlugin> getBrokerConnectionPlugins() {
return brokerConnectionPlugins;
@ -3584,10 +3589,6 @@ public class ConfigurationImpl implements Configuration, Serializable {
Object instance = null;
try {
if (name.indexOf(DOT_CLASS) > 0) {
final String clazzName = name.substring(0, name.length() - DOT_CLASS.length());
instance = this.getClass().getClassLoader().loadClass(clazzName).getDeclaredConstructor().newInstance();
} else {
// we don't know the type, infer from add method add(X x) or add(String key, X x)
final String addPropertyName = addPropertyNameBuilder.toString();
final Method[] methods = hostingBean.getClass().getMethods();
@ -3600,11 +3601,15 @@ public class ConfigurationImpl implements Configuration, Serializable {
if (candidate == null) {
throw new IllegalArgumentException("failed to locate add method for collection property " + addPropertyName);
}
Class type = candidate.getParameterTypes()[candidate.getParameterCount() - 1];
instance = candidate.getParameterTypes()[candidate.getParameterCount() - 1].getDeclaredConstructor().newInstance();
if (name.indexOf(DOT_CLASS) > 0) {
final String clazzName = name.substring(0, name.length() - DOT_CLASS.length());
instance = ClassloadingUtil.getInstanceWithTypeCheck(clazzName, type, this.getClass().getClassLoader());
} else {
instance = type.getDeclaredConstructor().newInstance();
}
// initialise with name
try {
beanUtilsBean.setProperty(instance, "name", name);
} catch (Throwable ignored) {
@ -3617,7 +3622,7 @@ public class ConfigurationImpl implements Configuration, Serializable {
if (logger.isDebugEnabled()) {
logger.debug("Failed to add entry for {} to collection: {}", name, hostingBean, e);
}
throw new IllegalArgumentException("failed to add entry for collection key " + name, e);
throw new IllegalArgumentException("failed to add entry for collection key " + name + ", cause " + e.getMessage(), e);
}
}

View File

@ -987,7 +987,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
ActiveMQServerPlugin serverPlugin = AccessController.doPrivileged(new PrivilegedAction<ActiveMQServerPlugin>() {
@Override
public ActiveMQServerPlugin run() {
return (ActiveMQServerPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz);
return (ActiveMQServerPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz, ActiveMQServerPlugin.class);
}
});
@ -1066,7 +1066,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
ActiveMQMetricsPlugin metricsPlugin = AccessController.doPrivileged(new PrivilegedAction<ActiveMQMetricsPlugin>() {
@Override
public ActiveMQMetricsPlugin run() {
return (ActiveMQMetricsPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz);
return (ActiveMQMetricsPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz, ActiveMQMetricsPlugin.class);
}
});
@ -1273,7 +1273,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
SecuritySettingPlugin securitySettingPlugin = AccessController.doPrivileged(new PrivilegedAction<SecuritySettingPlugin>() {
@Override
public SecuritySettingPlugin run() {
return (SecuritySettingPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz);
return (SecuritySettingPlugin) ClassloadingUtil.newInstanceFromClassLoader(FileConfigurationParser.class, clazz, SecuritySettingPlugin.class);
}
});

View File

@ -122,7 +122,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
if (configs != null) {
for (final ConnectorServiceConfiguration config : configs) {
if (connectorServices.get(config.getConnectorName()) == null) {
ConnectorServiceFactory factory = loadClass(config.getFactoryClassName());
ConnectorServiceFactory factory = loadClass(config.getFactoryClassName(), ConnectorServiceFactory.class);
addConnectorService(factory, config);
}
}
@ -133,7 +133,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
@Override
public ConnectorServiceFactory getConnectorService(ConnectorServiceConfiguration configuration) {
return loadClass(configuration.getFactoryClassName());
return loadClass(configuration.getFactoryClassName(), ConnectorServiceFactory.class);
}
@Override
@ -235,7 +235,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
AcceptorFactory factory = acceptorFactories.get(name);
if (factory == null && className != null) {
factory = loadClass(className);
factory = loadClass(className, AcceptorFactory.class);
addAcceptorFactory(name, factory);
}
@ -248,11 +248,11 @@ public class ServiceRegistryImpl implements ServiceRegistry {
}
@SuppressWarnings("TypeParameterUnusedInFormals")
public <T> T loadClass(final String className) {
public <T> T loadClass(final String className, Class expectedType) {
return AccessController.doPrivileged(new PrivilegedAction<T>() {
@Override
public T run() {
return (T) ClassloadingUtil.newInstanceFromClassLoader(className);
return (T) ClassloadingUtil.newInstanceFromClassLoader(className, expectedType);
}
});
}
@ -262,7 +262,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
if (transformerConfiguration != null && transformerConfiguration.getClassName() != null) {
try {
transformer = new RegisteredTransformer(loadClass(transformerConfiguration.getClassName()));
transformer = new RegisteredTransformer(loadClass(transformerConfiguration.getClassName(), Transformer.class));
transformer.init(Collections.unmodifiableMap(transformerConfiguration.getProperties()));
} catch (Exception e) {
throw ActiveMQMessageBundle.BUNDLE.errorCreatingTransformerClass(transformerConfiguration.getClassName(), e);
@ -274,7 +274,7 @@ public class ServiceRegistryImpl implements ServiceRegistry {
private void instantiateInterceptors(List<String> classNames, List<BaseInterceptor> interceptors) {
if (classNames != null) {
for (final String className : classNames) {
BaseInterceptor interceptor = loadClass(className);
BaseInterceptor interceptor = loadClass(className, BaseInterceptor.class);
interceptors.add(interceptor);
}
}

View File

@ -41,6 +41,7 @@ import org.apache.activemq.artemis.dto.BindingDTO;
import org.apache.activemq.artemis.dto.ComponentDTO;
import org.apache.activemq.artemis.dto.WebServerDTO;
import org.apache.activemq.artemis.marker.WebServerComponentMarker;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.activemq.artemis.utils.PemConfigUtil;
import org.eclipse.jetty.security.DefaultAuthenticatorFactory;
import org.eclipse.jetty.server.ConnectionFactory;
@ -132,7 +133,7 @@ public class WebServerComponent implements ExternalComponent, WebServerComponent
if (this.webServerConfig.customizer != null) {
try {
httpConfiguration.addCustomizer((HttpConfiguration.Customizer) Class.forName(this.webServerConfig.customizer).getConstructor().newInstance());
httpConfiguration.addCustomizer((HttpConfiguration.Customizer) ClassloadingUtil.getInstanceWithTypeCheck(this.webServerConfig.customizer, HttpConfiguration.Customizer.class, this.getClass().getClassLoader()));
} catch (Throwable t) {
ActiveMQWebLogger.LOGGER.customizerNotLoaded(this.webServerConfig.customizer, t);
}