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 */
017package org.apache.camel.model.language;
018
019import javax.xml.bind.annotation.XmlAccessType;
020import javax.xml.bind.annotation.XmlAccessorType;
021import javax.xml.bind.annotation.XmlAttribute;
022import javax.xml.bind.annotation.XmlRootElement;
023import javax.xml.bind.annotation.XmlTransient;
024
025import org.apache.camel.CamelContext;
026import org.apache.camel.Expression;
027import org.apache.camel.ExpressionIllegalSyntaxException;
028import org.apache.camel.Predicate;
029import org.apache.camel.component.bean.BeanHolder;
030import org.apache.camel.component.bean.BeanInfo;
031import org.apache.camel.component.bean.ConstantBeanHolder;
032import org.apache.camel.component.bean.ConstantStaticTypeBeanHolder;
033import org.apache.camel.component.bean.MethodNotFoundException;
034import org.apache.camel.component.bean.RegistryBean;
035import org.apache.camel.language.bean.BeanExpression;
036import org.apache.camel.util.ObjectHelper;
037import 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)
047public 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 ConstantStaticTypeBeanHolder(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}