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.language.juel;
018    
019    import java.io.IOException;
020    
021    import javax.el.ArrayELResolver;
022    import javax.el.CompositeELResolver;
023    import javax.el.ELContext;
024    import javax.el.ELResolver;
025    import javax.el.ExpressionFactory;
026    import javax.el.ListELResolver;
027    import javax.el.MapELResolver;
028    import javax.el.ResourceBundleELResolver;
029    import javax.el.ValueExpression;
030    import de.odysseus.el.ExpressionFactoryImpl;
031    import de.odysseus.el.util.SimpleContext;
032    
033    import org.apache.camel.CamelContext;
034    import org.apache.camel.Exchange;
035    import org.apache.camel.Message;
036    import org.apache.camel.impl.ExpressionSupport;
037    import org.apache.camel.spi.FactoryFinder;
038    import org.slf4j.Logger;
039    import org.slf4j.LoggerFactory;
040    
041    /**
042     * The <a href="http://camel.apache.org/el.html">EL Language from JSP and JSF</a>
043     * using the <a href="http://camel.apache.org/juel.html">JUEL library</a>
044     *
045     * @version 
046     */
047    public class JuelExpression extends ExpressionSupport {
048        public static final String DEFAULT_EXPRESSION_FACTORY_IMPL_CLASS = "de.odysseus.el.ExpressionFactoryImpl";
049        private static final Logger LOG = LoggerFactory.getLogger(JuelExpression.class);
050    
051        private final String expression;
052        private final Class<?> type;
053        private ExpressionFactory expressionFactory;
054    
055        public JuelExpression(String expression, Class<?> type) {
056            this.expression = expression;
057            this.type = type;
058        }
059    
060        public static JuelExpression el(String expression) {
061            return new JuelExpression(expression, Object.class);
062        }
063    
064        public <T> T evaluate(Exchange exchange, Class<T> tClass) {
065            // TODO we could use caching here but then we'd have possible concurrency issues
066            // so lets assume that the provider caches
067            
068            // Create (if needed) the ExpressionFactory first from the CamelContext using FactoryFinder
069            ExpressionFactory factory = getExpressionFactory(exchange.getContext());
070            ELContext context = populateContext(createContext(), exchange);
071            ValueExpression valueExpression = factory.createValueExpression(context, expression, type);
072            Object value = valueExpression.getValue(context);
073            return exchange.getContext().getTypeConverter().convertTo(tClass, value);
074        }
075    
076        public ExpressionFactory getExpressionFactory(CamelContext context) {
077            if (expressionFactory == null && context != null) {
078                try {
079                    FactoryFinder finder = context.getFactoryFinder("META-INF/services/org/apache/camel/language/");
080                    Class<?> clazz = finder.findClass("el", "impl.", ExpressionFactory.class);
081                    if (clazz != null) {
082                        expressionFactory = (ExpressionFactory)clazz.newInstance();
083                    }
084                } catch (ClassNotFoundException e) {
085                    LOG.debug("'impl.class' not found", e);
086                } catch (IOException e) {
087                    LOG.debug("No impl class for juel ExpressionFactory defined in 'META-INF/services/org/apache/camel/language/el'", e);
088                } catch (InstantiationException e) {
089                    LOG.debug("Failed to instantiate juel ExpressionFactory implementation class.", e);
090                } catch (IllegalAccessException e) {
091                    LOG.debug("Failed to instantiate juel ExpressionFactory implementation class.", e);
092                }
093            }
094            return getExpressionFactory();
095        }
096    
097        public ExpressionFactory getExpressionFactory() {
098            if (expressionFactory == null) {
099                expressionFactory = new ExpressionFactoryImpl();
100            }
101            return expressionFactory;
102        }
103    
104        public void setExpressionFactory(ExpressionFactory expressionFactory) {
105            this.expressionFactory = expressionFactory;
106        }
107    
108        protected ELContext populateContext(ELContext context, Exchange exchange) {
109            setVariable(context, "exchange", exchange, Exchange.class);
110            setVariable(context, "in", exchange.getIn(), Message.class);
111            if (exchange.hasOut()) {
112                setVariable(context, "out", exchange.getOut(), Message.class);
113            }
114            return context;
115        }
116    
117        protected void setVariable(ELContext context, String name, Object value, Class<?> type) {
118            ValueExpression valueExpression = getExpressionFactory().createValueExpression(value, type);
119            SimpleContext simpleContext = (SimpleContext) context;
120            simpleContext.setVariable(name, valueExpression);
121        }
122    
123        /**
124         * Factory method to create the EL context
125         */
126        protected ELContext createContext() {
127            ELResolver resolver = new CompositeELResolver() {
128                {
129                    //add(methodResolver);
130                    add(new ArrayELResolver(false));
131                    add(new ListELResolver(false));
132                    add(new MapELResolver(false));
133                    add(new ResourceBundleELResolver());
134                    add(new BeanAndMethodELResolver());
135                }
136            };
137            return new SimpleContext(resolver);
138        }
139    
140        protected String assertionFailureMessage(Exchange exchange) {
141            return expression;
142        }
143    }