You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by dh...@apache.org on 2014/06/10 21:51:33 UTC
[05/35] git commit: Initial version of Component utilities
Initial version of Component utilities
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/10a0b615
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/10a0b615
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/10a0b615
Branch: refs/heads/master
Commit: 10a0b6156a1a2a7ad8d349d3e561b713b5e51377
Parents: c59a0ff
Author: Dhiraj Bokde <dh...@yahoo.com>
Authored: Fri May 23 13:29:39 2014 -0700
Committer: Dhiraj Bokde <dh...@yahoo.com>
Committed: Tue Jun 10 12:48:29 2014 -0700
----------------------------------------------------------------------
.../camel/util/component/ApiCollection.java | 33 ++
.../apache/camel/util/component/ApiMethod.java | 57 +++
.../camel/util/component/ApiMethodHelper.java | 366 +++++++++++++++++++
.../camel/util/component/ApiMethodImpl.java | 123 +++++++
.../camel/util/component/ApiMethodParser.java | 288 +++++++++++++++
.../component/ApiMethodPropertiesHelper.java | 103 ++++++
.../component/ArgumentSubstitutionParser.java | 143 ++++++++
.../util/component/ApiMethodHelperTest.java | 169 +++++++++
.../ApiMethodPropertiesHelperTest.java | 122 +++++++
.../ArgumentSubstitutionParserTest.java | 52 +++
.../apache/camel/util/component/TestProxy.java | 64 ++++
11 files changed, 1520 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ApiCollection.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiCollection.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiCollection.java
new file mode 100644
index 0000000..585fbb2
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiCollection.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Base class for a collection of ApiMethods. Meant to be extended by Components to create the api name map.
+ */
+public abstract class ApiCollection {
+
+ protected final Map<String, ApiMethodHelper> apis = new HashMap<String, ApiMethodHelper>();
+
+ public final ApiMethodHelper getHelper(String apiName) {
+ return apis.get(apiName);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ApiMethod.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethod.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethod.java
new file mode 100644
index 0000000..7ee7cb9
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethod.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Interface for proxy methods.
+ */
+public interface ApiMethod {
+
+ /**
+ * Returns method name.
+ * @return
+ */
+ String getName();
+
+ /**
+ * Returns method result type.
+ * @return
+ */
+ Class<?> getResultType();
+
+ /**
+ * Returns method argument names.
+ * @return
+ */
+ List<String> getArgNames();
+
+ /**
+ * Return method argument types.
+ * @return
+ */
+ List<Class<?>> getArgTypes();
+
+ /**
+ * Returns {@link Method} in proxy type.
+ * @return
+ */
+ Method getMethod();
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/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
new file mode 100644
index 0000000..9f250f0
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodHelper.java
@@ -0,0 +1,366 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.RuntimeCamelException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class for working with {@link ApiMethod}.
+ */
+public final class ApiMethodHelper<T extends Enum<T> & ApiMethod> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ApiMethodHelper.class);
+
+ // maps method name to ApiMethod
+ private final Map<String, List<T>> METHOD_MAP = new HashMap<String, List<T>>();
+
+ // maps method name to method arguments of the form Class type1, String name1, Class type2, String name2,...
+ private final Map<String, List<Object>> ARGUMENTS_MAP = new HashMap<String, List<Object>>();
+
+ // maps argument name to argument type
+ private final Map<String, Class<?>> VALID_ARGUMENTS = new HashMap<String, Class<?>>();
+
+ // maps aliases to actual method names
+ private final HashMap<String, String> ALIASES = new HashMap<String, String>();
+
+ /**
+ * Create a helper to work with a {@link ApiMethod}, using optional method aliases.
+ * @param apiMethodEnum {@link ApiMethod} enumeration class
+ * @param aliases Aliases mapped to actual method names
+ */
+ public ApiMethodHelper(Class<T> apiMethodEnum, Map<String, String> aliases) {
+
+ // validate ApiMethod Enum
+ if (apiMethodEnum == null) {
+ throw new IllegalArgumentException("ApiMethod enumeration cannot be null");
+ }
+
+ final Map<Pattern, String> aliasPatterns = new HashMap<Pattern, String>();
+ for (Map.Entry<String, String> alias : aliases.entrySet()) {
+ if (alias.getKey() == null || alias.getValue() == null) {
+ throw new IllegalArgumentException("Alias pattern and replacement cannot be null");
+ }
+ aliasPatterns.put(Pattern.compile(alias.getKey()), alias.getValue());
+ }
+
+ LOG.debug("Processing " + apiMethodEnum.getName());
+ final T[] methods = apiMethodEnum.getEnumConstants();
+
+ // load lookup maps
+ for (T method : methods) {
+
+ final String name = method.getName();
+
+ // add method name aliases
+ for (Map.Entry<Pattern, String> aliasEntry : aliasPatterns.entrySet()) {
+ final Matcher matcher = aliasEntry.getKey().matcher(name);
+ if (matcher.find()) {
+ // add method name alias
+ String alias = matcher.replaceAll(aliasEntry.getValue());
+ // convert first character to lowercase
+ assert alias.length() > 1;
+ final char firstChar = alias.charAt(0);
+ if (!Character.isLowerCase(firstChar)) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(Character.toLowerCase(firstChar)).append(alias.substring(1));
+ alias = builder.toString();
+ }
+ ALIASES.put(alias, name);
+ }
+ }
+
+ // map method name to Enum
+ List<T> overloads = METHOD_MAP.get(name);
+ if (overloads == null) {
+ overloads = new ArrayList<T>();
+ METHOD_MAP.put(method.getName(), overloads);
+ }
+ overloads.add(method);
+
+ // add arguments for this method
+ List<Object> arguments = ARGUMENTS_MAP.get(name);
+ if (arguments == null) {
+ arguments = new ArrayList<Object>();
+ ARGUMENTS_MAP.put(name, arguments);
+ }
+
+ // process all arguments for this method
+ final int nArgs = method.getArgNames().size();
+ final String[] argNames = method.getArgNames().toArray(new String[nArgs]);
+ final Class<?>[] argTypes = method.getArgTypes().toArray(new Class[nArgs]);
+ for (int i = 0; i < nArgs; i++) {
+ final String argName = argNames[i];
+ final Class<?> argType = argTypes[i];
+ if (!arguments.contains(argName)) {
+ arguments.add(argType);
+ arguments.add(argName);
+ }
+
+ // also collect argument names for all methods, and detect clashes here
+ final Class<?> previousType = VALID_ARGUMENTS.get(argName);
+ if (previousType != null && previousType != argType) {
+ throw new ExceptionInInitializerError(String.format(
+ "Argument %s has ambiguous types (%s, %s) across methods!",
+ name, previousType, argType));
+ } else if (previousType == null) {
+ VALID_ARGUMENTS.put(argName, argType);
+ }
+ }
+
+ }
+
+ LOG.debug("Found {} unique method names in {} methods", METHOD_MAP.size(), methods.length);
+ }
+
+ /**
+ * 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 full method name 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<T> getCandidateMethods(String name, String... argNames) {
+ List<T> methods = METHOD_MAP.get(name);
+ if (methods == null) {
+ if (ALIASES.containsKey(name)) {
+ methods = METHOD_MAP.get(ALIASES.get(name));
+ }
+ }
+ if (methods == null) {
+ LOG.debug("No matching method for method {}", name);
+ return Collections.emptyList();
+ }
+ int nArgs = argNames != null ? argNames.length : 0;
+ if (nArgs == 0) {
+ LOG.debug("Found {} methods for method {}", methods.size(), name);
+ return Collections.unmodifiableList(methods);
+ } else {
+ final List<T> filteredSet = filterMethods(methods, MatchType.SUBSET, argNames);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found {} filtered methods for {}",
+ filteredSet.size(), name + Arrays.toString(argNames).replace('[', '(').replace(']', ')'));
+ }
+ return filteredSet;
+ }
+ }
+
+ /**
+ * 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<T> filterMethods(List<T> methods, MatchType matchType,
+ String... argNames) {
+ List<String> argsList = Arrays.asList(argNames);
+ // list of methods that have all args in the given names
+ final List<T> result = new ArrayList<T>();
+ final List<T> extraArgs = new ArrayList<T>();
+
+ for (T method : methods) {
+ final List<String> methodArgs = method.getArgNames();
+ switch (matchType) {
+ case EXACT:
+ // method must take all args, and no more
+ if (methodArgs.containsAll(argsList) && argsList.containsAll(methodArgs)) {
+ result.add(method);
+ }
+ break;
+ case SUBSET:
+ // all args are required, method may take more
+ if (methodArgs.containsAll(argsList)) {
+ result.add(method);
+ }
+ break;
+ default:
+ case SUPER_SET:
+ // all method args must be present
+ if (argsList.containsAll(methodArgs)) {
+ if (methodArgs.containsAll(argsList)) {
+ // prefer exact match to avoid unused args
+ result.add(method);
+ } else {
+ // method takes a subset, unused args
+ extraArgs.add(method);
+ }
+ }
+ break;
+ }
+ }
+
+ return Collections.unmodifiableList(result.isEmpty() ? extraArgs : result);
+ }
+
+ /**
+ * Gets argument types and names for all overloaded methods and aliases with the given name.
+ * @param name method name, either an exact name or an alias, exact matches are checked first
+ * @return list of arguments of the form Class type1, String name1, Class type2, String name2,...
+ */
+ public List<Object> getArguments(final String name) throws IllegalArgumentException {
+ List<Object> arguments = ARGUMENTS_MAP.get(name);
+ if (arguments == null) {
+ if (ALIASES.containsKey(name)) {
+ arguments = ARGUMENTS_MAP.get(ALIASES.get(name));
+ }
+ }
+ if (arguments == null) {
+ throw new IllegalArgumentException(name);
+ }
+ return Collections.unmodifiableList(arguments);
+ }
+
+ /**
+ * Get missing properties.
+ * @param methodName method name
+ * @param argNames available arguments
+ * @return Set of missing argument names
+ */
+ public Set<String> getMissingProperties(String methodName, Set<String> argNames) {
+ final List<Object> argsWithTypes = getArguments(methodName);
+ final Set<String> missingArgs = new HashSet<String>();
+
+ for (int i = 1; i < argsWithTypes.size(); i += 2) {
+ final String name = (String) argsWithTypes.get(i);
+ if (!argNames.contains(name)) {
+ missingArgs.add(name);
+ }
+ }
+
+ return missingArgs;
+ }
+
+ /**
+ * Get argument types and names used by all methods.
+ * @return map with argument names as keys, and types as values
+ */
+ public Map<String, Class<?>> allArguments() {
+ return Collections.unmodifiableMap(VALID_ARGUMENTS);
+ }
+
+ /**
+ * Get the type for the given argument name.
+ * @param argName argument name
+ * @return argument type
+ */
+ public Class<?> getType(String argName) throws IllegalArgumentException {
+ final Class<?> type = VALID_ARGUMENTS.get(argName);
+ if (type == null) {
+ throw new IllegalArgumentException(argName);
+ }
+ return type;
+ }
+
+ public T getHighestPriorityMethod(List<T> filteredMethods) {
+ T highest = null;
+ for (T method : filteredMethods) {
+ if (highest == null || method.compareTo(highest) > 0) {
+ highest = method;
+ }
+ }
+ return highest;
+ }
+
+ /**
+ * Invokes given method with argument values from given properties.
+ *
+ * @param proxy Proxy object for invoke
+ * @param method method to invoke
+ * @param properties Map of arguments
+ * @return result of method invocation
+ * @throws org.apache.camel.RuntimeCamelException on errors
+ */
+ public Object invokeMethod(Object proxy, T method, Map<String, Object> properties)
+ throws RuntimeCamelException {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Invoking {} with arguments {}", method.getName(), properties);
+ }
+
+ final List<String> argNames = method.getArgNames();
+ final Object[] values = new Object[argNames.size()];
+ final List<Class<?>> argTypes = method.getArgTypes();
+ final Class<?>[] types = argTypes.toArray(new Class[argTypes.size()]);
+ int index = 0;
+ for (String name : argNames) {
+ Object value = properties.get(name);
+
+ // is the parameter an array type?
+ if (value != null && types[index].isArray()) {
+ Class<?> type = types[index];
+
+ if (value instanceof Collection) {
+ // convert collection to array
+ Collection<?> collection = (Collection<?>) value;
+ Object array = Array.newInstance(type.getComponentType(), collection.size());
+ if (array instanceof Object[]) {
+ collection.toArray((Object[]) array);
+ } else {
+ int i = 0;
+ for (Object el : collection) {
+ Array.set(array, i++, el);
+ }
+ }
+ value = array;
+ } else if (value.getClass().isArray()
+ && type.getComponentType().isAssignableFrom(value.getClass().getComponentType())) {
+ // convert derived array to super array
+ final int size = Array.getLength(value);
+ Object array = Array.newInstance(type.getComponentType(), size);
+ for (int i = 0; i < size; i++) {
+ Array.set(array, i, Array.get(value, i));
+ }
+ value = array;
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Cannot convert %s to %s", value.getClass(), type));
+ }
+ }
+
+ values[index++] = value;
+ }
+
+ try {
+ return method.getMethod().invoke(proxy, values);
+ } catch (Throwable e) {
+ throw new RuntimeCamelException(
+ String.format("Error invoking %s with %s: %s", method.getName(), properties, e.getMessage()), e);
+ }
+ }
+
+ public static enum MatchType {
+ EXACT, SUBSET, SUPER_SET
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java
new file mode 100644
index 0000000..2884164
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodImpl.java
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Delegate class for {@link ApiMethod}.
+ * This class is instantiated by Enumerations for Api Proxy types.
+ * <p>
+ * For example:
+ * </p>
+ * <pre>
+ * {@code
+ * public enum HelloWorldMethod implements ApiMethod {
+ * SAYHI(String.class, "sayHi", "name", String.class);
+ *
+ * private ApiMethodImpl apiMethod;
+ *
+ * private HelloWorldMethods(Class<?> resultType, String name, Object... args) throws IllegalArgumentException {
+ * this.apiMethod = new ApiMethod(HelloWorld.class, resultType, name, args);
+ * }
+ *
+ * // implement ApiMethod interface
+ * String getName() { return apiMethod.getName(); }
+ * Class<?> getResultType() {return apiMethod.getResultType(); }
+ * List<String> getArgNames() { return apiMethod.getArgNames(); }
+ * List<Class<?>> getArgTypes() {return apiMethod.getArgTypes(); }
+ * Method getMethod() { return apiMethod.getMethod(); }
+ * }
+ * }
+ * </pre>
+ */
+public final class ApiMethodImpl implements ApiMethod {
+
+ // name, result class, ordered argument names and classes, and Method to invoke
+ private final String name;
+ private final Class<?> resultType;
+ private final List<String> argNames;
+ private final List<Class<?>> argTypes;
+ private final Method method;
+
+ public ApiMethodImpl(Class<?> proxyType, Class<?> resultType, String name, Object... args) throws IllegalArgumentException {
+ this.name = name;
+ this.resultType = resultType;
+
+ if (args.length % 2 != 0) {
+ throw new IllegalArgumentException("Invalid parameter list, "
+ + "must be of the form 'Class arg1, String arg1Name, Class arg2, String arg2Name...");
+ }
+ int nArgs = args.length / 2;
+ this.argNames = new ArrayList<String>(nArgs);
+ this.argTypes = new ArrayList<Class<?>>(nArgs);
+ for (int i = 0; i < nArgs; i++) {
+ this.argTypes.add((Class<?>) args[i * 2]);
+ this.argNames.add((String) args[i * 2 + 1]);
+ }
+
+ // find method in Proxy type
+ try {
+ this.method = proxyType.getMethod(name, argTypes.toArray(new Class[nArgs]));
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ String.format("Missing method %s %s", name, argTypes.toString().replace('[', '(').replace(']', ')')),
+ e);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Class<?> getResultType() {
+ return resultType;
+ }
+
+ @Override
+ public List<String> getArgNames() {
+ return Collections.unmodifiableList(argNames);
+ }
+
+ @Override
+ public List<Class<?>> getArgTypes() {
+ return Collections.unmodifiableList(argTypes);
+ }
+
+ @Override
+ public Method getMethod() {
+ return method;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{")
+ .append("name=").append(name)
+ .append(", resultType=").append(resultType)
+ .append(", argNames=").append(argNames)
+ .append(", argTypes=").append(argTypes)
+ .append("}");
+ return builder.toString();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java
new file mode 100644
index 0000000..4c6523a
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodParser.java
@@ -0,0 +1,288 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Parser base class for generating ApiMethod enumerations.
+ */
+public abstract class ApiMethodParser<T> {
+
+ private static final Pattern METHOD_PATTERN = Pattern.compile("\\s*(\\S+)\\s+(\\S+)\\s*\\(\\s*([\\S\\s,]*)\\)\\s*;?\\s*");
+ private static final Pattern ARGS_PATTERN = Pattern.compile("\\s*(\\S+)\\s+([^\\s,]+)\\s*,?");
+ private static final String JAVA_LANG = "java.lang.";
+ private static final Map<String, Class> PRIMITIVE_TYPES;
+
+ static {
+ PRIMITIVE_TYPES = new HashMap<String, Class>();
+ PRIMITIVE_TYPES.put("int", Integer.TYPE);
+ PRIMITIVE_TYPES.put("long", Long.TYPE);
+ PRIMITIVE_TYPES.put("double", Double.TYPE);
+ PRIMITIVE_TYPES.put("float", Float.TYPE);
+ PRIMITIVE_TYPES.put("boolean", Boolean.TYPE);
+ PRIMITIVE_TYPES.put("char", Character.TYPE);
+ PRIMITIVE_TYPES.put("byte", Byte.TYPE);
+ PRIMITIVE_TYPES.put("void", Void.TYPE);
+ PRIMITIVE_TYPES.put("short", Short.TYPE);
+ }
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final Class<T> proxyType;
+ private List<String> signatures;
+ private ClassLoader classLoader = ApiMethodParser.class.getClassLoader();
+
+ public ApiMethodParser(Class<T> proxyType) {
+ this.proxyType = proxyType;
+ }
+
+ public Class<T> getProxyType() {
+ return proxyType;
+ }
+
+ public final List<String> getSignatures() {
+ return signatures;
+ }
+
+ public final void setSignatures(List<String> signatures) {
+ this.signatures = new ArrayList<String>();
+ this.signatures.addAll(signatures);
+ }
+
+ public final ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ public final void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Parses the method signatures from {@code getSignatures()}.
+ * @return list of Api methods as {@link ApiMethodModel}
+ */
+ public List<ApiMethodModel> parse() {
+ // parse sorted signatures and generate descriptions
+ List<ApiMethodModel> result = new ArrayList<ApiMethodModel>();
+ for (String signature: signatures) {
+ // remove all type parameters and modifiers
+ signature = signature.replaceAll("<[^>]*>|public|final", "");
+ log.debug("Processing " + signature);
+
+ final Matcher methodMatcher = METHOD_PATTERN.matcher(signature);
+ if (!methodMatcher.matches()) {
+ throw new IllegalArgumentException("Invalid method signature " + signature);
+ }
+
+ final Class<?> resultType = forName(methodMatcher.group(1));
+ final String name = methodMatcher.group(2);
+ final String argSignature = methodMatcher.group(3);
+
+ final List<Argument> arguments = new ArrayList<Argument>();
+
+ List<Class<?>> argTypes = new ArrayList<Class<?>>();
+ final Matcher argsMatcher = ARGS_PATTERN.matcher(argSignature);
+ while (argsMatcher.find()) {
+ final Class<?> type = forName(argsMatcher.group(1));
+ arguments.add(new Argument(argsMatcher.group(2), type));
+ argTypes.add(type);
+ }
+
+ Method method;
+ try {
+ method = proxyType.getMethod(name, argTypes.toArray(new Class<?>[argTypes.size()]));
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Method not found [" + signature + "] in type " + proxyType.getName());
+ }
+ result.add(new ApiMethodModel(name, resultType, arguments, method));
+ }
+
+ Collections.sort(result, new Comparator<ApiMethodModel>() {
+ @Override
+ public int compare(ApiMethodModel model1, ApiMethodModel model2) {
+ final int nameCompare = model1.name.compareTo(model2.name);
+ if (nameCompare != 0) {
+ return nameCompare;
+ } else {
+
+ final int nArgs1 = model1.arguments.size();
+ final int nArgsCompare = nArgs1 - model2.arguments.size();
+ if (nArgsCompare != 0) {
+ return nArgsCompare;
+ } else {
+ // same number of args, compare arg names, kinda arbitrary to use alphabetized order
+ for (int i = 0; i < nArgs1; i++) {
+ final int argCompare = model1.arguments.get(i).name.compareTo(model2.arguments.get(i).name);
+ if (argCompare != 0) {
+ return argCompare;
+ }
+ }
+ // duplicate methods???
+ log.warn("Duplicate methods found [" + model1 + "], [" + model2 + "]");
+ return 0;
+ }
+ }
+ }
+ });
+
+ // assign unique names to every method model
+ final Map<String, Integer> dups = new HashMap<String, Integer>();
+ for (ApiMethodModel model : result) {
+ // TODO watch out, this uses default locale to convert to upper case
+ String uniqueName = model.name.toUpperCase();
+ Integer suffix = dups.get(uniqueName);
+ if (suffix == null) {
+ dups.put(uniqueName, 1);
+ } else {
+ dups.put(uniqueName, suffix + 1);
+ StringBuilder builder = new StringBuilder(uniqueName);
+ builder.append("_").append(suffix);
+ uniqueName = builder.toString();
+ }
+ model.uniqueName = uniqueName;
+ }
+ return result;
+ }
+
+ protected Class<?> forName(String className) {
+ return forName(className, classLoader);
+ }
+
+ public static Class<?> forName(String className, ClassLoader classLoader) {
+ Class<?> result;
+ try {
+ // lookup primitive types first
+ result = PRIMITIVE_TYPES.get(className);
+ if (result == null) {
+ result = Class.forName(className, true, classLoader);
+ }
+ } catch (ClassNotFoundException e) {
+ // check if array type
+ if (className.endsWith("[]")) {
+ final int firstDim = className.indexOf('[');
+ final int nDimensions = (className.length() - firstDim) / 2;
+ return Array.newInstance(forName(className.substring(0, firstDim), classLoader), new int[nDimensions]).getClass();
+ }
+ try {
+ // try loading from default Java package java.lang
+ result = Class.forName(JAVA_LANG + className, true, classLoader);
+ } catch (ClassNotFoundException e1) {
+ throw new IllegalArgumentException("Error loading class " + className);
+ }
+ }
+
+ return result;
+ }
+
+ public static final class ApiMethodModel {
+ private final String name;
+ private final Class<?> resultType;
+ private final List<Argument> arguments;
+ private final Method method;
+
+ private String uniqueName;
+
+ protected ApiMethodModel(String name, Class<?> resultType, List<Argument> arguments, Method method) {
+ this.name = name;
+ this.resultType = resultType;
+ this.arguments = arguments;
+ this.method = method;
+ }
+
+ protected ApiMethodModel(String uniqueName, String name, Class<?> resultType, List<Argument> arguments, Method method) {
+ this.name = name;
+ this.uniqueName = uniqueName;
+ this.resultType = resultType;
+ this.arguments = arguments;
+ this.method = method;
+ }
+
+ public String getUniqueName() {
+ return uniqueName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class<?> getResultType() {
+ return resultType;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ public List<Argument> getArguments() {
+ return arguments;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(resultType.getName()).append(" ");
+ builder.append(name).append("(");
+ for (Argument argument : arguments) {
+ builder.append(argument.getType().getCanonicalName()).append(" ");
+ builder.append(argument.getName()).append(", ");
+ }
+ if (!arguments.isEmpty()) {
+ builder.delete(builder.length() - 2, builder.length());
+ }
+ builder.append(");");
+ return builder.toString();
+ }
+ }
+
+ public static final class Argument {
+ private final String name;
+ private final Class<?> type;
+
+ protected Argument(String name, Class<?> type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class<?> getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(type.getCanonicalName()).append(" ").append(name);
+ return builder.toString();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodPropertiesHelper.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodPropertiesHelper.java b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodPropertiesHelper.java
new file mode 100644
index 0000000..ea0f442
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ApiMethodPropertiesHelper.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.util.IntrospectionSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class to work with ApiMethod arguments.
+ */
+public final class ApiMethodPropertiesHelper<C> {
+
+ private final Logger LOG = LoggerFactory.getLogger(ApiMethodPropertiesHelper.class);
+
+ // set of field names which are specific to the api, to be excluded from method argument considerations
+ private final Set<String> COMPONENT_CONFIG_FIELDS = new HashSet<String>();
+
+ private final Class<?> componentConfigClass;
+ private final String propertyPrefix;
+
+ public ApiMethodPropertiesHelper(Class<C> componentConfiguration, String propertyPrefix) {
+
+ this.componentConfigClass = componentConfiguration;
+ this.propertyPrefix = propertyPrefix;
+
+ for (Field field : componentConfiguration.getDeclaredFields()) {
+ COMPONENT_CONFIG_FIELDS.add(field.getName());
+ }
+ }
+
+ /**
+ * Gets exchange header properties that start with propertyPrefix.
+ *
+ * @param exchange Camel exchange
+ * @param properties map to collect properties with required prefix
+ */
+ public Map<String, Object> getExchangeProperties(Exchange exchange, Map<String, Object> properties) {
+ final int prefixLength = propertyPrefix.length();
+ int nProperties = 0;
+ for (Map.Entry<String, Object> entry : exchange.getIn().getHeaders().entrySet()) {
+ if (entry.getKey().startsWith(propertyPrefix)) {
+ properties.put(entry.getKey().substring(prefixLength),
+ entry.getValue());
+ nProperties++;
+ }
+ }
+ LOG.debug("Found {} properties in exchange", nProperties);
+ return properties;
+ }
+
+ public void getEndpointProperties(Object endpointConfiguration,
+ Map<String, Object> properties) {
+
+ if (IntrospectionSupport.getProperties(endpointConfiguration, properties, null, false)) {
+ final Set<String> names = properties.keySet();
+ // remove component config properties so we only have endpoint properties
+ names.removeAll(COMPONENT_CONFIG_FIELDS);
+ }
+ if (LOG.isDebugEnabled()) {
+ final Set<String> names = properties.keySet();
+ LOG.debug("Found endpoint properties {}",
+ names.retainAll(getValidEndpointProperties(endpointConfiguration)));
+ }
+ }
+
+ public Set<String> getEndpointPropertyNames(Object endpointConfiguration) {
+ Map<String, Object> properties = new HashMap<String, Object>();
+ getEndpointProperties(endpointConfiguration, properties);
+ return Collections.unmodifiableSet(properties.keySet());
+ }
+
+ public Set<String> getValidEndpointProperties(Object endpointConfiguration) {
+ Set<String> fields = new HashSet<String>();
+ for (Field field : endpointConfiguration.getClass().getDeclaredFields()) {
+ fields.add(field.getName());
+ }
+ return Collections.unmodifiableSet(fields);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java b/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java
new file mode 100644
index 0000000..06ab253
--- /dev/null
+++ b/camel-core/src/main/java/org/apache/camel/util/component/ArgumentSubstitutionParser.java
@@ -0,0 +1,143 @@
+package org.apache.camel.util.component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Adds support for parameter name substitutions.
+ */
+public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> {
+
+ private final HashMap<Pattern, Map<Pattern, List<NameReplacement>>> methodMap;
+
+ /**
+ * Create a parser using regular expressions to adapt parameter names.
+ * @param proxyType Proxy class.
+ * @param substitutions an array of <b>ordered</b> Argument adapters.
+ */
+ public ArgumentSubstitutionParser(Class<T> proxyType, Substitution[] substitutions) {
+ super(proxyType);
+ Map<String, Map<String, List<NameReplacement>>> regexMap = new HashMap<String, Map<String, List<NameReplacement>>>();
+
+ for (Substitution tuple : substitutions) {
+ tuple.validate();
+
+ final NameReplacement nameReplacement = new NameReplacement();
+ nameReplacement.replacement = tuple.replacement;
+ if (tuple.argType != null) {
+ nameReplacement.type = forName(tuple.argType);
+ }
+
+ Map<String, List<NameReplacement>> replacementMap = regexMap.get(tuple.method);
+ if (replacementMap == null) {
+ replacementMap = new HashMap<String, List<NameReplacement>>();
+ regexMap.put(tuple.method, replacementMap);
+ }
+ List<NameReplacement> replacements = replacementMap.get(tuple.argName);
+ if (replacements == null) {
+ replacements = new ArrayList<NameReplacement>();
+ replacementMap.put(tuple.argName, replacements);
+ }
+ replacements.add(nameReplacement);
+ }
+
+ // now compile the patterns, all this because Pattern doesn't override equals()!!!
+ this.methodMap = new LinkedHashMap<Pattern, Map<Pattern, List<NameReplacement>>>();
+ for (Map.Entry<String, Map<String, List<NameReplacement>>> method : regexMap.entrySet()) {
+ Map<Pattern, List<NameReplacement>> argMap = new LinkedHashMap<Pattern, List<NameReplacement>>();
+ for (Map.Entry<String, List<NameReplacement>> arg : method.getValue().entrySet()) {
+ argMap.put(Pattern.compile(arg.getKey()), arg.getValue());
+ }
+ methodMap.put(Pattern.compile(method.getKey()), argMap);
+ }
+ }
+
+ @Override
+ public List<ApiMethodModel> parse() {
+ final List<ApiMethodModel> result = new ArrayList<ApiMethodModel>();
+
+ for (ApiMethodModel model : super.parse()) {
+ // look for method name matches
+ for (Map.Entry<Pattern, Map<Pattern, List<NameReplacement>>> methodEntry : methodMap.entrySet()) {
+ if (methodEntry.getKey().matcher(model.getName()).matches()) {
+
+ // look for arg name matches
+ final List<Argument> updatedArguments = new ArrayList<Argument>();
+ final Map<Pattern, List<NameReplacement>> argMap = methodEntry.getValue();
+ for (Argument argument : model.getArguments()) {
+ for (Map.Entry<Pattern, List<NameReplacement>> argEntry : argMap.entrySet()) {
+ final Matcher matcher = argEntry.getKey().matcher(argument.getName());
+ if (matcher.find()) {
+ final List<NameReplacement> adapters = argEntry.getValue();
+ for (NameReplacement adapter : adapters) {
+ if (adapter.type == null || adapter.type.isAssignableFrom(argument.getType())) {
+ argument = new Argument(matcher.replaceAll(adapter.replacement), argument.getType());
+ }
+ }
+ }
+ }
+
+ updatedArguments.add(argument);
+ }
+
+ model = new ApiMethodModel(model.getName(), model.getUniqueName(), model.getResultType(),
+ updatedArguments, model.getMethod());
+ }
+ }
+
+ result.add(model);
+ }
+
+ return result;
+ }
+
+ public static class Substitution {
+
+ private String method;
+ private String argName;
+ private String argType;
+ private String replacement;
+
+ /**
+ * Creates a substitution for all argument types.
+ * @param method regex to match method name
+ * @param argName regex to match argument name
+ * @param replacement replacement text for argument name
+ */
+ public Substitution(String method, String argName, String replacement) {
+ this.method = method;
+ this.argName = argName;
+ this.replacement = replacement;
+ }
+
+ /**
+ * Creates a substitution for a specific argument type.
+ * @param method regex to match method name
+ * @param argName regex to match argument name
+ * @param argType argument type as String
+ * @param replacement replacement text for argument name
+ */
+ public Substitution(String method, String argName, String argType, String replacement) {
+ this.method = method;
+ this.argName = argName;
+ this.argType = argType;
+ this.replacement = replacement;
+ }
+
+ public void validate() {
+ if (method == null || argName == null || replacement == null) {
+ throw new IllegalArgumentException("Properties method, argName and replacement MUST be provided");
+ }
+ }
+ }
+
+ private static class NameReplacement {
+ private String replacement;
+ private Class<?> type;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/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
new file mode 100644
index 0000000..f41bc76
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodHelperTest.java
@@ -0,0 +1,169 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class ApiMethodHelperTest {
+
+ private static TestMethod[] sayHis = new TestMethod[] { TestMethod.SAYHI, TestMethod.SAYHI_1};
+ private static ApiMethodHelper<TestMethod> apiMethodHelper;
+
+ static {
+ final HashMap<String, String> aliases = new HashMap<String, String>();
+ aliases.put("say(.*)", "$1");
+ apiMethodHelper = new ApiMethodHelper<TestMethod>(TestMethod.class, aliases);
+ }
+
+ @Test
+ public void testGetCandidateMethods() {
+ List<TestMethod> methods = apiMethodHelper.getCandidateMethods("sayHi");
+ assertEquals("Can't find sayHi(*)", 2, methods.size());
+
+ methods = apiMethodHelper.getCandidateMethods("hi");
+ assertEquals("Can't find sayHi(name)", 2, methods.size());
+
+ methods = apiMethodHelper.getCandidateMethods("hi", "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");
+ assertEquals("Can't find greetUs(name1, name2)", 1, methods.size());
+ }
+
+ @Test
+ public void testFilterMethods() {
+ List<TestMethod> methods = apiMethodHelper.filterMethods(Arrays.asList(sayHis), ApiMethodHelper.MatchType.EXACT);
+ assertEquals("Exact match failed for sayHi()", 1, methods.size());
+ assertEquals("Exact match failed for sayHi()", TestMethod.SAYHI, methods.get(0));
+
+ 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");
+ 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");
+ 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");
+ assertEquals("Super set match failed for sayHi(name)", 2, methods.size());
+ }
+
+ @Test
+ public void testGetArguments() {
+ assertEquals("GetArguments failed for hi", 2, apiMethodHelper.getArguments("hi").size());
+ assertEquals("GetArguments failed for greetMe", 2, apiMethodHelper.getArguments("greetMe").size());
+ assertEquals("GetArguments failed for greetUs", 4, apiMethodHelper.getArguments("greetUs").size());
+ }
+
+ @Test
+ public void testGetMissingProperties() throws Exception {
+ assertEquals("Missing properties for hi", 1,
+ apiMethodHelper.getMissingProperties("hi", new HashSet<String>()).size());
+
+ final HashSet<String> argNames = new HashSet<String>();
+ argNames.add("name");
+ assertEquals("Missing properties for greetMe", 0,
+ apiMethodHelper.getMissingProperties("greetMe", argNames).size());
+
+ argNames.clear();
+ argNames.add("name1");
+ assertEquals("Missing properties for greetMe", 1,
+ apiMethodHelper.getMissingProperties("greetUs", argNames).size());
+ }
+
+ @Test
+ public void testAllArguments() throws Exception {
+ assertEquals("Get all arguments", 6, apiMethodHelper.allArguments().size());
+ }
+
+ @Test
+ public void testGetType() throws Exception {
+ assertEquals("Get type name", String.class, apiMethodHelper.getType("name"));
+ assertEquals("Get type name1", String.class, apiMethodHelper.getType("name1"));
+ assertEquals("Get type name2", String.class, apiMethodHelper.getType("name2"));
+ }
+
+ @Test
+ public void testGetHighestPriorityMethod() throws Exception {
+ assertEquals("Get highest priority method",
+ TestMethod.SAYHI_1, apiMethodHelper.getHighestPriorityMethod(Arrays.asList(sayHis)));
+ }
+
+ @Test
+ public void testInvokeMethod() throws Exception {
+ TestProxy proxy = new TestProxy();
+ assertEquals("sayHi()", "Hello!", apiMethodHelper.invokeMethod(proxy, TestMethod.SAYHI, Collections.EMPTY_MAP));
+
+ final HashMap<String, Object> properties = new HashMap<String, Object>();
+ properties.put("name", "Dave");
+
+ assertEquals("sayHi(name)", "Hello Dave", apiMethodHelper.invokeMethod(proxy, TestMethod.SAYHI_1, properties));
+ assertEquals("greetMe(name)", "Greetings Dave", apiMethodHelper.invokeMethod(proxy, TestMethod.GREETME, properties));
+
+ properties.clear();
+ properties.put("name1", "Dave");
+ properties.put("name2", "Frank");
+ assertEquals("greetUs(name1, name2)", "Greetings Dave, Frank", apiMethodHelper.invokeMethod(proxy, TestMethod.GREETUS, properties));
+ }
+
+ static enum TestMethod implements ApiMethod {
+
+ SAYHI(String.class, "sayHi"),
+ SAYHI_1(String.class, "sayHi", String.class, "name"),
+ GREETME(String.class, "greetMe", String.class, "name"),
+ GREETUS(String.class, "greetUs", String.class, "name1", String.class, "name2"),
+ GREETALL(String.class, "greetAll", new String[0].getClass(), "names"),
+ GREETALL_1(String.class, "greetAll", List.class, "nameList"),
+ GREETTIMES(new String[0].getClass(), "greetTimes", String.class, "name", int.class, "times");
+
+ private final ApiMethod apiMethod;
+
+ private TestMethod(Class<?> resultType, String name, Object... args) {
+ this.apiMethod = new ApiMethodImpl(TestProxy.class, resultType, name, args);
+ }
+
+ @Override
+ public String getName() { return apiMethod.getName(); }
+
+ @Override
+ public Class<?> getResultType() { return apiMethod.getResultType(); }
+
+ @Override
+ public List<String> getArgNames() { return apiMethod.getArgNames(); }
+
+ @Override
+ public List<Class<?>> getArgTypes() { return apiMethod.getArgTypes(); }
+
+ @Override
+ public Method getMethod() { return apiMethod.getMethod(); }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodPropertiesHelperTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodPropertiesHelperTest.java b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodPropertiesHelperTest.java
new file mode 100644
index 0000000..c711c5f
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/component/ApiMethodPropertiesHelperTest.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.util.HashMap;
+
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.impl.DefaultExchange;
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+public class ApiMethodPropertiesHelperTest {
+
+ private static final String TEST_PREFIX = "TestComponent.";
+
+ private static final String PROPERTY_1 = TEST_PREFIX + "property1";
+ private static final String PROPERTY_2 = TEST_PREFIX + "property2";
+ private static final String PROPERTY_3 = TEST_PREFIX + "property3";
+ private static final String PROPERTY_4 = TEST_PREFIX + "property4";
+
+ private static final String VALUE_1 = "value1";
+ private static final long VALUE_2 = 2;
+ private static final String VALUE_3 = "value3";
+ private static final String VALUE_4 = "true";
+
+ private static ApiMethodPropertiesHelper propertiesHelper =
+ new ApiMethodPropertiesHelper(TestComponentConfiguration.class, TEST_PREFIX);
+
+ @Test
+ public void testGetExchangeProperties() throws Exception {
+ final HashMap<String, Object> properties = new HashMap<String, Object>();
+ final DefaultExchange exchange = new DefaultExchange(new MockEndpoint());
+ exchange.getIn().setHeader(PROPERTY_1, VALUE_1);
+ exchange.getIn().setHeader(PROPERTY_2, VALUE_2);
+ exchange.getIn().setHeader(PROPERTY_3, VALUE_3);
+ exchange.getIn().setHeader(PROPERTY_4, VALUE_4);
+ propertiesHelper.getExchangeProperties(exchange, properties);
+ assertEquals(4, properties.size());
+ }
+
+ @Test
+ public void testGetEndpointProperties() throws Exception {
+ final HashMap<String, Object> properties = new HashMap<String, Object>();
+ final TestEndpointConfiguration endpointConfiguration = new TestEndpointConfiguration();
+ endpointConfiguration.setProperty1(VALUE_1);
+ endpointConfiguration.setProperty2(VALUE_2);
+ endpointConfiguration.setProperty3(VALUE_3);
+ endpointConfiguration.setProperty4(Boolean.valueOf(VALUE_4));
+ propertiesHelper.getEndpointProperties(endpointConfiguration, properties);
+ assertEquals(2, properties.size());
+ }
+
+ @Test
+ public void testGetEndpointPropertyNames() throws Exception {
+ final TestEndpointConfiguration endpointConfiguration = new TestEndpointConfiguration();
+ endpointConfiguration.setProperty1(VALUE_1);
+ endpointConfiguration.setProperty4(Boolean.valueOf(VALUE_4));
+ assertEquals(1, propertiesHelper.getEndpointPropertyNames(endpointConfiguration).size());
+ }
+
+ @Test
+ public void testGetValidEndpointProperties() throws Exception {
+ assertEquals(2, propertiesHelper.getValidEndpointProperties(new TestEndpointConfiguration()).size());
+ }
+
+ private static class TestComponentConfiguration {
+ private String property1;
+ private Long property2;
+
+ public String getProperty1() {
+ return property1;
+ }
+
+ public void setProperty1(String property1) {
+ this.property1 = property1;
+ }
+
+ public long getProperty2() {
+ return property2;
+ }
+
+ public void setProperty2(Long property2) {
+ this.property2 = property2;
+ }
+ }
+
+ private static class TestEndpointConfiguration extends TestComponentConfiguration {
+ private String property3;
+ private Boolean property4;
+
+ public String getProperty3() {
+ return property3;
+ }
+
+ public void setProperty3(String property3) {
+ this.property3 = property3;
+ }
+
+ public Boolean getProperty4() {
+ return property4;
+ }
+
+ public void setProperty4(Boolean property4) {
+ this.property4 = property4;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/test/java/org/apache/camel/util/component/ArgumentSubstitutionParserTest.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/component/ArgumentSubstitutionParserTest.java b/camel-core/src/test/java/org/apache/camel/util/component/ArgumentSubstitutionParserTest.java
new file mode 100644
index 0000000..ce0153d
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/component/ArgumentSubstitutionParserTest.java
@@ -0,0 +1,52 @@
+package org.apache.camel.util.component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import static org.apache.camel.util.component.ArgumentSubstitutionParser.*;
+import static org.junit.Assert.assertEquals;
+
+public class ArgumentSubstitutionParserTest {
+
+ private static final String PERSON = "person";
+
+ @Test
+ public void testParse() throws Exception {
+
+ final Substitution[] adapters = new Substitution[3];
+ adapters[0] = new Substitution(".+", "name", PERSON);
+ adapters[1] = new Substitution("greet.+", "person([0-9]+)", "astronaut$1");
+ adapters[2] = new Substitution(".+", "(.+)", "java.util.List", "$1List");
+
+ final ApiMethodParser<TestProxy> parser = new ArgumentSubstitutionParser<TestProxy>(TestProxy.class, adapters);
+
+ final ArrayList<String> signatures = new ArrayList<String>();
+ signatures.add("public String sayHi();");
+ signatures.add("public String sayHi(final String name);");
+ signatures.add("public final String greetMe(final String name);");
+ signatures.add("public final String greetUs(final String name1, String name2);");
+ signatures.add("public final String greetAll(String[] names);");
+ signatures.add("public final String greetAll(java.util.List<String> names);");
+ signatures.add("public final String[] greetTimes(String name, int times);");
+ parser.setSignatures(signatures);
+
+ final List<ApiMethodParser.ApiMethodModel> methodModels = parser.parse();
+ assertEquals(7, methodModels.size());
+
+ final ApiMethodParser.ApiMethodModel sayHi1 = methodModels.get(6);
+ assertEquals(PERSON, sayHi1.getArguments().get(0).getName());
+ assertEquals("SAYHI_1", sayHi1.getUniqueName());
+
+ final ApiMethodParser.ApiMethodModel greetMe = methodModels.get(2);
+ assertEquals(PERSON, greetMe.getArguments().get(0).getName());
+
+ final ApiMethodParser.ApiMethodModel greetUs = methodModels.get(4);
+ assertEquals("astronaut1", greetUs.getArguments().get(0).getName());
+ assertEquals("astronaut2", greetUs.getArguments().get(1).getName());
+
+ final ApiMethodParser.ApiMethodModel greetAll = methodModels.get(1);
+ assertEquals("personsList", greetAll.getArguments().get(0).getName());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/10a0b615/camel-core/src/test/java/org/apache/camel/util/component/TestProxy.java
----------------------------------------------------------------------
diff --git a/camel-core/src/test/java/org/apache/camel/util/component/TestProxy.java b/camel-core/src/test/java/org/apache/camel/util/component/TestProxy.java
new file mode 100644
index 0000000..d707eb8
--- /dev/null
+++ b/camel-core/src/test/java/org/apache/camel/util/component/TestProxy.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.util.component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class TestProxy {
+ public String sayHi() {
+ return "Hello!";
+ }
+
+ public String sayHi(final String name) {
+ return "Hello " + name;
+ }
+
+ public final String greetMe(final String name) {
+ return "Greetings " + name;
+ }
+
+ public final String greetUs(final String name1, String name2) {
+ return "Greetings " + name1 + ", " + name2;
+ }
+
+ public final String greetAll(final String[] names) {
+ StringBuilder builder = new StringBuilder("Greetings ");
+ for (String name : names) {
+ builder.append(name).append(", ");
+ }
+ builder.delete(builder.length() - 2, builder.length());
+ return builder.toString();
+ }
+
+ public final String greetAll(List<String> names) {
+ StringBuilder builder = new StringBuilder("Greetings ");
+ for (String name : names) {
+ builder.append(name).append(", ");
+ }
+ builder.delete(builder.length() - 2, builder.length());
+ return builder.toString();
+ }
+
+ public final String[] greetTimes(String name, int times) {
+ final List<String> result = new ArrayList<String>();
+ for (int i = 0; i < times; i++) {
+ result.add("Greetings " + name);
+ }
+ return result.toArray(new String[result.size()]);
+ }
+}