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