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.model.language;
018    
019    import javax.xml.bind.annotation.XmlAccessType;
020    import javax.xml.bind.annotation.XmlAccessorType;
021    import javax.xml.bind.annotation.XmlAttribute;
022    import javax.xml.bind.annotation.XmlRootElement;
023    import javax.xml.bind.annotation.XmlTransient;
024    
025    import org.apache.camel.CamelContext;
026    import org.apache.camel.Expression;
027    import org.apache.camel.ExpressionIllegalSyntaxException;
028    import org.apache.camel.Predicate;
029    import org.apache.camel.component.bean.BeanHolder;
030    import org.apache.camel.component.bean.BeanInfo;
031    import org.apache.camel.component.bean.ConstantBeanHolder;
032    import org.apache.camel.component.bean.ConstantTypeBeanHolder;
033    import org.apache.camel.component.bean.MethodNotFoundException;
034    import org.apache.camel.component.bean.RegistryBean;
035    import org.apache.camel.language.bean.BeanExpression;
036    import org.apache.camel.util.ObjectHelper;
037    import org.apache.camel.util.OgnlHelper;
038    
039    /**
040     * For expressions and predicates using the
041     * <a href="http://camel.apache.org/bean-language.html">bean language</a>
042     *
043     * @version
044     */
045    @XmlRootElement(name = "method")
046    @XmlAccessorType(XmlAccessType.FIELD)
047    public class MethodCallExpression extends ExpressionDefinition {
048        @XmlAttribute
049        @Deprecated
050        private String bean;
051        @XmlAttribute
052        private String ref;
053        @XmlAttribute
054        private String method;
055        @XmlAttribute(name = "beanType")
056        private String beanTypeName;
057        @XmlTransient
058        private Class<?> beanType;
059        @XmlTransient
060        private Object instance;
061    
062        public MethodCallExpression() {
063        }
064    
065        public MethodCallExpression(String beanName) {
066            this(beanName, null);
067        }
068    
069        public MethodCallExpression(String beanName, String method) {
070            super(beanName);
071            this.method = method;
072        }
073    
074        public MethodCallExpression(Object instance) {
075            this(instance, null);
076        }
077    
078        public MethodCallExpression(Object instance, String method) {
079            super(ObjectHelper.className(instance));
080            // must use setter as they have special logic
081            setInstance(instance);
082            setMethod(method);
083        }
084    
085        public MethodCallExpression(Class<?> type) {
086            this(type, null);
087        }
088    
089        public MethodCallExpression(Class<?> type, String method) {
090            super(type.getName());
091            this.beanType = type;
092            this.method = method;
093        }
094    
095        public String getLanguage() {
096            return "bean";
097        }
098    
099        public String getBean() {
100            return bean;
101        }
102    
103        public void setBean(String bean) {
104            this.bean = bean;
105        }
106    
107        public String getRef() {
108            return ref;
109        }
110    
111        public void setRef(String ref) {
112            this.ref = ref;
113        }
114    
115        public String getMethod() {
116            return method;
117        }
118    
119        public void setMethod(String method) {
120            this.method = method;
121        }
122    
123        public Class<?> getBeanType() {
124            return beanType;
125        }
126    
127        public void setBeanType(Class<?> beanType) {
128            this.beanType = beanType;
129            this.instance = null;
130        }
131    
132        public String getBeanTypeName() {
133            return beanTypeName;
134        }
135    
136        public void setBeanTypeName(String beanTypeName) {
137            this.beanTypeName = beanTypeName;
138        }
139    
140        public Object getInstance() {
141            return instance;
142        }
143    
144        public void setInstance(Object instance) {
145            // people may by mistake pass in a class type as the instance
146            if (instance instanceof Class) {
147                this.beanType = (Class<?>) instance;
148                this.instance = null;
149            } else {
150                this.beanType = null;
151                this.instance = instance;
152            }
153        }
154    
155        @Override
156        public Expression createExpression(CamelContext camelContext) {
157            Expression answer;
158    
159            if (beanType == null && beanTypeName != null) {
160                try {
161                    beanType = camelContext.getClassResolver().resolveMandatoryClass(beanTypeName);
162                } catch (ClassNotFoundException e) {
163                    throw ObjectHelper.wrapRuntimeCamelException(e);
164                }
165            }
166    
167            BeanHolder holder;
168            if (beanType != null) {
169                // create a bean if there is a default public no-arg constructor
170                if (ObjectHelper.hasDefaultPublicNoArgConstructor(beanType)) {
171                    instance = camelContext.getInjector().newInstance(beanType);
172                    holder = new ConstantBeanHolder(instance, camelContext);
173                } else {
174                    holder = new ConstantTypeBeanHolder(beanType, camelContext);
175                }
176            } else if (instance != null) {
177                holder = new ConstantBeanHolder(instance, camelContext);
178            } else {
179                String ref = beanName();
180                // if its a ref then check that the ref exists
181                BeanHolder regHolder = new RegistryBean(camelContext, ref);
182                // get the bean which will check that it exists
183                instance = regHolder.getBean();
184                holder = new ConstantBeanHolder(instance, camelContext);
185            }
186    
187            // create answer using the holder
188            answer = new BeanExpression(holder, getMethod());
189    
190            // and do sanity check that if a method name was given, that it exists
191            validateHasMethod(camelContext, instance, beanType, getMethod());
192            return answer;
193        }
194    
195        @Override
196        public Predicate createPredicate(CamelContext camelContext) {
197            return (BeanExpression) createExpression(camelContext);
198        }
199    
200        /**
201         * Validates the given bean has the method.
202         * <p/>
203         * This implementation will skip trying to validate OGNL method name expressions.
204         *
205         * @param context  camel context
206         * @param bean     the bean instance
207         * @param type     the bean type
208         * @param method   the method, can be <tt>null</tt> if no method name provided
209         * @throws org.apache.camel.RuntimeCamelException is thrown if bean does not have the method
210         */
211        protected void validateHasMethod(CamelContext context, Object bean, Class<?> type, String method) {
212            if (method == null) {
213                return;
214            }
215    
216            if (bean == null && type == null) {
217                throw new IllegalArgumentException("Either bean or type should be provided on " + this);
218            }
219    
220            // do not try to validate ognl methods
221            if (OgnlHelper.isValidOgnlExpression(method)) {
222                return;
223            }
224    
225            // if invalid OGNL then fail
226            if (OgnlHelper.isInvalidValidOgnlExpression(method)) {
227                ExpressionIllegalSyntaxException cause = new ExpressionIllegalSyntaxException(method);
228                throw ObjectHelper.wrapRuntimeCamelException(new MethodNotFoundException(bean != null ? bean : type, method, cause));
229            }
230    
231            if (bean != null) {
232                BeanInfo info = new BeanInfo(context, bean.getClass());
233                if (!info.hasMethod(method)) {
234                    throw ObjectHelper.wrapRuntimeCamelException(new MethodNotFoundException(null, bean, method));
235                }
236            } else {
237                BeanInfo info = new BeanInfo(context, type);
238                // must be a static method as we do not have a bean instance to invoke
239                if (!info.hasStaticMethod(method)) {
240                    throw ObjectHelper.wrapRuntimeCamelException(new MethodNotFoundException(null, type, method, true));
241                }
242            }
243        }
244    
245        protected String beanName() {
246            if (bean != null) {
247                return bean;
248            } else if (ref != null) {
249                return ref;
250            } else if (instance != null) {
251                return ObjectHelper.className(instance);
252            }
253            return getExpression();
254        }
255    
256        @Override
257        public String toString() {
258            return "bean{" + beanName() + (method != null ? ", method=" + method : "") + "}";
259        }
260    }