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    }