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.servicemix.bean.support;
018
019 import java.lang.annotation.Annotation;
020 import java.lang.reflect.Method;
021 import java.util.Collection;
022 import java.util.Map;
023 import java.util.concurrent.ConcurrentHashMap;
024
025 import javax.jbi.messaging.MessageExchange;
026 import javax.jbi.messaging.MessagingException;
027 import javax.jbi.messaging.NormalizedMessage;
028 import javax.xml.namespace.QName;
029
030 import org.aopalliance.intercept.MethodInvocation;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033 import org.apache.servicemix.bean.Content;
034 import org.apache.servicemix.bean.Operation;
035 import org.apache.servicemix.bean.Property;
036 import org.apache.servicemix.bean.XPath;
037 import org.apache.servicemix.components.util.MessageHelper;
038 import org.apache.servicemix.expression.Expression;
039 import org.apache.servicemix.expression.JAXPStringXPathExpression;
040 import org.apache.servicemix.expression.PropertyExpression;
041 import org.apache.servicemix.jbi.messaging.PojoMarshaler;
042
043 /**
044 * Represents the metadata about a bean type created via a combination of
045 * introspection and annotations together with some useful sensible defaults
046 *
047 * @version $Revision: $
048 */
049 public class BeanInfo {
050
051 private static final Log LOG = LogFactory.getLog(BeanInfo.class);
052
053 private Class type;
054 private MethodInvocationStrategy strategy;
055 private Map<String, MethodInfo> operations = new ConcurrentHashMap<String, MethodInfo>();
056 private MethodInfo defaultExpression;
057
058
059 public BeanInfo(Class type, MethodInvocationStrategy strategy) {
060 this.type = type;
061 this.strategy = strategy;
062 }
063
064 public Class getType() {
065 return type;
066 }
067
068 public void introspect() {
069 introspect(getType());
070 if (operations.size() == 1) {
071 Collection<MethodInfo> methodInfos = operations.values();
072 for (MethodInfo methodInfo : methodInfos) {
073 defaultExpression = methodInfo;
074 }
075 }
076 }
077
078 public MethodInvocation createInvocation(Object pojo, MessageExchange messageExchange) throws MessagingException {
079 QName operation = messageExchange.getOperation();
080 MethodInfo methodInfo = null;
081 if (operation == null) {
082 methodInfo = defaultExpression;
083 } else {
084 methodInfo = operations.get(operation.getLocalPart());
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 void 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 return;
117 }
118 parameterExpressions[i] = expression;
119 }
120
121 // now lets add the method to the repository
122 String opName = method.getName();
123 if (method.getAnnotation(Operation.class) != null) {
124 String name = method.getAnnotation(Operation.class).name();
125 if (name != null && name.length() > 0) {
126 opName = name;
127 }
128 }
129 Expression parametersExpression = createMethodParametersExpression(parameterExpressions);
130 operations.put(opName, new MethodInfo(clazz, method, parametersExpression));
131 }
132
133 protected Expression createMethodParametersExpression(final Expression[] parameterExpressions) {
134 return new Expression() {
135
136 public Object evaluate(MessageExchange messageExchange,
137 NormalizedMessage normalizedMessage) throws MessagingException {
138 Object[] answer = new Object[parameterExpressions.length];
139 for (int i = 0; i < parameterExpressions.length; i++) {
140 Expression parameterExpression = parameterExpressions[i];
141 answer[i] = parameterExpression.evaluate(messageExchange, normalizedMessage);
142 }
143 return answer;
144 }
145 };
146 }
147
148 /**
149 * Creates an expression for the given parameter type if the parameter can be mapped
150 * automatically or null if the parameter cannot be mapped due to unsufficient
151 * annotations or not fitting with the default type conventions.
152 */
153 protected Expression createParameterUnmarshalExpression(Class clazz, Method method,
154 Class parameterType, Annotation[] parameterAnnotation) {
155 // TODO look for a parameter annotation that converts into an expression
156 for (Annotation annotation : parameterAnnotation) {
157 Expression answer = createParameterUnmarshalExpressionForAnnotation(
158 clazz, method, parameterType, annotation);
159 if (answer != null) {
160 return answer;
161 }
162 }
163 return strategy.getDefaultParameterTypeExpression(parameterType);
164 }
165
166 protected Expression createParameterUnmarshalExpressionForAnnotation(Class clazz, Method method,
167 Class parameterType, Annotation annotation) {
168 if (annotation instanceof Property) {
169 Property propertyAnnotation = (Property) annotation;
170 return new PropertyExpression(propertyAnnotation.name());
171 } else if (annotation instanceof Content) {
172 Content content = (Content) annotation;
173 final PojoMarshaler marshaller = newInstance(content);
174 return createContentExpression(marshaller);
175 } else if (annotation instanceof XPath) {
176 XPath xpathAnnotation = (XPath) annotation;
177 return new JAXPStringXPathExpression(xpathAnnotation.xpath());
178 }
179 return null;
180 }
181
182 protected Expression createContentExpression(final PojoMarshaler marshaller) {
183 return new Expression() {
184 public Object evaluate(MessageExchange exchange, NormalizedMessage message) throws MessagingException {
185 return MessageHelper.getBody(message, marshaller);
186 }
187 };
188 }
189
190 protected PojoMarshaler newInstance(Content content) {
191 try {
192 return content.marshalType().newInstance();
193 } catch (InstantiationException e) {
194 throw new RuntimeException(e);
195 } catch (IllegalAccessException e) {
196 throw new RuntimeException(e);
197 }
198 }
199 }