diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/pom.xml b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/pom.xml index 55d1e8adf6..306a272202 100644 --- a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/pom.xml +++ b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/pom.xml @@ -80,4 +80,19 @@ test + + + + + org.apache.rat + apache-rat-plugin + + + src/test/resources/dummy.conf + + + + + + diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java index e9c24d53af..c61305c6d0 100644 --- a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java +++ b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Map.Entry; import javax.net.ssl.SSLContext; -import java.io.File; import javax.jms.ConnectionFactory; @@ -44,6 +43,7 @@ import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService.ClientAuth; +import org.apache.nifi.util.file.classloader.ClassLoaderUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,9 +78,9 @@ public class JMSConnectionFactoryProvider extends AbstractControllerService impl private volatile ConnectionFactory connectionFactory; - static final String BROKER = "broker"; - static final String CF_IMPL = "cf"; - static final String CF_LIB = "cflib"; + private static final String BROKER = "broker"; + private static final String CF_IMPL = "cf"; + private static final String CF_LIB = "cflib"; public static final PropertyDescriptor CONNECTION_FACTORY_IMPL = new PropertyDescriptor.Builder() .name(CF_IMPL) @@ -97,8 +97,9 @@ public class JMSConnectionFactoryProvider extends AbstractControllerService impl .description("Path to the directory with additional resources (i.e., JARs, configuration files etc.) to be added " + "to the classpath. Such resources typically represent target MQ client libraries for the " + "ConnectionFactory implementation.") - .addValidator(new ClientLibValidator()) + .addValidator(StandardValidators.createListValidator(true, true, StandardValidators.createURLorFileValidator())) .required(true) + .dynamicallyModifiesClasspath(true) .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) .build(); @@ -159,13 +160,15 @@ public class JMSConnectionFactoryProvider extends AbstractControllerService impl if (logger.isInfoEnabled()) { logger.info("Configuring " + this.getClass().getSimpleName() + " for '" + context.getProperty(CONNECTION_FACTORY_IMPL).evaluateAttributeExpressions().getValue() + "' to be connected to '" - + BROKER_URI + "'"); + + context.getProperty(BROKER_URI).evaluateAttributeExpressions().getValue() + "'"); } + // will load user provided libraries/resources on the classpath - Utils.addResourcesToClasspath(context.getProperty(CLIENT_LIB_DIR_PATH).evaluateAttributeExpressions().getValue()); + final String clientLibPath = context.getProperty(CLIENT_LIB_DIR_PATH).evaluateAttributeExpressions().getValue(); + ClassLoader customClassLoader = ClassLoaderUtils.getCustomClassLoader(clientLibPath, this.getClass().getClassLoader(), null); + Thread.currentThread().setContextClassLoader(customClassLoader); this.createConnectionFactoryInstance(context); - this.setConnectionFactoryProperties(context); } this.configured = true; @@ -312,48 +315,13 @@ public class JMSConnectionFactoryProvider extends AbstractControllerService impl * evaluation */ static class NonEmptyBrokerURIValidator implements Validator { - @Override public ValidationResult validate(String subject, String input, ValidationContext context) { - String value = input; if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) { - value = context.getProperty(BROKER_URI).evaluateAttributeExpressions().getValue(); + return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build(); } - return StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, value, context); + return StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input, context); } } - /** - * - */ - static class ClientLibValidator implements Validator { - - @Override - public ValidationResult validate(String subject, String input, ValidationContext context) { - String libDirPath = context.getProperty(CLIENT_LIB_DIR_PATH).evaluateAttributeExpressions().getValue(); - StringBuilder invalidationMessageBuilder = new StringBuilder(); - if (libDirPath != null) { - File file = new File(libDirPath); - if (!file.isDirectory()) { - invalidationMessageBuilder - .append("MQ Client library directory path must point to a directory. Was '") - .append(file.getAbsolutePath()) - .append("'."); - } - } else { - invalidationMessageBuilder.append("'MQ Client Libraries path' must be provided. \n"); - } - String invalidationMessage = invalidationMessageBuilder.toString(); - ValidationResult vResult; - if (invalidationMessage.length() == 0) { - vResult = new ValidationResult.Builder().subject(subject).input(input) - .explanation("Client lib path is valid and points to a directory").valid(true).build(); - } else { - vResult = new ValidationResult.Builder().subject(subject).input(input) - .explanation("Client lib path is invalid. " + invalidationMessage) - .valid(false).build(); - } - return vResult; - } - } } diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/Utils.java b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/Utils.java index 912f2c0b32..a70d8e3f33 100644 --- a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/Utils.java +++ b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/main/java/org/apache/nifi/jms/cf/Utils.java @@ -16,10 +16,7 @@ */ package org.apache.nifi.jms.cf; -import java.io.File; import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -101,40 +98,4 @@ public final class Utils { } } - /** - * Adds content of the directory specified with 'path' to the classpath. It - * does so by creating a new instance of the {@link URLClassLoader} using - * {@link URL}s created from listing the contents of the directory denoted - * by 'path' and setting it as thread context class loader. - */ - static void addResourcesToClasspath(String path) { - if (logger.isDebugEnabled()) { - logger.debug("Adding additional resources from '" + path + "' to the classpath."); - } - if (path == null) { - throw new IllegalArgumentException("'path' must not be null"); - } - File libraryDir = new File(path); - if (libraryDir.exists() && libraryDir.isDirectory()) { - String[] cpResourceNames = libraryDir.list(); - URL[] urls = new URL[cpResourceNames.length]; - try { - for (int i = 0; i < urls.length; i++) { - urls[i] = new File(libraryDir, cpResourceNames[i]).toURI().toURL(); - if (logger.isDebugEnabled()) { - logger.debug("Identifying additional resource to the classpath: " + urls[i]); - } - } - } catch (Exception e) { - throw new IllegalStateException( - "Failed to parse user libraries from '" + libraryDir.getAbsolutePath() + "'", e); - } - - URLClassLoader cl = new URLClassLoader(urls, Utils.class.getClassLoader()); - Thread.currentThread().setContextClassLoader(cl); - } else { - throw new IllegalArgumentException("Path '" + libraryDir.getAbsolutePath() - + "' is not valid because it doesn't exist or does not point to a directory."); - } - } } \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProviderTest.java b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProviderTest.java index 9ddfb3f4d5..25f4398f75 100644 --- a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProviderTest.java +++ b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/java/org/apache/nifi/jms/cf/JMSConnectionFactoryProviderTest.java @@ -17,12 +17,16 @@ package org.apache.nifi.jms.cf; import org.apache.nifi.processor.Processor; +import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunners; +import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URISyntaxException; + import static org.mockito.Mockito.mock; /** @@ -46,16 +50,50 @@ public class JMSConnectionFactoryProviderTest { } @Test - public void validateNotValidForNonDirectoryPath() throws Exception { + public void validateELExpression() throws InitializationException, URISyntaxException { TestRunner runner = TestRunners.newTestRunner(mock(Processor.class)); + runner.setValidateExpressionUsage(true); JMSConnectionFactoryProvider cfProvider = new JMSConnectionFactoryProvider(); + String clientLib = this.getClass().getResource("/dummy-lib.jar").toURI().toString(); runner.addControllerService("cfProvider", cfProvider); - runner.setProperty(cfProvider, JMSConnectionFactoryProvider.BROKER_URI, "myhost:1234"); - runner.setProperty(cfProvider, JMSConnectionFactoryProvider.CLIENT_LIB_DIR_PATH, "pom.xml"); + runner.setVariable("broker.uri", "tcp://0.0.0.0:616161"); + runner.setVariable("client.lib", clientLib); + + runner.setProperty(cfProvider, JMSConnectionFactoryProvider.BROKER_URI, "${broker.uri}"); + runner.setProperty(cfProvider, JMSConnectionFactoryProvider.CLIENT_LIB_DIR_PATH, "${client.lib}"); runner.setProperty(cfProvider, JMSConnectionFactoryProvider.CONNECTION_FACTORY_IMPL, "org.apache.nifi.jms.testcflib.TestConnectionFactory"); - runner.assertNotValid(cfProvider); + runner.assertValid(cfProvider); + } + + @Test + public void testClientLibResourcesLoaded() throws InitializationException, URISyntaxException { + TestRunner runner = TestRunners.newTestRunner(mock(Processor.class)); + runner.setValidateExpressionUsage(true); + + JMSConnectionFactoryProvider cfProvider = new JMSConnectionFactoryProvider(); + + String clientLib = this.getClass().getResource("/dummy-lib.jar").toURI().toString() + "," + + this.getClass().getResource("/dummy-lib-2.jar").toURI().toString() + "," + + this.getClass().getResource("/dummy.conf").toURI().toString() + ","; + + runner.addControllerService("cfProvider", cfProvider); + + runner.setVariable("broker.uri", "tcp://0.0.0.0:616161"); + runner.setVariable("client.lib", clientLib); + + runner.setProperty(cfProvider, JMSConnectionFactoryProvider.BROKER_URI, "${broker.uri}"); + runner.setProperty(cfProvider, JMSConnectionFactoryProvider.CLIENT_LIB_DIR_PATH, "${client.lib}"); + runner.setProperty(cfProvider, JMSConnectionFactoryProvider.CONNECTION_FACTORY_IMPL, + "org.apache.nifi.jms.testcflib.TestConnectionFactory"); + + runner.assertValid(cfProvider); + + ClassLoader loader = runner.getClass().getClassLoader(); + Assert.assertTrue(loader.getResource("dummy.conf") != null); + Assert.assertTrue(loader.getResource("dummy-lib.jar") != null); + Assert.assertTrue(loader.getResource("dummy-lib-2.jar") != null); } @Test(expected = IllegalStateException.class) diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy-lib-2.jar b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy-lib-2.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy-lib.jar b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy-lib.jar new file mode 100644 index 0000000000..e69de29bb2 diff --git a/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy.conf b/nifi-nar-bundles/nifi-jms-bundle/nifi-jms-processors/src/test/resources/dummy.conf new file mode 100644 index 0000000000..e69de29bb2