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: 982894 $
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.RESOURCE_LOADER, "file, class");
086                properties.setProperty("class.resource.loader.description", "Velocity Classpath Resource Loader");
087                properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
088                properties.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, CommonsLogLogChute.class.getName());
089                properties.setProperty(CommonsLogLogChute.LOGCHUTE_COMMONS_LOG_NAME, VelocityEndpoint.class.getName());
090                velocityEngine.init(properties);
091    
092            }
093            return velocityEngine;
094        }
095    
096        public void setVelocityEngine(VelocityEngine velocityEngine) {
097            this.velocityEngine = velocityEngine;
098    
099        }
100    
101        public boolean isLoaderCache() {
102            return loaderCache;
103        }
104    
105        /**
106         * Enables / disables the velocity resource loader cache which is enabled by default
107         *
108         * @param loaderCache a flag to enable/disable the cache
109         */
110        public void setLoaderCache(boolean loaderCache) {
111            this.loaderCache = loaderCache;
112        }
113    
114        public void setEncoding(String encoding) {
115            this.encoding = encoding;
116        }
117    
118        public String getEncoding() {
119            return encoding;
120        }
121    
122        public void setPropertiesFile(String file) {
123            propertiesFile = file;
124        }
125    
126        public String getPropertiesFile() {
127            return propertiesFile;
128        }
129    
130        public VelocityEndpoint findOrCreateEndpoint(String uri, String newResourceUri) {
131            String newUri = uri.replace(getResourceUri(), newResourceUri);
132            if (log.isDebugEnabled()) {
133                log.debug("Getting endpoint with URI: " + newUri);
134            }
135            return (VelocityEndpoint) getCamelContext().getEndpoint(newUri);
136        }
137    
138        @SuppressWarnings("unchecked")
139        @Override
140        protected void onExchange(Exchange exchange) throws Exception {
141            String path = getResourceUri();
142            ObjectHelper.notNull(path, "resourceUri");
143    
144            String newResourceUri = exchange.getIn().getHeader(VelocityConstants.VELOCITY_RESOURCE_URI, String.class);
145            if (newResourceUri != null) {
146                exchange.getIn().removeHeader(VelocityConstants.VELOCITY_RESOURCE_URI);
147    
148                if (log.isDebugEnabled()) {
149                    log.debug(VelocityConstants.VELOCITY_RESOURCE_URI + " set to " + newResourceUri + " creating new endpoint to handle exchange");
150                }
151                VelocityEndpoint newEndpoint = findOrCreateEndpoint(getEndpointUri(), newResourceUri);
152                newEndpoint.onExchange(exchange);
153                return;
154            }
155    
156            Resource resource = null;
157            Reader reader;
158            String content = exchange.getIn().getHeader(VelocityConstants.VELOCITY_TEMPLATE, String.class);
159            if (content != null) {
160                // use content from header
161                reader = new StringReader(content);
162                if (log.isDebugEnabled()) {
163                    log.debug("Velocity content read from header " + VelocityConstants.VELOCITY_TEMPLATE + " for endpoint " + getEndpointUri());
164                }
165                // remove the header to avoid it being propagated in the routing
166                exchange.getIn().removeHeader(VelocityConstants.VELOCITY_TEMPLATE);
167            } else {
168                // use resource from endpoint configuration
169                resource = getResource();
170                ObjectHelper.notNull(resource, "resource");
171                if (log.isDebugEnabled()) {
172                    log.debug("Velocity content read from resource " + resource + " with resourceUri: " + path + " for endpoint " + getEndpointUri());
173                }
174                reader = getEncoding() != null ? new InputStreamReader(getResourceAsInputStream(), getEncoding()) : new InputStreamReader(getResourceAsInputStream());
175            }
176    
177            // getResourceAsInputStream also considers the content cache
178            StringWriter buffer = new StringWriter();
179            String logTag = getClass().getName();
180            Map variableMap = ExchangeHelper.createVariableMap(exchange);
181            Context velocityContext = new VelocityContext(variableMap);
182    
183            // let velocity parse and generate the result in buffer
184            VelocityEngine engine = getVelocityEngine();
185            if (log.isDebugEnabled()) {
186                log.debug("Velocity is evaluating using velocity context: " + variableMap);
187            }
188            engine.evaluate(velocityContext, buffer, logTag, reader);
189    
190            // now lets output the results to the exchange
191            Message out = exchange.getOut();
192            out.setBody(buffer.toString());
193    
194            Map<String, Object> headers = (Map<String, Object>) velocityContext.get("headers");
195            for (Entry<String, Object> entry : headers.entrySet()) {
196                out.setHeader(entry.getKey(), entry.getValue());
197            }
198        }
199    }