ARTEMIS-4766 - validate type before newInstance calls
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
index e5200a9..219a581 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
@@ -39,6 +39,7 @@
 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 @@
             "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();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
index 88c87c2..f059d10 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/messages/DestAbstract.java
@@ -25,6 +25,7 @@
 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 @@
    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());
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/security/SecurityManagerHandler.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/security/SecurityManagerHandler.java
index 4c1e10b..61d9839 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/security/SecurityManagerHandler.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/security/SecurityManagerHandler.java
@@ -39,6 +39,6 @@
             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);
    }
 }
diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java
index 2a26c41..2a40199 100644
--- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java
+++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ClassloadingUtil.java
@@ -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 @@
 
    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 @@
             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();
-         }
-         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);
+   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);
+   }
 
-         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);
-         }
+   private static Class<?> loadWithCheck(String className,
+                                         Class<?> expectedType,
+                                         ClassLoader loader) throws ClassNotFoundException {
+      Class<?> clazz = loader.loadClass(className);
+      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) {
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
index d5fa8a6..d7dc12f 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
@@ -1153,7 +1153,7 @@
       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);
          }
       });
    }
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
index 9c67b3f..165b551 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
@@ -256,7 +256,7 @@
       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 @@
 
             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;
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/FederationStreamConnectMessage.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/FederationStreamConnectMessage.java
index ac867dc..64bf823 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/FederationStreamConnectMessage.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/protocol/core/impl/wireformat/FederationStreamConnectMessage.java
@@ -25,6 +25,7 @@
 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 @@
 
    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);
       }
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/TransportConfigurationUtil.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/TransportConfigurationUtil.java
index 33bfbac..2083991 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/TransportConfigurationUtil.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/TransportConfigurationUtil.java
@@ -46,7 +46,7 @@
       }
 
       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 @@
       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;
             }
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
index eb4d04c..8c600e4 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/remoting/impl/ssl/SSLSupport.java
@@ -278,7 +278,7 @@
 
    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;
diff --git a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/JDBCDataSourceUtils.java b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/JDBCDataSourceUtils.java
index d71e19b..0018443 100644
--- a/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/JDBCDataSourceUtils.java
+++ b/artemis-jdbc-store/src/main/java/org/apache/activemq/artemis/jdbc/store/drivers/JDBCDataSourceUtils.java
@@ -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 @@
          .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());
          }
diff --git a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
index 7927d4f..b8e89c7 100644
--- a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
+++ b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
@@ -158,7 +158,7 @@
          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;
             }
diff --git a/artemis-lockmanager/artemis-lockmanager-api/pom.xml b/artemis-lockmanager/artemis-lockmanager-api/pom.xml
index ef14152..e8fe4c8 100644
--- a/artemis-lockmanager/artemis-lockmanager-api/pom.xml
+++ b/artemis-lockmanager/artemis-lockmanager-api/pom.xml
@@ -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>
diff --git a/artemis-lockmanager/artemis-lockmanager-api/src/main/java/org/apache/activemq/artemis/lockmanager/DistributedLockManager.java b/artemis-lockmanager/artemis-lockmanager-api/src/main/java/org/apache/activemq/artemis/lockmanager/DistributedLockManager.java
index d58ddea..d42f8e9 100644
--- a/artemis-lockmanager/artemis-lockmanager-api/src/main/java/org/apache/activemq/artemis/lockmanager/DistributedLockManager.java
+++ b/artemis-lockmanager/artemis-lockmanager-api/src/main/java/org/apache/activemq/artemis/lockmanager/DistributedLockManager.java
@@ -21,10 +21,16 @@
 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
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
index a8a0bc2..0b78fab 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java
@@ -115,6 +115,7 @@
 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 @@
                      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 @@
       return brokerPlugins;
    }
 
+   // for properties type inference
+   public void addBrokerPlugin(ActiveMQServerBasePlugin type) {
+      registerBrokerPlugin(type);
+   }
+
    @Override
    public List<ActiveMQServerConnectionPlugin> getBrokerConnectionPlugins() {
       return brokerConnectionPlugins;
@@ -3584,27 +3589,27 @@
 
          Object instance = null;
          try {
+            // 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();
+            final Method candidate = Arrays.stream(methods).filter(method -> method.getName().equals(addPropertyName) && ((method.getParameterCount() == 1) || (method.getParameterCount() == 2
+               // has a String key
+               && String.class.equals(method.getParameterTypes()[0])
+               // but not initialised from a String form (eg: uri)
+               && !String.class.equals(method.getParameterTypes()[1])))).sorted((method1, method2) -> method2.getParameterCount() - method1.getParameterCount()).findFirst().orElse(null);
+
+            if (candidate == null) {
+               throw new IllegalArgumentException("failed to locate add method for collection property " + addPropertyName);
+            }
+            Class type = candidate.getParameterTypes()[candidate.getParameterCount() - 1];
+
             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();
+               instance = ClassloadingUtil.getInstanceWithTypeCheck(clazzName, type, this.getClass().getClassLoader());
             } 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();
-               final Method candidate = Arrays.stream(methods).filter(method -> method.getName().equals(addPropertyName) && ((method.getParameterCount() == 1) || (method.getParameterCount() == 2
-                  // has a String key
-                  && String.class.equals(method.getParameterTypes()[0])
-                  // but not initialised from a String form (eg: uri)
-                  && !String.class.equals(method.getParameterTypes()[1])))).sorted((method1, method2) -> method2.getParameterCount() - method1.getParameterCount()).findFirst().orElse(null);
-
-               if (candidate == null) {
-                  throw new IllegalArgumentException("failed to locate add method for collection property " + addPropertyName);
-               }
-
-               instance = candidate.getParameterTypes()[candidate.getParameterCount() - 1].getDeclaredConstructor().newInstance();
+               instance = type.getDeclaredConstructor().newInstance();
             }
             // initialise with name
-
             try {
                beanUtilsBean.setProperty(instance, "name", name);
             } catch (Throwable ignored) {
@@ -3617,7 +3622,7 @@
             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);
          }
       }
 
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java
index 99012fd..ac927ac 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java
@@ -987,7 +987,7 @@
       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 @@
       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 @@
       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);
          }
       });
 
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServiceRegistryImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServiceRegistryImpl.java
index 7a8df5f..6603fb1 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServiceRegistryImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServiceRegistryImpl.java
@@ -122,7 +122,7 @@
       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 @@
 
    @Override
    public ConnectorServiceFactory getConnectorService(ConnectorServiceConfiguration configuration) {
-      return loadClass(configuration.getFactoryClassName());
+      return loadClass(configuration.getFactoryClassName(), ConnectorServiceFactory.class);
    }
 
    @Override
@@ -235,7 +235,7 @@
       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 @@
    }
 
    @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 @@
 
       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 @@
    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);
          }
       }
diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
index 2093cc3..88c828e 100644
--- a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
+++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java
@@ -41,6 +41,7 @@
 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 @@
 
       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);
          }