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