001: /**
002: * Copyright (c) 2003-2007, David A. Czarnecki
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009: * following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
011: * following disclaimer in the documentation and/or other materials provided with the distribution.
012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
013: * endorse or promote products derived from this software without specific prior written permission.
014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
015: * without prior written permission of David A. Czarnecki.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030: */package org.blojsom.dispatcher.velocity;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
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.exception.MethodInvocationException;
038: import org.apache.velocity.exception.ParseErrorException;
039: import org.apache.velocity.exception.ResourceNotFoundException;
040: import org.apache.velocity.util.EnumerationIterator;
041: import org.blojsom.dispatcher.Dispatcher;
042: import org.blojsom.blog.Blog;
043: import org.blojsom.util.BlojsomConstants;
044: import org.blojsom.util.BlojsomUtils;
045:
046: import javax.servlet.ServletConfig;
047: import javax.servlet.ServletContext;
048: import javax.servlet.ServletException;
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.HttpServletResponse;
051: import javax.servlet.http.HttpSession;
052: import java.io.IOException;
053: import java.io.StringWriter;
054: import java.io.Writer;
055: import java.util.Map;
056: import java.util.Properties;
057:
058: /**
059: * Velocity dispatcher
060: *
061: * @author David Czarnecki
062: * @since blojsom 3.0
063: * @version $Id: VelocityDispatcher.java,v 1.6 2007/01/17 02:35:17 czarneckid Exp $
064: */
065: public class VelocityDispatcher implements Dispatcher {
066:
067: private Log _logger = LogFactory.getLog(VelocityDispatcher.class);
068:
069: private static final String BLOJSOM_RENDER_TOOL = "BLOJSOM_RENDER_TOOL";
070:
071: private Properties _velocityProperties;
072: private ServletConfig _servletConfig;
073: private Properties _blojsomProperties;
074:
075: private String _templatesDirectory;
076: private String _blogsDirectory;
077:
078: /**
079: * Create a new instance of the Velocity dispatcher
080: */
081: public VelocityDispatcher() {
082: }
083:
084: /**
085: * Initialization method for blojsom dispatchers
086: *
087: * @throws org.blojsom.BlojsomException If there is an error initializing the dispatcher
088: */
089: public void init() throws org.blojsom.BlojsomException {
090: _templatesDirectory = _blojsomProperties.getProperty(
091: BlojsomConstants.TEMPLATES_DIRECTORY_IP,
092: BlojsomConstants.DEFAULT_TEMPLATES_DIRECTORY);
093: _blogsDirectory = _blojsomProperties.getProperty(
094: BlojsomConstants.BLOGS_DIRECTORY_IP,
095: BlojsomConstants.DEFAULT_BLOGS_DIRECTORY);
096: }
097:
098: /**
099: * Set the Velocity properties for use by the dispatcher
100: *
101: * @param velocityProperties Properties for Velocity configuration
102: */
103: public void setVelocityProperties(Properties velocityProperties) {
104: _velocityProperties = velocityProperties;
105: }
106:
107: /**
108: * Set the properties in use by blojsom
109: *
110: * @param blojsomProperties Properties in use by blojsom
111: */
112: public void setBlojsomProperties(Properties blojsomProperties) {
113: _blojsomProperties = blojsomProperties;
114: }
115:
116: /**
117: * Set the {@link ServletConfig}
118: *
119: * @param servletConfig {@link ServletConfig}
120: */
121: public void setServletConfig(ServletConfig servletConfig) {
122: _servletConfig = servletConfig;
123: }
124:
125: /**
126: * Populate the Velocity context with the request and session attributes
127: *
128: * @param httpServletRequest Request
129: * @param context Context
130: */
131: protected void populateVelocityContext(
132: HttpServletRequest httpServletRequest, Map context) {
133: EnumerationIterator iterator = new EnumerationIterator(
134: httpServletRequest.getAttributeNames());
135: while (iterator.hasNext()) {
136: Object key = iterator.next();
137: Object value = httpServletRequest.getAttribute(key
138: .toString());
139: context.put(key, value);
140: }
141:
142: HttpSession httpSession = httpServletRequest.getSession(false);
143: if (httpSession != null) {
144: iterator = new EnumerationIterator(httpSession
145: .getAttributeNames());
146: while (iterator.hasNext()) {
147: Object key = iterator.next();
148: Object value = httpSession.getAttribute(key.toString());
149: context.put(key, value);
150: }
151: }
152: }
153:
154: /**
155: * Remove references from the Velocity context
156: *
157: * @param velocityContext {@link VelocityContext}
158: */
159: protected void destroyVelocityContext(
160: VelocityContext velocityContext) {
161: // Make sure no objects are referenced in the context after they're finished
162: Object[] contextKeys = velocityContext.getKeys();
163: for (int i = 0; i < contextKeys.length; i++) {
164: Object contextKey = contextKeys[i];
165: velocityContext.remove(contextKey);
166: }
167: }
168:
169: /**
170: * Dispatch a request and response. A context map is provided for the BlojsomServlet to pass
171: * any required information for use by the dispatcher. The dispatcher is also
172: * provided with the template for the requested flavor along with the content type for the
173: * specific flavor.
174: *
175: * @param httpServletRequest Request
176: * @param httpServletResponse Response
177: * @param blog {@link Blog}
178: * @param context Context map
179: * @param flavorTemplate Template to dispatch to for the requested flavor
180: * @param flavorContentType Content type for the requested flavor
181: * @throws java.io.IOException If there is an exception during IO
182: * @throws javax.servlet.ServletException If there is an exception in dispatching the request
183: */
184: public void dispatch(HttpServletRequest httpServletRequest,
185: HttpServletResponse httpServletResponse, Blog blog,
186: Map context, String flavorTemplate, String flavorContentType)
187: throws IOException, ServletException {
188: httpServletResponse.setContentType(flavorContentType);
189: ServletContext servletContext = _servletConfig
190: .getServletContext();
191:
192: // Create the Velocity Engine
193: VelocityEngine velocityEngine = new VelocityEngine();
194: try {
195: Properties updatedProperties = (Properties) _velocityProperties
196: .clone();
197: updatedProperties
198: .put(
199: VelocityEngine.FILE_RESOURCE_LOADER_PATH,
200: servletContext
201: .getRealPath(BlojsomConstants.DEFAULT_CONFIGURATION_BASE_DIRECTORY
202: + _blogsDirectory
203: + blog.getBlogId()
204: + _templatesDirectory)
205: + ", "
206: + servletContext
207: .getRealPath(BlojsomConstants.DEFAULT_CONFIGURATION_BASE_DIRECTORY
208: + _templatesDirectory));
209: velocityEngine.init(updatedProperties);
210: } catch (Exception e) {
211: if (_logger.isErrorEnabled()) {
212: _logger.error(e);
213: }
214:
215: return;
216: }
217:
218: Writer responseWriter = httpServletResponse.getWriter();
219: String flavorTemplateForPage = null;
220: String pageParameter = BlojsomUtils.getRequestValue(
221: BlojsomConstants.PAGE_PARAM, httpServletRequest, true);
222:
223: if (pageParameter != null) {
224: flavorTemplateForPage = BlojsomUtils.getTemplateForPage(
225: flavorTemplate, pageParameter);
226:
227: if (_logger.isDebugEnabled()) {
228: _logger.debug("Retrieved template for page: "
229: + flavorTemplateForPage);
230: }
231: }
232:
233: // Setup the VelocityContext
234: populateVelocityContext(httpServletRequest, context);
235: VelocityContext velocityContext = new VelocityContext(context);
236: velocityContext.put(BLOJSOM_RENDER_TOOL, new BlojsomRenderTool(
237: velocityEngine, velocityContext));
238:
239: if (flavorTemplateForPage != null) {
240: // Try and look for the flavor page template for the individual user
241: if (!velocityEngine.templateExists(flavorTemplateForPage)) {
242: if (_logger.isErrorEnabled()) {
243: _logger
244: .error("Could not find flavor page template for user: "
245: + flavorTemplateForPage);
246: }
247:
248: responseWriter.flush();
249: destroyVelocityContext(velocityContext);
250:
251: return;
252: } else {
253: try {
254: velocityEngine.mergeTemplate(flavorTemplateForPage,
255: BlojsomConstants.UTF8, velocityContext,
256: responseWriter);
257: } catch (Exception e) {
258: if (_logger.isErrorEnabled()) {
259: _logger.error(e);
260: }
261:
262: responseWriter.flush();
263: destroyVelocityContext(velocityContext);
264:
265: return;
266: }
267: }
268:
269: _logger.debug("Dispatched to flavor page template: "
270: + flavorTemplateForPage);
271: } else {
272: // Otherwise, fallback and look for the flavor template for the individual user
273: if (!velocityEngine.templateExists(flavorTemplate)) {
274: if (_logger.isErrorEnabled()) {
275: _logger
276: .error("Could not find flavor template for user: "
277: + flavorTemplate);
278: }
279:
280: responseWriter.flush();
281: destroyVelocityContext(velocityContext);
282:
283: return;
284: } else {
285: try {
286: velocityEngine.mergeTemplate(flavorTemplate,
287: BlojsomConstants.UTF8, velocityContext,
288: responseWriter);
289: } catch (Exception e) {
290: if (_logger.isErrorEnabled()) {
291: _logger.error(e);
292: }
293:
294: responseWriter.flush();
295: destroyVelocityContext(velocityContext);
296:
297: return;
298: }
299: }
300:
301: if (_logger.isDebugEnabled()) {
302: _logger.debug("Dispatched to flavor template: "
303: + flavorTemplate);
304: }
305: }
306:
307: responseWriter.flush();
308: destroyVelocityContext(velocityContext);
309: }
310:
311: /**
312: * Blojsom render tool mimics the functionality of the Velocity render tool to parse VTL markup added to a
313: * template
314: */
315: public class BlojsomRenderTool {
316:
317: private static final String LOG_TAG = "BlojsomRenderTool";
318:
319: private VelocityEngine _velocityEngine;
320: private VelocityContext _velocityContext;
321:
322: /**
323: * Create a new instance of the render tool
324: *
325: * @param velocityEngine {@link VelocityEngine}
326: * @param velocityContext {@link VelocityContext}
327: */
328: public BlojsomRenderTool(VelocityEngine velocityEngine,
329: VelocityContext velocityContext) {
330: _velocityEngine = velocityEngine;
331: _velocityContext = velocityContext;
332: }
333:
334: /**
335: * Evaluate a string containing VTL markup
336: *
337: * @param template VTL markup
338: * @return Processed VTL or <code>null</code> if an error in evaluation
339: */
340: public String evaluate(String template) {
341: if (BlojsomUtils.checkNullOrBlank(template)) {
342: return null;
343: }
344:
345: StringWriter sw = new StringWriter();
346: boolean success = false;
347:
348: try {
349: if (_velocityEngine == null) {
350: success = Velocity.evaluate(_velocityContext, sw,
351: LOG_TAG, template);
352: } else {
353: success = _velocityEngine.evaluate(
354: _velocityContext, sw, LOG_TAG, template);
355: }
356: } catch (ParseErrorException e) {
357: if (_logger.isErrorEnabled()) {
358: _logger.error(e);
359: }
360: } catch (MethodInvocationException e) {
361: if (_logger.isErrorEnabled()) {
362: _logger.error(e);
363: }
364: } catch (ResourceNotFoundException e) {
365: if (_logger.isErrorEnabled()) {
366: _logger.error(e);
367: }
368: } catch (IOException e) {
369: if (_logger.isErrorEnabled()) {
370: _logger.error(e);
371: }
372: }
373:
374: if (success) {
375: return sw.toString();
376: }
377:
378: return null;
379: }
380: }
381: }
|