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