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