001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.spring.util; 018 019 import org.aopalliance.intercept.MethodInvocation; 020 import org.apache.camel.Body; 021 import org.apache.camel.Exchange; 022 import org.apache.camel.Expression; 023 import org.apache.camel.Header; 024 import org.apache.camel.Property; 025 import org.apache.camel.RuntimeCamelException; 026 import org.apache.camel.builder.ExpressionBuilder; 027 import org.apache.commons.logging.Log; 028 import org.apache.commons.logging.LogFactory; 029 030 import java.lang.annotation.Annotation; 031 import java.lang.reflect.Method; 032 import java.util.Arrays; 033 import java.util.Collection; 034 import java.util.Map; 035 import java.util.concurrent.ConcurrentHashMap; 036 037 /** 038 * Represents the metadata about a bean type created via a combination of 039 * introspection and annotations together with some useful sensible defaults 040 * 041 * @version $Revision: $ 042 */ 043 public class BeanInfo { 044 private static final transient Log log = LogFactory.getLog(BeanInfo.class); 045 private Class type; 046 private MethodInvocationStrategy strategy; 047 private Map<String, MethodInfo> operations = new ConcurrentHashMap<String, MethodInfo>(); 048 private MethodInfo defaultExpression; 049 050 public BeanInfo(Class type, MethodInvocationStrategy strategy) { 051 this.type = type; 052 this.strategy = strategy; 053 } 054 055 public Class getType() { 056 return type; 057 } 058 059 public void introspect() { 060 introspect(getType()); 061 if (operations.size() == 1) { 062 Collection<MethodInfo> methodInfos = operations.values(); 063 for (MethodInfo methodInfo : methodInfos) { 064 defaultExpression = methodInfo; 065 } 066 } 067 } 068 069 public MethodInvocation createInvocation(Method method, Object pojo, Exchange messageExchange) throws RuntimeCamelException { 070 MethodInfo methodInfo = introspect(type, method); 071 return methodInfo.createMethodInvocation(pojo, messageExchange); 072 } 073 074 public MethodInvocation createInvocation(Object pojo, Exchange messageExchange) throws RuntimeCamelException { 075 MethodInfo methodInfo = null; 076 077 // TODO use some other mechanism? 078 String name = messageExchange.getIn().getHeader("org.apache.camel.MethodName", String.class); 079 if (name != null) { 080 methodInfo = operations.get(name); 081 } 082 if (methodInfo == null) { 083 methodInfo = defaultExpression; 084 } 085 if (methodInfo != null) { 086 return methodInfo.createMethodInvocation(pojo, messageExchange); 087 } 088 return null; 089 } 090 091 protected void introspect(Class clazz) { 092 Method[] methods = clazz.getDeclaredMethods(); 093 for (Method method : methods) { 094 introspect(clazz, method); 095 } 096 Class superclass = clazz.getSuperclass(); 097 if (superclass != null && !superclass.equals(Object.class)) { 098 introspect(superclass); 099 } 100 } 101 102 protected MethodInfo introspect(Class clazz, Method method) { 103 Class[] parameterTypes = method.getParameterTypes(); 104 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 105 final Expression[] parameterExpressions = new Expression[parameterTypes.length]; 106 for (int i = 0; i < parameterTypes.length; i++) { 107 Class parameterType = parameterTypes[i]; 108 Expression expression = createParameterUnmarshalExpression(clazz, method, 109 parameterType, parameterAnnotations[i]); 110 if (expression == null) { 111 if (log.isDebugEnabled()) { 112 log.debug("No expression available for method: " 113 + method.toString() + " parameter: " + i + " so ignoring method"); 114 } 115 if (parameterTypes.length == 1) { 116 // lets assume its the body 117 expression = ExpressionBuilder.bodyExpression(parameterType); 118 } 119 else { 120 return null; 121 } 122 } 123 parameterExpressions[i] = expression; 124 } 125 126 // now lets add the method to the repository 127 String opName = method.getName(); 128 129 /* 130 131 TODO allow an annotation to expose the operation name to use 132 133 if (method.getAnnotation(Operation.class) != null) { 134 String name = method.getAnnotation(Operation.class).name(); 135 if (name != null && name.length() > 0) { 136 opName = name; 137 } 138 } 139 */ 140 Expression parametersExpression = createMethodParametersExpression(parameterExpressions); 141 MethodInfo methodInfo = new MethodInfo(clazz, method, parametersExpression); 142 operations.put(opName, methodInfo); 143 return methodInfo; 144 } 145 146 protected Expression createMethodParametersExpression(final Expression[] parameterExpressions) { 147 return new Expression<Exchange>() { 148 public Object evaluate(Exchange exchange) { 149 Object[] answer = new Object[parameterExpressions.length]; 150 for (int i = 0; i < parameterExpressions.length; i++) { 151 Expression parameterExpression = parameterExpressions[i]; 152 answer[i] = parameterExpression.evaluate(exchange); 153 } 154 return answer; 155 } 156 157 @Override 158 public String toString() { 159 return "parametersExpression" + Arrays.asList(parameterExpressions); 160 } 161 }; 162 } 163 164 /** 165 * Creates an expression for the given parameter type if the parameter can be mapped 166 * automatically or null if the parameter cannot be mapped due to unsufficient 167 * annotations or not fitting with the default type conventions. 168 */ 169 protected Expression createParameterUnmarshalExpression(Class clazz, Method method, Class parameterType, Annotation[] parameterAnnotation) { 170 171 // TODO look for a parameter annotation that converts into an expression 172 for (Annotation annotation : parameterAnnotation) { 173 Expression answer = createParameterUnmarshalExpressionForAnnotation( 174 clazz, method, parameterType, annotation); 175 if (answer != null) { 176 return answer; 177 } 178 } 179 return strategy.getDefaultParameterTypeExpression(parameterType); 180 } 181 182 protected Expression createParameterUnmarshalExpressionForAnnotation(Class clazz, Method method, Class parameterType, Annotation annotation) { 183 if (annotation instanceof Property) { 184 Property propertyAnnotation = (Property) annotation; 185 return ExpressionBuilder.propertyExpression(propertyAnnotation.name()); 186 } 187 else if (annotation instanceof Header) { 188 Header headerAnnotation = (Header) annotation; 189 return ExpressionBuilder.headerExpression(headerAnnotation.name()); 190 } 191 else if (annotation instanceof Body) { 192 Body content = (Body) annotation; 193 return ExpressionBuilder.bodyExpression(parameterType); 194 195 // TODO allow annotations to be used to create expressions? 196 /* 197 } else if (annotation instanceof XPath) { 198 XPath xpathAnnotation = (XPath) annotation; 199 return new JAXPStringXPathExpression(xpathAnnotation.xpath()); 200 } 201 */ 202 } 203 return null; 204 } 205 }