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 046 private Class type; 047 private MethodInvocationStrategy strategy; 048 private Map<String, MethodInfo> operations = new ConcurrentHashMap<String, MethodInfo>(); 049 private MethodInfo defaultExpression; 050 051 public BeanInfo(Class type, MethodInvocationStrategy strategy) { 052 this.type = type; 053 this.strategy = strategy; 054 } 055 056 public Class getType() { 057 return type; 058 } 059 060 public void introspect() { 061 introspect(getType()); 062 if (operations.size() == 1) { 063 Collection<MethodInfo> methodInfos = operations.values(); 064 for (MethodInfo methodInfo : methodInfos) { 065 defaultExpression = methodInfo; 066 } 067 } 068 } 069 070 public MethodInvocation createInvocation(Method method, Object pojo, Exchange messageExchange) throws RuntimeCamelException { 071 MethodInfo methodInfo = introspect(type, method); 072 return methodInfo.createMethodInvocation(pojo, messageExchange); 073 } 074 075 public MethodInvocation createInvocation(Object pojo, Exchange messageExchange) throws RuntimeCamelException { 076 MethodInfo methodInfo = null; 077 078 // TODO use some other mechanism? 079 String name = messageExchange.getIn().getHeader("org.apache.camel.MethodName", String.class); 080 if (name != null) { 081 methodInfo = operations.get(name); 082 } 083 if (methodInfo == null) { 084 methodInfo = defaultExpression; 085 } 086 if (methodInfo != null) { 087 return methodInfo.createMethodInvocation(pojo, messageExchange); 088 } 089 return null; 090 } 091 092 protected void introspect(Class clazz) { 093 Method[] methods = clazz.getDeclaredMethods(); 094 for (Method method : methods) { 095 introspect(clazz, method); 096 } 097 Class superclass = clazz.getSuperclass(); 098 if (superclass != null && !superclass.equals(Object.class)) { 099 introspect(superclass); 100 } 101 } 102 103 protected MethodInfo introspect(Class clazz, Method method) { 104 Class[] parameterTypes = method.getParameterTypes(); 105 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 106 final Expression[] parameterExpressions = new Expression[parameterTypes.length]; 107 for (int i = 0; i < parameterTypes.length; i++) { 108 Class parameterType = parameterTypes[i]; 109 Expression expression = createParameterUnmarshalExpression(clazz, method, 110 parameterType, parameterAnnotations[i]); 111 if (expression == null) { 112 if (log.isDebugEnabled()) { 113 log.debug("No expression available for method: " 114 + method.toString() + " parameter: " + i + " so ignoring method"); 115 } 116 if (parameterTypes.length == 1) { 117 // lets assume its the body 118 expression = ExpressionBuilder.bodyExpression(parameterType); 119 } 120 else { 121 return null; 122 } 123 } 124 parameterExpressions[i] = expression; 125 } 126 127 // now lets add the method to the repository 128 String opName = method.getName(); 129 130 /* 131 132 TODO allow an annotation to expose the operation name to use 133 134 if (method.getAnnotation(Operation.class) != null) { 135 String name = method.getAnnotation(Operation.class).name(); 136 if (name != null && name.length() > 0) { 137 opName = name; 138 } 139 } 140 */ 141 Expression parametersExpression = createMethodParametersExpression(parameterExpressions); 142 MethodInfo methodInfo = new MethodInfo(clazz, method, parametersExpression); 143 operations.put(opName, methodInfo); 144 return methodInfo; 145 } 146 147 protected Expression createMethodParametersExpression(final Expression[] parameterExpressions) { 148 return new Expression<Exchange>() { 149 public Object evaluate(Exchange exchange) { 150 Object[] answer = new Object[parameterExpressions.length]; 151 for (int i = 0; i < parameterExpressions.length; i++) { 152 Expression parameterExpression = parameterExpressions[i]; 153 answer[i] = parameterExpression.evaluate(exchange); 154 } 155 return answer; 156 } 157 158 @Override 159 public String toString() { 160 return "parametersExpression" + Arrays.asList(parameterExpressions); 161 } 162 }; 163 } 164 165 /** 166 * Creates an expression for the given parameter type if the parameter can be mapped 167 * automatically or null if the parameter cannot be mapped due to unsufficient 168 * annotations or not fitting with the default type conventions. 169 */ 170 protected Expression createParameterUnmarshalExpression(Class clazz, Method method, Class parameterType, Annotation[] parameterAnnotation) { 171 172 // TODO look for a parameter annotation that converts into an expression 173 for (Annotation annotation : parameterAnnotation) { 174 Expression answer = createParameterUnmarshalExpressionForAnnotation( 175 clazz, method, parameterType, annotation); 176 if (answer != null) { 177 return answer; 178 } 179 } 180 return strategy.getDefaultParameterTypeExpression(parameterType); 181 } 182 183 protected Expression createParameterUnmarshalExpressionForAnnotation(Class clazz, Method method, Class parameterType, Annotation annotation) { 184 if (annotation instanceof Property) { 185 Property propertyAnnotation = (Property) annotation; 186 return ExpressionBuilder.propertyExpression(propertyAnnotation.name()); 187 } 188 else if (annotation instanceof Header) { 189 Header headerAnnotation = (Header) annotation; 190 return ExpressionBuilder.headerExpression(headerAnnotation.name()); 191 } 192 else if (annotation instanceof Body) { 193 Body content = (Body) annotation; 194 return ExpressionBuilder.bodyExpression(parameterType); 195 196 // TODO allow annotations to be used to create expressions? 197 /* 198 } else if (annotation instanceof XPath) { 199 XPath xpathAnnotation = (XPath) annotation; 200 return new JAXPStringXPathExpression(xpathAnnotation.xpath()); 201 } 202 */ 203 } 204 return null; 205 } 206 }