CAMEL-10102: [api-component-framework] api method resolution should not allocate temporary arrays
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/773e9ee7 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/773e9ee7 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/773e9ee7 Branch: refs/heads/master Commit: 773e9ee7daef2eef51924bb2cf54734d2a33fc60 Parents: 6a7338b Author: lburgazzoli <lburgazz...@gmail.com> Authored: Wed Jun 29 17:35:19 2016 +0200 Committer: lburgazzoli <lburgazz...@gmail.com> Committed: Thu Jul 21 11:47:40 2016 +0200 ---------------------------------------------------------------------- .../util/component/AbstractApiConsumer.java | 21 +------- .../util/component/AbstractApiEndpoint.java | 10 ++-- .../util/component/AbstractApiProducer.java | 12 +++-- .../camel/util/component/ApiConsumerHelper.java | 7 +-- .../camel/util/component/ApiMethodHelper.java | 55 ++++++++++++++------ .../util/component/ApiMethodHelperTest.java | 14 ++--- .../apache/camel/component/box/BoxConsumer.java | 11 ++-- 7 files changed, 62 insertions(+), 68 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiConsumer.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiConsumer.java b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiConsumer.java index dc14855..900fb82 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiConsumer.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiConsumer.java @@ -16,7 +16,6 @@ */ package org.apache.camel.util.component; -import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -62,7 +61,7 @@ public abstract class AbstractApiConsumer<E extends Enum<E> & ApiName, T> @Override protected int poll() throws Exception { // invoke the consumer method - final Map<String, Object> args = new HashMap<String, Object>(); + final Map<String, Object> args = new HashMap<>(); args.putAll(endpoint.getEndpointProperties()); // let the endpoint and the Consumer intercept properties @@ -102,13 +101,7 @@ public abstract class AbstractApiConsumer<E extends Enum<E> & ApiName, T> @Override public Object splitResult(Object result) { - // process result according to type - if (splitResult && result != null && (result instanceof Collection || result.getClass().isArray())) { - // create an exchange for every element - return getResultAsArray(result); - } else { - return result; - } + return result; } @Override @@ -116,16 +109,6 @@ public abstract class AbstractApiConsumer<E extends Enum<E> & ApiName, T> // do nothing by default } - private static Object getResultAsArray(Object result) { - if (result.getClass().isArray()) { - // no conversion needed - return result; - } - // must be a Collection - Collection<?> collection = (Collection<?>) result; - return collection.toArray(new Object[collection.size()]); - } - public final boolean isSplitResult() { return splitResult; } http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiEndpoint.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiEndpoint.java b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiEndpoint.java index 89e8e41..ab50d12 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiEndpoint.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiEndpoint.java @@ -132,9 +132,7 @@ public abstract class AbstractApiEndpoint<E extends ApiName, T> this.endpointProperties = Collections.unmodifiableMap(properties); // get endpoint property names - final Set<String> arguments = new HashSet<String>(); - arguments.addAll(endpointPropertyNames); - + final Set<String> arguments = new HashSet<>(endpointPropertyNames); // add inBody argument for producers if (inBody != null) { arguments.add(inBody); @@ -142,11 +140,9 @@ public abstract class AbstractApiEndpoint<E extends ApiName, T> interceptPropertyNames(arguments); - final String[] argNames = arguments.toArray(new String[arguments.size()]); - // create a list of candidate methods - candidates = new ArrayList<ApiMethod>(); - candidates.addAll(methodHelper.getCandidateMethods(methodName, argNames)); + candidates = new ArrayList<>(); + candidates.addAll(methodHelper.getCandidateMethods(methodName, arguments)); candidates = Collections.unmodifiableList(candidates); // error if there are no candidates http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiProducer.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiProducer.java b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiProducer.java index 0a6130f..506b5d0 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiProducer.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/AbstractApiProducer.java @@ -139,15 +139,17 @@ public abstract class AbstractApiProducer<E extends Enum<E> & ApiName, T> // filter candidates based on endpoint and exchange properties final Set<String> argNames = properties.keySet(); - final List<ApiMethod> filteredMethods = methodHelper.filterMethods(candidates, - ApiMethodHelper.MatchType.SUPER_SET, - argNames.toArray(new String[argNames.size()])); + final List<ApiMethod> filteredMethods = methodHelper.filterMethods( + candidates, + ApiMethodHelper.MatchType.SUPER_SET, + argNames); // get the method to call if (filteredMethods.isEmpty()) { - final Set<String> missing = methodHelper.getMissingProperties(endpoint.getMethodName(), argNames); throw new RuntimeCamelException(String.format("Missing properties for %s, need one or more from %s", - endpoint.getMethodName(), missing)); + endpoint.getMethodName(), + methodHelper.getMissingProperties(endpoint.getMethodName(), argNames)) + ); } else if (filteredMethods.size() == 1) { // found an exact match method = filteredMethods.get(0); http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/main/java/org/apache/camel/util/component/ApiConsumerHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiConsumerHelper.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiConsumerHelper.java index 658ce0c..e298690 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ApiConsumerHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiConsumerHelper.java @@ -49,14 +49,11 @@ public final class ApiConsumerHelper { ApiMethod result; // find one that takes the largest subset of endpoint parameters - final Set<String> argNames = new HashSet<String>(); - argNames.addAll(endpoint.getEndpointPropertyNames()); - + final Set<String> argNames = new HashSet<>(endpoint.getEndpointPropertyNames()); propertyNamesInterceptor.interceptPropertyNames(argNames); - final String[] argNamesArray = argNames.toArray(new String[argNames.size()]); List<ApiMethod> filteredMethods = endpoint.methodHelper.filterMethods( - endpoint.getCandidates(), ApiMethodHelper.MatchType.SUPER_SET, argNamesArray); + endpoint.getCandidates(), ApiMethodHelper.MatchType.SUPER_SET, argNames); if (filteredMethods.isEmpty()) { ApiMethodHelper<? extends ApiMethod> methodHelper = endpoint.getMethodHelper(); http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java index d938f30..3d06e3f 100644 --- a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java +++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java @@ -19,7 +19,6 @@ package org.apache.camel.util.component; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -29,6 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.camel.RuntimeCamelException; import org.slf4j.Logger; @@ -207,32 +207,43 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { * Note that the args list is a required subset of arguments for returned methods. * * @param name case sensitive method name or alias to lookup + * @return non-null unmodifiable list of methods that take all of the given arguments, empty if there is no match + */ + public List<ApiMethod> getCandidateMethods(String name) { + return getCandidateMethods(name, Collections.emptyList()); + } + + /** + * Gets methods that match the given name and arguments.<p/> + * Note that the args list is a required subset of arguments for returned methods. + * + * @param name case sensitive method name or alias to lookup * @param argNames unordered required argument names * @return non-null unmodifiable list of methods that take all of the given arguments, empty if there is no match */ - public List<ApiMethod> getCandidateMethods(String name, String... argNames) { + public List<ApiMethod> getCandidateMethods(String name, Collection<String> argNames) { List<T> methods = methodMap.get(name); if (methods == null) { if (aliasesMap.containsKey(name)) { - methods = new ArrayList<T>(); - for (String method : aliasesMap.get(name)) { - methods.addAll(methodMap.get(method)); - } + methods = aliasesMap.get(name) + .stream() + .map(method -> (T)methodMap.get(method)) + .collect(Collectors.toList()); } } if (methods == null) { LOG.debug("No matching method for method {}", name); return Collections.emptyList(); } - int nArgs = argNames != null ? argNames.length : 0; + int nArgs = argNames != null ? argNames.size() : 0; if (nArgs == 0) { LOG.debug("Found {} methods for method {}", methods.size(), name); - return Collections.<ApiMethod>unmodifiableList(methods); + return Collections.unmodifiableList(methods); } else { final List<ApiMethod> filteredSet = filterMethods(methods, MatchType.SUBSET, argNames); if (LOG.isDebugEnabled()) { LOG.debug("Found {} filtered methods for {}", - filteredSet.size(), name + Arrays.toString(argNames).replace('[', '(').replace(']', ')')); + filteredSet.size(), name + argNames.toString().replace('[', '(').replace(']', ')')); } return filteredSet; } @@ -243,18 +254,28 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { * * @param methods list of methods to filter * @param matchType whether the arguments are an exact match, a subset or a super set of method args + * @return methods with arguments that satisfy the match type.<p/> + * For SUPER_SET match, if methods with exact match are found, methods that take a subset are ignored + */ + public List<ApiMethod> filterMethods(List<? extends ApiMethod> methods, MatchType matchType) { + return filterMethods(methods, matchType, Collections.emptyList()); + } + + /** + * Filters a list of methods to those that take the given set of arguments. + * + * @param methods list of methods to filter + * @param matchType whether the arguments are an exact match, a subset or a super set of method args * @param argNames argument names to filter the list * @return methods with arguments that satisfy the match type.<p/> * For SUPER_SET match, if methods with exact match are found, methods that take a subset are ignored */ - public List<ApiMethod> filterMethods(List<? extends ApiMethod> methods, MatchType matchType, - String... argNames) { + public List<ApiMethod> filterMethods(List<? extends ApiMethod> methods, MatchType matchType, Collection<String> argNames) { // original arguments - final List<String> argsList = Arrays.asList(argNames); // supplied arguments with missing nullable arguments final List<String> withNullableArgsList; if (!nullableArguments.isEmpty()) { - withNullableArgsList = new ArrayList<String>(argsList); + withNullableArgsList = new ArrayList<>(argNames); withNullableArgsList.addAll(nullableArguments); } else { withNullableArgsList = null; @@ -270,21 +291,21 @@ public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> { switch (matchType) { case EXACT: // method must take all args, and no more - if (methodArgs.containsAll(argsList) && argsList.containsAll(methodArgs)) { + if (methodArgs.containsAll(argNames) && argNames.containsAll(methodArgs)) { result.add(method); } break; case SUBSET: // all args are required, method may take more - if (methodArgs.containsAll(argsList)) { + if (methodArgs.containsAll(argNames)) { result.add(method); } break; default: case SUPER_SET: // all method args must be present - if (argsList.containsAll(methodArgs)) { - if (methodArgs.containsAll(argsList)) { + if (argNames.containsAll(methodArgs)) { + if (methodArgs.containsAll(argNames)) { // prefer exact match to avoid unused args result.add(method); } else { http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java index 574c9d4..8913e33 100644 --- a/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java +++ b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java @@ -49,19 +49,19 @@ public class ApiMethodHelperTest { methods = apiMethodHelper.getCandidateMethods("hi"); assertEquals("Can't find sayHi(name)", 2, methods.size()); - methods = apiMethodHelper.getCandidateMethods("hi", "name"); + methods = apiMethodHelper.getCandidateMethods("hi", Arrays.asList("name")); assertEquals("Can't find sayHi(name)", 1, methods.size()); methods = apiMethodHelper.getCandidateMethods("greetMe"); assertEquals("Can't find greetMe(name)", 1, methods.size()); - methods = apiMethodHelper.getCandidateMethods("greetUs", "name1"); + methods = apiMethodHelper.getCandidateMethods("greetUs", Arrays.asList("name1")); assertEquals("Can't find greetUs(name1, name2)", 1, methods.size()); - methods = apiMethodHelper.getCandidateMethods("greetAll", "nameMap"); + methods = apiMethodHelper.getCandidateMethods("greetAll", Arrays.asList("nameMap")); assertEquals("Can't find greetAll(nameMap)", 1, methods.size()); - methods = apiMethodHelper.getCandidateMethods("greetInnerChild", "child"); + methods = apiMethodHelper.getCandidateMethods("greetInnerChild", Arrays.asList("child")); assertEquals("Can't find greetInnerChild(child)", 1, methods.size()); } @@ -74,15 +74,15 @@ public class ApiMethodHelperTest { methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.SUBSET); assertEquals("Subset match failed for sayHi(*)", 2, methods.size()); - methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.SUBSET, "name"); + methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.SUBSET, Arrays.asList("name")); assertEquals("Subset match failed for sayHi(name)", 1, methods.size()); assertEquals("Exact match failed for sayHi()", TestMethod.SAYHI_1, methods.get(0)); - methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.SUPER_SET, "name"); + methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.SUPER_SET, Arrays.asList("name")); assertEquals("Super set match failed for sayHi(name)", 1, methods.size()); assertEquals("Exact match failed for sayHi()", TestMethod.SAYHI_1, methods.get(0)); - methods = apiMethodHelper.filterMethods(Arrays.asList(TestMethod.values()), ApiMethodHelper.MatchType.SUPER_SET, "name"); + methods = apiMethodHelper.filterMethods(Arrays.asList(TestMethod.values()), ApiMethodHelper.MatchType.SUPER_SET, Arrays.asList("name")); assertEquals("Super set match failed for sayHi(name)", 2, methods.size()); // test nullable names http://git-wip-us.apache.org/repos/asf/camel/blob/773e9ee7/components/camel-box/src/main/java/org/apache/camel/component/box/BoxConsumer.java ---------------------------------------------------------------------- diff --git a/components/camel-box/src/main/java/org/apache/camel/component/box/BoxConsumer.java b/components/camel-box/src/main/java/org/apache/camel/component/box/BoxConsumer.java index 6b6fabf..7868d70 100644 --- a/components/camel-box/src/main/java/org/apache/camel/component/box/BoxConsumer.java +++ b/components/camel-box/src/main/java/org/apache/camel/component/box/BoxConsumer.java @@ -16,13 +16,11 @@ */ package org.apache.camel.component.box; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; import com.box.boxjavalibv2.dao.BoxEventCollection; -import com.box.boxjavalibv2.dao.BoxTypedObject; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.component.box.internal.BoxConstants; @@ -109,12 +107,9 @@ public class BoxConsumer extends DefaultConsumer @Override public Object splitResult(Object result) { - if (result instanceof BoxEventCollection && splitResult) { - BoxEventCollection eventCollection = (BoxEventCollection) result; - final ArrayList<BoxTypedObject> entries = eventCollection.getEntries(); - return entries.toArray(new BoxTypedObject[entries.size()]); - } - return result; + return (result instanceof BoxEventCollection && splitResult) + ? ((BoxEventCollection) result).getEntries() + : result; } @Override