View Javadoc
1 package org.apache.turbine.services.velocity; 2 3 /* ==================================================================== 4 * The Apache Software License, Version 1.1 5 * 6 * Copyright (c) 2001 The Apache Software Foundation. All rights 7 * reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. The end-user documentation included with the redistribution, 22 * if any, must include the following acknowledgment: 23 * "This product includes software developed by the 24 * Apache Software Foundation (http://www.apache.org/)." 25 * Alternately, this acknowledgment may appear in the software itself, 26 * if and wherever such third-party acknowledgments normally appear. 27 * 28 * 4. The names "Apache" and "Apache Software Foundation" and 29 * "Apache Turbine" must not be used to endorse or promote products 30 * derived from this software without prior written permission. For 31 * written permission, please contact apache@apache.org. 32 * 33 * 5. Products derived from this software may not be called "Apache", 34 * "Apache Turbine", nor may "Apache" appear in their name, without 35 * prior written permission of the Apache Software Foundation. 36 * 37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 48 * SUCH DAMAGE. 49 * ==================================================================== 50 * 51 * This software consists of voluntary contributions made by many 52 * individuals on behalf of the Apache Software Foundation. For more 53 * information on the Apache Software Foundation, please see 54 * <http://www.apache.org/>;. 55 */ 56 57 import java.io.ByteArrayOutputStream; 58 import java.io.IOException; 59 import java.io.OutputStream; 60 import java.io.OutputStreamWriter; 61 import java.io.Writer; 62 import java.util.Iterator; 63 import java.util.Vector; 64 import javax.servlet.ServletConfig; 65 import org.apache.commons.configuration.Configuration; 66 import org.apache.commons.configuration.ConfigurationConverter; 67 import org.apache.turbine.Turbine; 68 import org.apache.turbine.services.InitializationException; 69 import org.apache.turbine.services.pull.TurbinePull; 70 import org.apache.turbine.services.servlet.TurbineServlet; 71 import org.apache.turbine.services.template.BaseTemplateEngineService; 72 import org.apache.turbine.util.Log; 73 import org.apache.turbine.util.RunData; 74 import org.apache.turbine.util.StringUtils; 75 import org.apache.turbine.util.TurbineConfig; 76 import org.apache.turbine.util.TurbineException; 77 import org.apache.velocity.VelocityContext; 78 import org.apache.velocity.app.Velocity; 79 import org.apache.velocity.context.Context; 80 81 82 /*** 83 * This is a Service that can process Velocity templates from within a 84 * Turbine Screen. Here's an example of how you might use it from a 85 * screen:<br> 86 * 87 * <code> 88 * Context context = TurbineVelocity.getContext(data);<br> 89 * context.put("message", "Hello from Turbine!");<br> 90 * String results = TurbineVelocity.handleRequest(context,"helloWorld.vm");<br> 91 * data.getPage().getBody().addElement(results);<br> 92 * </code> 93 * 94 * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a> 95 * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a> 96 * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a> 97 * @author <a href="mailto:sean@informage.ent">Sean Legassick</a> 98 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 99 * @version $Id: TurbineVelocityService.java,v 1.8 2002/07/11 16:53:23 mpoeschl Exp $ 100 */ 101 public class TurbineVelocityService extends BaseTemplateEngineService 102 implements VelocityService 103 { 104 /*** 105 * The generic resource loader path property in velocity. 106 */ 107 private static final String RESOURCE_LOADER_PATH = 108 ".resource.loader.path"; 109 110 /*** 111 * Default character set to use if not specified in the RunData object. 112 */ 113 private static final String DEFAULT_CHAR_SET = "ISO-8859-1"; 114 115 /*** 116 * The prefix used for URIs which are of type <code>jar</code>. 117 */ 118 private static final String JAR_PREFIX = "jar:"; 119 120 /*** 121 * The prefix used for URIs which are of type <code>absolute</code>. 122 */ 123 private static final String ABSOLUTE_PREFIX = "file://"; 124 125 /*** 126 * The context used to the store the context 127 * containing the global application tools. 128 */ 129 private Context globalContext = null; 130 131 /*** 132 * Is the pullModelActive. 133 */ 134 private boolean pullModelActive = false; 135 136 /*** 137 * Should we refresh the tools on a per 138 * request basis. This is used for development. 139 */ 140 private boolean refreshToolsPerRequest = false; 141 142 /*** 143 * Performs early initialization of this Turbine service. 144 */ 145 public void init(ServletConfig config) throws InitializationException 146 { 147 try 148 { 149 initVelocity(); 150 151 globalContext = null; 152 153 /* 154 * We can only load the Pull Model ToolBox 155 * if the Pull service has been listed in the TR.props 156 * and the service has successfully been initialized. 157 */ 158 if (TurbinePull.isRegistered()) 159 { 160 globalContext = TurbinePull.getGlobalContext(); 161 162 pullModelActive = true; 163 164 refreshToolsPerRequest = TurbinePull.refreshToolsPerRequest(); 165 } 166 167 /* 168 * If the Pull service is inactive then we create 169 * an empty toolBoxContext 170 */ 171 if (globalContext == null) 172 { 173 globalContext = new VelocityContext(); 174 } 175 176 /* 177 * Register with the template service. 178 */ 179 registerConfiguration("vm"); 180 setInit(true); 181 } 182 catch (Exception e) 183 { 184 throw new InitializationException( 185 "Failed to initialize TurbineVelocityService", e); 186 } 187 } 188 189 /*** 190 * Create a Context object that also contains the globalContext. 191 * 192 * @return A Context object. 193 */ 194 public Context getContext() 195 { 196 return new VelocityContext(globalContext); 197 } 198 199 /*** 200 * Create a Context from the RunData object. Adds a pointer to 201 * the RunData object to the WC so that RunData is available in 202 * the templates. 203 * 204 * @param data The Turbine RunData object. 205 * @return A clone of the WebContext needed by Velocity. 206 */ 207 public Context getContext(RunData data) 208 { 209 /* 210 * Attempt to get it from the data first. If it doesn't 211 * exist, create it and then stuff it into the data. 212 */ 213 Context context = (Context) 214 data.getTemplateInfo().getTemplateContext(VelocityService.CONTEXT); 215 216 if (context == null) 217 { 218 context = getContext(); 219 context.put ( "data", data ); 220 221 if (pullModelActive) 222 { 223 /* 224 * Populate the toolbox with request scope, session scope 225 * and persistent scope tools (global tools are already in 226 * the toolBoxContent which has been wrapped to construct 227 * this request-specific context). 228 */ 229 TurbinePull.populateContext(context, data); 230 } 231 232 data.getTemplateInfo().setTemplateContext( 233 VelocityService.CONTEXT, context); 234 } 235 return context; 236 } 237 238 /*** 239 * Process the request and fill in the template with the values 240 * you set in the Context. 241 * 242 * @param context The populated context. 243 * @param filename The file name of the template. 244 * @return The process template as a String. 245 * 246 * @throws TurbineException Any exception trown while processing will be 247 * wrapped into a TurbineException and rethrown. 248 */ 249 public String handleRequest(Context context, String filename) 250 throws TurbineException 251 { 252 String results = null; 253 ByteArrayOutputStream bytes = null; 254 255 try 256 { 257 bytes = new ByteArrayOutputStream(); 258 String charset = decodeRequest(context, filename, bytes); 259 results = bytes.toString(charset); 260 } 261 catch(Exception e) 262 { 263 renderingError(filename, e); 264 } 265 finally 266 { 267 try 268 { 269 if (bytes != null) bytes.close(); 270 } 271 catch(IOException ignored) 272 { 273 } 274 } 275 return results; 276 } 277 278 /*** 279 * Process the request and fill in the template with the values 280 * you set in the Context. 281 * 282 * @param context A Context. 283 * @param filename A String with the filename of the template. 284 * @param out A OutputStream where we will write the process template as 285 * a String. 286 * 287 * @throws TurbineException Any exception trown while processing will be 288 * wrapped into a TurbineException and rethrown. 289 */ 290 public void handleRequest(Context context, 291 String filename, 292 OutputStream output) 293 throws TurbineException 294 { 295 decodeRequest(context, filename, output); 296 } 297 298 /*** 299 * Process the request and fill in the template with the values 300 * you set in the Context. 301 * 302 * @param context A Context. 303 * @param filename A String with the filename of the template. 304 * @param writer A Writer where we will write the process template as 305 * a String. 306 * 307 * @throws TurbineException Any exception trown while processing will be 308 * wrapped into a TurbineException and rethrown. 309 */ 310 public void handleRequest(Context context, 311 String filename, 312 Writer writer) 313 throws TurbineException 314 { 315 String encoding = getEncoding(context); 316 decodeRequest(context, filename, encoding, writer); 317 } 318 319 320 /*** 321 * Process the request and fill in the template with the values 322 * you set in the Context. Apply the character and template 323 * encodings from RunData to the result. 324 * 325 * @param context A Context. 326 * @param filename A String with the filename of the template. 327 * @param out A OutputStream where we will write the process template as 328 * a String. 329 * @return The character encoding applied to the resulting String. 330 * 331 * @throws TurbineException Any exception trown while processing will be 332 * wrapped into a TurbineException and rethrown. 333 */ 334 private String decodeRequest(Context context, 335 String filename, 336 OutputStream output) 337 throws TurbineException 338 { 339 /* 340 * This is for development. 341 */ 342 if (pullModelActive && refreshToolsPerRequest) 343 { 344 TurbinePull.refreshGlobalTools(); 345 } 346 347 /* 348 * Get the character and template encodings from the RunData object. 349 */ 350 String charset = getCharSet(context); 351 String encoding = getEncoding(context); 352 353 OutputStreamWriter writer = null; 354 355 try 356 { 357 writer = new OutputStreamWriter(output, charset); 358 if (encoding != null) 359 { 360 Velocity.mergeTemplate(filename, encoding, context, writer); 361 } 362 else 363 { 364 Velocity.mergeTemplate(filename, context, writer); 365 } 366 } 367 catch(Exception e) 368 { 369 renderingError(filename, e); 370 } 371 finally 372 { 373 try 374 { 375 if (writer != null) 376 { 377 writer.flush(); 378 } 379 } 380 catch (Exception ignored) 381 { 382 /* 383 * do nothing. 384 */ 385 } 386 } 387 388 return charset; 389 } 390 391 /*** 392 * Retrieve the required charset from the Turbine RunData in the context 393 * 394 * @param context A Context. 395 * @return The character set applied to the resulting String. 396 */ 397 private String getCharSet(Context context) 398 { 399 String charset; 400 Object data = context.get("data"); 401 if ((data != null) && (data instanceof RunData)) 402 { 403 charset = ((RunData) data).getCharSet(); 404 if (charset == null) 405 { 406 charset = DEFAULT_CHAR_SET; 407 } 408 } 409 else 410 { 411 charset = DEFAULT_CHAR_SET; 412 } 413 414 return charset; 415 } 416 417 /*** 418 * Retrieve the required encoding from the Turbine RunData in the context 419 * 420 * @param context A Context. 421 * @return The encoding applied to the resulting String. 422 */ 423 private String getEncoding(Context context) 424 { 425 String encoding; 426 Object data = context.get("data"); 427 if ((data != null) && (data instanceof RunData)) 428 { 429 encoding = ((RunData) data).getTemplateEncoding(); 430 } 431 else 432 { 433 encoding = null; 434 } 435 return encoding; 436 } 437 438 /*** 439 * Process the request and fill in the template with the values 440 * you set in the Context. 441 * 442 * @param context A Context. 443 * @param filename A String with the filename of the template. 444 * @param encoding The encoding to use with the template 445 * @param writer A Writer where we will write the process template as 446 * a String. This writer charset should be compatible with the selected 447 * encoding 448 * 449 * @throws TurbineException Any exception trown while processing will be 450 * wrapped into a TurbineException and rethrown. 451 */ 452 private void decodeRequest(Context context, 453 String filename, 454 String encoding, 455 Writer writer) 456 throws TurbineException 457 { 458 /* 459 * This is for development. 460 */ 461 if (pullModelActive && refreshToolsPerRequest) 462 { 463 TurbinePull.refreshGlobalTools(); 464 } 465 466 try 467 { 468 if (encoding != null) 469 { 470 Velocity.mergeTemplate(filename, encoding, context, writer); 471 } 472 else 473 { 474 Velocity.mergeTemplate(filename, context, writer); 475 } 476 } 477 catch(Exception e) 478 { 479 renderingError(filename, e); 480 } 481 finally 482 { 483 try 484 { 485 if (writer != null) 486 { 487 writer.flush(); 488 } 489 } 490 catch (Exception ignored) 491 { 492 /* 493 * do nothing. 494 */ 495 } 496 } 497 } 498 499 /*** 500 * Macro to handle rendering errors. 501 * 502 * @param filename The file name of the unrenderable template. 503 * @param e The error. 504 * 505 * @exception TurbineException Thrown every time. Adds additional 506 * information to <code>e</code>. 507 */ 508 private static final void renderingError(String filename, Exception e) 509 throws TurbineException 510 { 511 String err = "Error rendering Velocity template: " + filename; 512 Log.error(err + ": " + e.getMessage()); 513 throw new TurbineException(err, e); 514 } 515 516 /*** 517 * Setup the velocity runtime by using a subset of the 518 * Turbine configuration which relates to velocity. 519 * 520 * @exception InitializationException For any errors during initialization. 521 */ 522 private void initVelocity() throws InitializationException 523 { 524 /* 525 * Get the configuration for this service. 526 */ 527 Configuration configuration = getConfiguration(); 528 529 /* 530 * Now we have to perform a couple of path translations 531 * for our log file and template paths. 532 */ 533 String path = Turbine.getRealPath 534 (configuration.getString(Velocity.RUNTIME_LOG, null)); 535 536 if (StringUtils.isValid(path)) 537 { 538 configuration.setProperty(Velocity.RUNTIME_LOG, path); 539 } 540 else 541 { 542 String msg = VelocityService.SERVICE_NAME + " runtime log file " + 543 "is misconfigured: '" + path + "' is not a valid log file"; 544 545 if (TurbineServlet.getServletConfig() instanceof TurbineConfig) 546 { 547 msg += ": TurbineConfig users must use a path relative to " + 548 "web application root"; 549 } 550 throw new Error(msg); 551 } 552 553 /* 554 * Get all the template paths where the velocity runtime should search 555 * for templates and collect them into a separate vector to avoid 556 * concurrent modification exceptions. 557 */ 558 String key; 559 Vector keys = new Vector(); 560 for (Iterator i = configuration.getKeys(); i.hasNext();) 561 { 562 key = (String) i.next(); 563 if (key.endsWith(RESOURCE_LOADER_PATH)) 564 { 565 keys.add(key); 566 } 567 } 568 569 /* 570 * Loop through all template paths, clear the corresponding 571 * velocity properties and translate them all to the webapp space. 572 */ 573 574 int ind; 575 Vector paths; 576 String entry; 577 for (Iterator i = keys.iterator(); i.hasNext();) 578 { 579 key = (String) i.next(); 580 paths = configuration.getVector(key,null); 581 if (paths != null) 582 { 583 Velocity.clearProperty(key); 584 configuration.clearProperty(key); 585 586 for (Iterator j = paths.iterator(); j.hasNext();) 587 { 588 path = (String) j.next(); 589 if (path.startsWith(JAR_PREFIX + "file")) 590 { 591 /* 592 * A local jar resource URL path is a bit more 593 * complicated, but we can translate it as well. 594 */ 595 ind = path.indexOf("!/"); 596 if (ind >= 0) 597 { 598 entry = path.substring(ind); 599 path = path.substring(9,ind); 600 } 601 else 602 { 603 entry = "!/"; 604 path = path.substring(9); 605 } 606 path = JAR_PREFIX + "file:" + 607 Turbine.getRealPath(path) + entry; 608 } 609 else if (path.startsWith(ABSOLUTE_PREFIX)) 610 { 611 path = path.substring (ABSOLUTE_PREFIX.length(), 612 path.length()); 613 } 614 else if (!path.startsWith(JAR_PREFIX)) 615 { 616 // But we don't translate remote jar URLs. 617 path = Turbine.getRealPath(path); 618 } 619 // Put the translated paths back to the configuration. 620 configuration.addProperty(key,path); 621 } 622 } 623 } 624 try 625 { 626 Velocity.setExtendedProperties(ConfigurationConverter 627 .getExtendedProperties(configuration)); 628 Velocity.init(); 629 } 630 catch(Exception e) 631 { 632 /* 633 * This will be caught and rethrown by the init() method. 634 * Oh well, that will protect us from RuntimeException folk showing 635 * up somewhere above this try/catch 636 */ 637 throw new InitializationException( 638 "Failed to set up TurbineVelocityService", e); 639 } 640 } 641 642 /*** 643 * Find out if a given template exists. Velocity 644 * will do its own searching to determine whether 645 * a template exists or not. 646 * 647 * @param String template to search for 648 * @return boolean 649 */ 650 public boolean templateExists(String template) 651 { 652 return Velocity.templateExists(template); 653 } 654 655 /*** 656 * Performs post-request actions (releases context 657 * tools back to the object pool). 658 * 659 * @param context a Velocity Context 660 */ 661 public void requestFinished(Context context) 662 { 663 if (pullModelActive) 664 { 665 TurbinePull.releaseTools(context); 666 } 667 } 668 }

This page was automatically generated by Maven