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.language.spel;
018
019import org.apache.camel.Exchange;
020import org.apache.camel.ExpressionEvaluationException;
021import org.apache.camel.impl.ExpressionSupport;
022import org.apache.camel.spring.SpringCamelContext;
023import org.apache.camel.spring.util.RegistryBeanResolver;
024import org.springframework.context.ApplicationContext;
025import org.springframework.context.expression.BeanFactoryResolver;
026import org.springframework.expression.BeanResolver;
027import org.springframework.expression.EvaluationContext;
028import org.springframework.expression.Expression;
029import org.springframework.expression.ParserContext;
030import org.springframework.expression.common.TemplateParserContext;
031import org.springframework.expression.spel.standard.SpelExpressionParser;
032import org.springframework.expression.spel.support.StandardEvaluationContext;
033
034/**
035 * Class responsible for evaluating <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#expressions">
036 * Spring Expression Language (SpEL)</a> in the context of Camel.
037 */
038@SuppressWarnings("deprecation")
039public class SpelExpression extends ExpressionSupport {
040
041    private final String expressionString;
042    private final Class<?> type;
043    private final BeanResolver beanResolver;
044
045    // SpelExpressionParser is thread-safe according to the docs
046    private final SpelExpressionParser expressionParser;
047
048    public SpelExpression(String expressionString, Class<?> type) {
049        this(expressionString, type, null);
050    }
051
052    public SpelExpression(String expressionString, Class<?> type, BeanResolver beanResolver) {
053        this.expressionString = expressionString;
054        this.type = type;
055        this.beanResolver = beanResolver;
056        this.expressionParser = new SpelExpressionParser();
057    }
058
059    public static SpelExpression spel(String expression) {
060        return new SpelExpression(expression, Object.class);
061    }
062
063    public <T> T evaluate(Exchange exchange, Class<T> tClass) {
064        try {
065            Expression expression = parseExpression();
066            EvaluationContext evaluationContext = createEvaluationContext(exchange);
067            Object value = expression.getValue(evaluationContext);
068            // Let Camel handle the type conversion
069            return exchange.getContext().getTypeConverter().convertTo(tClass, value);
070        } catch (Exception e) {
071            throw new ExpressionEvaluationException(this, exchange, e);
072        }
073    }
074
075    private EvaluationContext createEvaluationContext(Exchange exchange) {
076        StandardEvaluationContext evaluationContext = new StandardEvaluationContext(new RootObject(exchange));
077        if (beanResolver != null) {
078            evaluationContext.setBeanResolver(beanResolver);
079        } else if (exchange.getContext() instanceof SpringCamelContext) {
080            // Support references (like @foo) in expressions to beans defined in the Registry/ApplicationContext
081            ApplicationContext applicationContext = ((SpringCamelContext) exchange.getContext()).getApplicationContext();
082            evaluationContext.setBeanResolver(new BeanFactoryResolver(applicationContext));
083        } else {
084            evaluationContext.setBeanResolver(new RegistryBeanResolver(exchange.getContext().getRegistry()));
085        }
086        return evaluationContext;
087    }
088
089    private Expression parseExpression() {
090        // Support template parsing with #{ } delimiters
091        ParserContext parserContext = new TemplateParserContext();
092        Expression expression = expressionParser.parseExpression(expressionString, parserContext);
093        return expression;
094    }
095
096    public Class<?> getType() {
097        return type;
098    }
099
100    protected String assertionFailureMessage(Exchange exchange) {
101        return expressionString;
102    }
103
104    @Override
105    public String toString() {
106        return "SpelExpression[" + expressionString + "]";
107    }
108}