From d3e62ed82acc856681d94da559060b5ce823e961 Mon Sep 17 00:00:00 2001 From: Julian Gimbel Date: Wed, 28 Feb 2018 13:39:32 +0100 Subject: [PATCH 1/3] looping over several methods to try and fit the dynamic attribute. If failed, use first method and throw error if not working. --- .../jms/cf/JMSConnectionFactoryProvider.java | 40 ++++++++++--------- .../java/org/apache/nifi/jms/cf/Utils.java | 35 +++++++++++++++- 2 files changed, 56 insertions(+), 19 deletions(-) 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 85d5ffbed321..eeb5c9daca9a 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 @@ -64,8 +64,8 @@ + "ConnectionFactory can be served once this service is configured successfully") @DynamicProperty(name = "The name of a Connection Factory configuration property.", value = "The value of a given Connection Factory configuration property.", description = "The properties that are set following Java Beans convention where a property name is derived from the 'set*' method of the vendor " - + "specific ConnectionFactory's implementation. For example, 'com.ibm.mq.jms.MQConnectionFactory.setChannel(String)' would imply 'channel' " - + "property and 'com.ibm.mq.jms.MQConnectionFactory.setTransportType(int)' would imply 'transportType' property.") + + "specific ConnectionFactory's implementation. For example, 'com.ibm.mq.jms.MQConnectionFactory.setChannel(String)' would imply 'channel' " + + "property and 'com.ibm.mq.jms.MQConnectionFactory.setTransportType(int)' would imply 'transportType' property.") @SeeAlso(classNames = {"org.apache.nifi.jms.processors.ConsumeJMS", "org.apache.nifi.jms.processors.PublishJMS"}) public class JMSConnectionFactoryProvider extends AbstractControllerService implements JMSConnectionFactoryProviderDefinition { @@ -138,7 +138,6 @@ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String } /** - * * @return new instance of {@link ConnectionFactory} */ @Override @@ -187,7 +186,7 @@ public void disable() { * service configuration. For example, 'channel' property will correspond to * 'setChannel(..) method and 'queueManager' property will correspond to * setQueueManager(..) method with a single argument. - * + *

* There are also few adjustments to accommodate well known brokers. For * example ActiveMQ ConnectionFactory accepts address of the Message Broker * in a form of URL while IBMs in the form of host/port pair (more common). @@ -243,7 +242,7 @@ private void setConnectionFactoryProperties(ConfigurationContext context) { * 'propertyName'. For example, 'channel' property will correspond to * 'setChannel(..) method and 'queueManager' property will correspond to * setQueueManager(..) method with a single argument. - * + *

* NOTE: There is a limited type conversion to accommodate property value * types since all NiFi configuration properties comes as String. It is * accomplished by checking the argument type of the method and executing @@ -257,21 +256,26 @@ private void setConnectionFactoryProperties(ConfigurationContext context) { */ private void setProperty(String propertyName, Object propertyValue) { String methodName = this.toMethodName(propertyName); - Method method = Utils.findMethod(methodName, this.connectionFactory.getClass()); - if (method != null) { + Method[] methods = Utils.findMethods(methodName, this.connectionFactory.getClass()); + if (methods != null && methods.length < 0) { try { - Class returnType = method.getParameterTypes()[0]; - if (String.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, propertyValue); - } else if (int.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Integer.parseInt((String) propertyValue)); - } else if (long.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Long.parseLong((String) propertyValue)); - } else if (boolean.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Boolean.parseBoolean((String) propertyValue)); - } else { - method.invoke(this.connectionFactory, propertyValue); + for (Method method : methods) { + Class returnType = method.getParameterTypes()[0]; + if (String.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, propertyValue); + return; + } else if (int.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Integer.parseInt((String) propertyValue)); + return; + } else if (long.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Long.parseLong((String) propertyValue)); + return; + } else if (boolean.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Boolean.parseBoolean((String) propertyValue)); + return; + } } + methods[0].invoke(this.connectionFactory, propertyValue); } catch (Exception e) { throw new IllegalStateException("Failed to set property " + propertyName, e); } 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 cd191c32ae07..912f2c0b3280 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 @@ -20,6 +20,9 @@ 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +53,7 @@ static T newDefaultInstance(String className) { * Finds a method by name on the target class. If more then one method * present it will return the first one encountered. * - * @param name method name + * @param name method name * @param targetClass instance of target class * @return instance of {@link Method} */ @@ -68,6 +71,36 @@ public static Method findMethod(String name, Class targetClass) { return null; } + /** + * Finds a method by name on the target class. If more then one method + * present it will return the first one encountered. + * + * @param name method name + * @param targetClass instance of target class + * @return Array of {@link Method} + */ + public static Method[] findMethods(String name, Class targetClass) { + Class searchType = targetClass; + ArrayList fittingMethods = new ArrayList<>(); + while (searchType != null) { + Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods()); + for (Method method : methods) { + if (name.equals(method.getName())) { + fittingMethods.add(method); + } + } + searchType = searchType.getSuperclass(); + } + if (fittingMethods.isEmpty()) { + return null; + } else { + //Sort so that in case there are two methods that accept the parameter type + //as first param use the one which accepts fewer parameters in total + Collections.sort(fittingMethods, Comparator.comparing(Method::getParameterCount)); + return fittingMethods.toArray(new Method[fittingMethods.size()]); + } + } + /** * Adds content of the directory specified with 'path' to the classpath. It * does so by creating a new instance of the {@link URLClassLoader} using From 255c8478fb2b4957bcc49c41ca069149387f3b32 Mon Sep 17 00:00:00 2001 From: Julian Gimbel Date: Wed, 28 Feb 2018 13:39:32 +0100 Subject: [PATCH 2/3] NIFI-4918 JMS Connection Factory setting the dynamic Properties wrong. Now looping over several methods to try and fit the dynamic attribute. If failed, use first method and throw error if not working. --- .../jms/cf/JMSConnectionFactoryProvider.java | 40 ++++++++++--------- .../java/org/apache/nifi/jms/cf/Utils.java | 35 +++++++++++++++- 2 files changed, 56 insertions(+), 19 deletions(-) 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 85d5ffbed321..eeb5c9daca9a 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 @@ -64,8 +64,8 @@ + "ConnectionFactory can be served once this service is configured successfully") @DynamicProperty(name = "The name of a Connection Factory configuration property.", value = "The value of a given Connection Factory configuration property.", description = "The properties that are set following Java Beans convention where a property name is derived from the 'set*' method of the vendor " - + "specific ConnectionFactory's implementation. For example, 'com.ibm.mq.jms.MQConnectionFactory.setChannel(String)' would imply 'channel' " - + "property and 'com.ibm.mq.jms.MQConnectionFactory.setTransportType(int)' would imply 'transportType' property.") + + "specific ConnectionFactory's implementation. For example, 'com.ibm.mq.jms.MQConnectionFactory.setChannel(String)' would imply 'channel' " + + "property and 'com.ibm.mq.jms.MQConnectionFactory.setTransportType(int)' would imply 'transportType' property.") @SeeAlso(classNames = {"org.apache.nifi.jms.processors.ConsumeJMS", "org.apache.nifi.jms.processors.PublishJMS"}) public class JMSConnectionFactoryProvider extends AbstractControllerService implements JMSConnectionFactoryProviderDefinition { @@ -138,7 +138,6 @@ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String } /** - * * @return new instance of {@link ConnectionFactory} */ @Override @@ -187,7 +186,7 @@ public void disable() { * service configuration. For example, 'channel' property will correspond to * 'setChannel(..) method and 'queueManager' property will correspond to * setQueueManager(..) method with a single argument. - * + *

* There are also few adjustments to accommodate well known brokers. For * example ActiveMQ ConnectionFactory accepts address of the Message Broker * in a form of URL while IBMs in the form of host/port pair (more common). @@ -243,7 +242,7 @@ private void setConnectionFactoryProperties(ConfigurationContext context) { * 'propertyName'. For example, 'channel' property will correspond to * 'setChannel(..) method and 'queueManager' property will correspond to * setQueueManager(..) method with a single argument. - * + *

* NOTE: There is a limited type conversion to accommodate property value * types since all NiFi configuration properties comes as String. It is * accomplished by checking the argument type of the method and executing @@ -257,21 +256,26 @@ private void setConnectionFactoryProperties(ConfigurationContext context) { */ private void setProperty(String propertyName, Object propertyValue) { String methodName = this.toMethodName(propertyName); - Method method = Utils.findMethod(methodName, this.connectionFactory.getClass()); - if (method != null) { + Method[] methods = Utils.findMethods(methodName, this.connectionFactory.getClass()); + if (methods != null && methods.length < 0) { try { - Class returnType = method.getParameterTypes()[0]; - if (String.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, propertyValue); - } else if (int.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Integer.parseInt((String) propertyValue)); - } else if (long.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Long.parseLong((String) propertyValue)); - } else if (boolean.class.isAssignableFrom(returnType)) { - method.invoke(this.connectionFactory, Boolean.parseBoolean((String) propertyValue)); - } else { - method.invoke(this.connectionFactory, propertyValue); + for (Method method : methods) { + Class returnType = method.getParameterTypes()[0]; + if (String.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, propertyValue); + return; + } else if (int.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Integer.parseInt((String) propertyValue)); + return; + } else if (long.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Long.parseLong((String) propertyValue)); + return; + } else if (boolean.class.isAssignableFrom(returnType)) { + method.invoke(this.connectionFactory, Boolean.parseBoolean((String) propertyValue)); + return; + } } + methods[0].invoke(this.connectionFactory, propertyValue); } catch (Exception e) { throw new IllegalStateException("Failed to set property " + propertyName, e); } 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 cd191c32ae07..912f2c0b3280 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 @@ -20,6 +20,9 @@ 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +53,7 @@ static T newDefaultInstance(String className) { * Finds a method by name on the target class. If more then one method * present it will return the first one encountered. * - * @param name method name + * @param name method name * @param targetClass instance of target class * @return instance of {@link Method} */ @@ -68,6 +71,36 @@ public static Method findMethod(String name, Class targetClass) { return null; } + /** + * Finds a method by name on the target class. If more then one method + * present it will return the first one encountered. + * + * @param name method name + * @param targetClass instance of target class + * @return Array of {@link Method} + */ + public static Method[] findMethods(String name, Class targetClass) { + Class searchType = targetClass; + ArrayList fittingMethods = new ArrayList<>(); + while (searchType != null) { + Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods()); + for (Method method : methods) { + if (name.equals(method.getName())) { + fittingMethods.add(method); + } + } + searchType = searchType.getSuperclass(); + } + if (fittingMethods.isEmpty()) { + return null; + } else { + //Sort so that in case there are two methods that accept the parameter type + //as first param use the one which accepts fewer parameters in total + Collections.sort(fittingMethods, Comparator.comparing(Method::getParameterCount)); + return fittingMethods.toArray(new Method[fittingMethods.size()]); + } + } + /** * Adds content of the directory specified with 'path' to the classpath. It * does so by creating a new instance of the {@link URLClassLoader} using From 898d2d317f91334ff4128b6487c9778985040ff4 Mon Sep 17 00:00:00 2001 From: Julian Gimbel Date: Fri, 2 Mar 2018 16:27:56 +0100 Subject: [PATCH 3/3] Array.length can never be less then 0 --- .../org/apache/nifi/jms/cf/JMSConnectionFactoryProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eeb5c9daca9a..5c822be7aa78 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 @@ -257,7 +257,7 @@ private void setConnectionFactoryProperties(ConfigurationContext context) { private void setProperty(String propertyName, Object propertyValue) { String methodName = this.toMethodName(propertyName); Method[] methods = Utils.findMethods(methodName, this.connectionFactory.getClass()); - if (methods != null && methods.length < 0) { + if (methods != null && methods.length > 0) { try { for (Method method : methods) { Class returnType = method.getParameterTypes()[0];