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.component.velocity;
018    
019    import java.io.InputStream;
020    import java.io.InputStreamReader;
021    import java.io.Reader;
022    import java.io.StringReader;
023    import java.io.StringWriter;
024    import java.util.Map;
025    import java.util.Properties;
026    
027    import org.apache.camel.Exchange;
028    import org.apache.camel.ExchangePattern;
029    import org.apache.camel.Message;
030    import org.apache.camel.component.ResourceBasedEndpoint;
031    import org.apache.camel.util.ExchangeHelper;
032    import org.apache.camel.util.ObjectHelper;
033    import org.apache.velocity.VelocityContext;
034    import org.apache.velocity.app.Velocity;
035    import org.apache.velocity.app.VelocityEngine;
036    import org.apache.velocity.context.Context;
037    import org.apache.velocity.runtime.log.CommonsLogLogChute;
038    import org.springframework.core.io.Resource;
039    
040    /**
041     * @version $Revision: 829724 $
042     */
043    public class VelocityEndpoint extends ResourceBasedEndpoint {
044        private VelocityEngine velocityEngine;
045        private boolean loaderCache = true;
046        private String encoding;
047        private String propertiesFile;
048    
049        public VelocityEndpoint() {
050        }
051    
052        public VelocityEndpoint(String uri, VelocityComponent component, String resourceUri) {
053            super(uri, component, resourceUri, null);
054        }
055    
056        @Override
057        public boolean isSingleton() {
058            return true;
059        }
060    
061        @Override
062        public ExchangePattern getExchangePattern() {
063            return ExchangePattern.InOut;
064        }
065    
066        @Override
067        protected String createEndpointUri() {
068            return "velocity:" + getResourceUri();
069        }
070    
071        private synchronized VelocityEngine getVelocityEngine() throws Exception {
072            if (velocityEngine == null) {
073                velocityEngine = new VelocityEngine();
074                Properties properties = new Properties();
075                // load the velocity properties from property file
076                if (ObjectHelper.isNotEmpty(getPropertiesFile())) {
077                    Resource resource = getResourceLoader().getResource(getPropertiesFile());
078                    InputStream reader = resource.getInputStream();               
079                    properties.load(reader);
080                    log.info("Loaded the velocity configuration file " + getPropertiesFile());
081                }
082                
083                properties.setProperty(Velocity.FILE_RESOURCE_LOADER_CACHE, isLoaderCache() ? "true" : "false");
084                properties.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, CommonsLogLogChute.class.getName());
085                properties.setProperty(CommonsLogLogChute.LOGCHUTE_COMMONS_LOG_NAME, VelocityEndpoint.class.getName());
086                velocityEngine.init(properties);
087    
088            }
089            return velocityEngine;
090        }
091    
092        public void setVelocityEngine(VelocityEngine velocityEngine) {
093            this.velocityEngine = velocityEngine;
094    
095        }
096    
097        public boolean isLoaderCache() {
098            return loaderCache;
099        }
100    
101        /**
102         * Enables / disables the velocity resource loader cache which is enabled by default
103         *
104         * @param loaderCache a flag to enable/disable the cache
105         */
106        public void setLoaderCache(boolean loaderCache) {
107            this.loaderCache = loaderCache;
108        }
109    
110        public void setEncoding(String encoding) {
111            this.encoding = encoding;
112        }
113    
114        public String getEncoding() {
115            return encoding;
116        }
117        
118        public void setPropertiesFile(String file) {
119            propertiesFile = file;
120        }
121        
122        public String getPropertiesFile() {
123            return propertiesFile;
124        }
125    
126        public VelocityEndpoint findOrCreateEndpoint(String uri, String newResourceUri) {
127            String newUri = uri.replace(getResourceUri(), newResourceUri);
128            if (log.isDebugEnabled()) {
129                log.debug("Getting endpoint with URI: " + newUri);
130            }
131            return (VelocityEndpoint) getCamelContext().getEndpoint(newUri);
132        }
133    
134        @SuppressWarnings("unchecked")
135        @Override
136        protected void onExchange(Exchange exchange) throws Exception {
137            String path = getResourceUri();
138            ObjectHelper.notNull(path, "resourceUri");
139    
140            String newResourceUri = exchange.getIn().getHeader(VelocityConstants.VELOCITY_RESOURCE_URI, String.class);
141            if (newResourceUri != null) {
142                exchange.getIn().removeHeader(VelocityConstants.VELOCITY_RESOURCE_URI);
143    
144                if (log.isDebugEnabled()) {
145                    log.debug(VelocityConstants.VELOCITY_RESOURCE_URI + " set to " + newResourceUri + " creating new endpoint to handle exchange");
146                }
147                VelocityEndpoint newEndpoint = findOrCreateEndpoint(getEndpointUri(), newResourceUri);
148                newEndpoint.onExchange(exchange);
149                return;
150            }
151    
152            Resource resource = null;
153            Reader reader;
154            String content = exchange.getIn().getHeader(VelocityConstants.VELOCITY_TEMPLATE, String.class);
155            if (content != null) {
156                // use content from header
157                reader = new StringReader(content);
158                if (log.isDebugEnabled()) {
159                    log.debug("Velocity content read from header " + VelocityConstants.VELOCITY_TEMPLATE + " for endpoint " + getEndpointUri());
160                }
161                // remove the header to avoid it being propagated in the routing
162                exchange.getIn().removeHeader(VelocityConstants.VELOCITY_TEMPLATE);
163            } else {
164                // use resource from endpoint configuration
165                resource = getResource();
166                ObjectHelper.notNull(resource, "resource");
167                if (log.isDebugEnabled()) {
168                    log.debug("Velocity content read from resource " + resource + " with resourceUri: " + path + " for endpoint " + getEndpointUri());
169                }
170                reader = getEncoding() != null ? new InputStreamReader(getResourceAsInputStream(), getEncoding()) : new InputStreamReader(getResourceAsInputStream());
171            }
172    
173            // getResourceAsInputStream also considers the content cache
174            StringWriter buffer = new StringWriter();
175            String logTag = getClass().getName();
176            Map variableMap = ExchangeHelper.createVariableMap(exchange);
177            Context velocityContext = new VelocityContext(variableMap);
178    
179            // let velocity parse and generate the result in buffer
180            VelocityEngine engine = getVelocityEngine();
181            if (log.isDebugEnabled()) {
182                log.debug("Velocity is evaluating using velocity context: " + variableMap);
183            }
184            engine.evaluate(velocityContext, buffer, logTag, reader);
185    
186            // now lets output the results to the exchange
187            Message out = exchange.getOut();
188            out.setBody(buffer.toString());
189           
190            Map<String, Object> headers = (Map<String, Object>) velocityContext.get("headers");
191            for (String key : headers.keySet()) {
192                out.setHeader(key, headers.get(key));
193            }
194        }
195    
196    }