| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| WMServlet |
|
| 2.064516129032258;2.065 |
| 1 | /* | |
| 2 | * Copyright (C) 1998-2000 Semiotek Inc. All Rights Reserved. | |
| 3 | * | |
| 4 | * Redistribution and use in source and binary forms, with or without | |
| 5 | * modification, are permitted under the terms of either of the following | |
| 6 | * Open Source licenses: | |
| 7 | * | |
| 8 | * The GNU General Public License, version 2, or any later version, as | |
| 9 | * published by the Free Software Foundation | |
| 10 | * (http://www.fsf.org/copyleft/gpl.html); | |
| 11 | * | |
| 12 | * or | |
| 13 | * | |
| 14 | * The Semiotek Public License (http://webmacro.org/LICENSE.) | |
| 15 | * | |
| 16 | * This software is provided "as is", with NO WARRANTY, not even the | |
| 17 | * implied warranties of fitness to purpose, or merchantability. You | |
| 18 | * assume all risks and liabilities associated with its use. | |
| 19 | * | |
| 20 | * See www.webmacro.org for more information on the WebMacro project. | |
| 21 | */ | |
| 22 | ||
| 23 | ||
| 24 | package org.webmacro.servlet; | |
| 25 | ||
| 26 | import java.io.IOException; | |
| 27 | import java.io.OutputStream; | |
| 28 | import java.io.UnsupportedEncodingException; | |
| 29 | import java.io.Writer; | |
| 30 | import java.lang.reflect.Method; | |
| 31 | import java.util.Locale; | |
| 32 | ||
| 33 | import javax.servlet.ServletConfig; | |
| 34 | import javax.servlet.ServletException; | |
| 35 | import javax.servlet.http.HttpServlet; | |
| 36 | import javax.servlet.http.HttpServletRequest; | |
| 37 | import javax.servlet.http.HttpServletResponse; | |
| 38 | ||
| 39 | import org.slf4j.Logger; | |
| 40 | import org.slf4j.LoggerFactory; | |
| 41 | ||
| 42 | import org.webmacro.Broker; | |
| 43 | import org.webmacro.Context; | |
| 44 | import org.webmacro.FastWriter; | |
| 45 | import org.webmacro.InitException; | |
| 46 | import org.webmacro.NotFoundException; | |
| 47 | import org.webmacro.PropertyException; | |
| 48 | import org.webmacro.ResourceException; | |
| 49 | import org.webmacro.Template; | |
| 50 | import org.webmacro.WM; | |
| 51 | import org.webmacro.WMConstants; | |
| 52 | import org.webmacro.WebMacro; | |
| 53 | ||
| 54 | /** | |
| 55 | * This is an abstract base class which can be used | |
| 56 | * to implement a kind of WebMacro servlet. | |
| 57 | * | |
| 58 | * You can either subclass from it directly, or make use of one of the | |
| 59 | * generic subclasses provided. | |
| 60 | * <p> | |
| 61 | * It's primary function is to create a WebContext and manage a | |
| 62 | * Broker. It also provides a couple of convenience functions | |
| 63 | * that access the Broker and/or WebContext to make some commonly | |
| 64 | * accessed services more readily available. | |
| 65 | * <p> | |
| 66 | * @see org.webmacro.Broker | |
| 67 | */ | |
| 68 | 0 | abstract public class WMServlet extends HttpServlet implements WebMacro |
| 69 | { | |
| 70 | ||
| 71 | private static final long serialVersionUID = -4903102471116106113L; | |
| 72 | 0 | static Logger _log = LoggerFactory.getLogger(WMServlet.class); |
| 73 | ||
| 74 | 0 | private WebMacro _wm = null; |
| 75 | 0 | private Broker _broker = null; |
| 76 | 0 | private boolean _started = false; |
| 77 | /** | |
| 78 | * The name of the config entry we look for to find out what to | |
| 79 | * call the variable used in the ERROR_TEMPLATE. | |
| 80 | */ | |
| 81 | final static String ERROR_VARIABLE = "ErrorVariable"; | |
| 82 | ||
| 83 | /** | |
| 84 | * The name of the error template we will use if something | |
| 85 | * goes wrong. | |
| 86 | */ | |
| 87 | final static String ERROR_TEMPLATE = "ErrorTemplate"; | |
| 88 | ||
| 89 | /** | |
| 90 | * Defaults for error variable and error template. | |
| 91 | */ | |
| 92 | final static String ERROR_TEMPLATE_DEFAULT = "error.wm"; | |
| 93 | final static String ERROR_VARIABLE_DEFAULT = "error"; | |
| 94 | ||
| 95 | /** | |
| 96 | * Null means all OK. | |
| 97 | */ | |
| 98 | 0 | private String _problem = "Not yet initialized: " + |
| 99 | "Your servlet API tried to access WebMacro without first calling init()!!!"; | |
| 100 | ||
| 101 | /** | |
| 102 | * This is the old-style init method, it just calls init(), after | |
| 103 | * handing the ServletConfig object to the superclass. | |
| 104 | * @exception ServletException if it failed to initialize | |
| 105 | */ | |
| 106 | public synchronized void init (ServletConfig sc) | |
| 107 | throws ServletException | |
| 108 | { | |
| 109 | 0 | super.init(sc); |
| 110 | 0 | init(); |
| 111 | 0 | } |
| 112 | ||
| 113 | /** | |
| 114 | * This method is called by the servlet runner--do not call it. It | |
| 115 | * must not be overidden because it manages a shared instance | |
| 116 | * of the broker--you can overide the start() method instead, which | |
| 117 | * is called just after the broker is initialized. | |
| 118 | */ | |
| 119 | public synchronized void init () | |
| 120 | { | |
| 121 | ||
| 122 | 0 | if (_started) |
| 123 | { | |
| 124 | 0 | return; |
| 125 | } | |
| 126 | ||
| 127 | // locate a Broker | |
| 128 | ||
| 129 | 0 | if (_wm == null) |
| 130 | { | |
| 131 | try | |
| 132 | { | |
| 133 | 0 | _wm = initWebMacro(); |
| 134 | 0 | _broker = _wm.getBroker(); |
| 135 | } | |
| 136 | 0 | catch (InitException e) |
| 137 | { | |
| 138 | 0 | _problem = "Could not initialize the broker!\n\n" |
| 139 | + "*** Check that WebMacro.properties was in your servlet\n" | |
| 140 | + "*** classpath, in a similar place to webmacro.jar \n" | |
| 141 | + "*** and that all values were set correctly.\n\n" | |
| 142 | + e.getMessage(); | |
| 143 | 0 | _log.error(_problem, e); |
| 144 | 0 | return; |
| 145 | 0 | } |
| 146 | } | |
| 147 | ||
| 148 | try | |
| 149 | { | |
| 150 | 0 | if (_log.isDebugEnabled()) |
| 151 | { | |
| 152 | 0 | java.net.URL url = getBroker().getResource(Broker.WEBMACRO_PROPERTIES); |
| 153 | 0 | if (url != null) |
| 154 | 0 | _log.debug("Using properties from " + url.toExternalForm()); |
| 155 | else | |
| 156 | 0 | _log.debug("No WebMacro.properties file was found."); |
| 157 | } | |
| 158 | 0 | start(); |
| 159 | 0 | _problem = null; |
| 160 | } | |
| 161 | 0 | catch (ServletException e) |
| 162 | { | |
| 163 | 0 | _problem = "WebMacro application code failed to initialize: \n" |
| 164 | + e + "\n" + "This error is the result of a failure in the\n" | |
| 165 | + "code supplied by the application programmer.\n"; | |
| 166 | 0 | _log.error(_problem, e); |
| 167 | 0 | } |
| 168 | 0 | _log.info("started: " + this); |
| 169 | 0 | _started = true; |
| 170 | ||
| 171 | 0 | } |
| 172 | ||
| 173 | /** | |
| 174 | * This method is called by the servlet runner--do not call it. It | |
| 175 | * must not be overidden because it manages a shared instance of | |
| 176 | * the broker--you can overide the stop() method instead, which | |
| 177 | * will be called just before the broker is shut down. Once the | |
| 178 | * stop() method has been called, the nested WM will be destroyed | |
| 179 | * and then the parent destroy() method will be invoked. | |
| 180 | * @see WM#destroy() | |
| 181 | */ | |
| 182 | public synchronized void destroy () | |
| 183 | { | |
| 184 | 0 | stop(); |
| 185 | 0 | _wm.destroy(); |
| 186 | 0 | _log.info("stopped: " + this); |
| 187 | 0 | _wm = null; |
| 188 | 0 | _started = false; |
| 189 | 0 | super.destroy(); |
| 190 | 0 | } |
| 191 | ||
| 192 | ||
| 193 | // SERVLET API METHODS | |
| 194 | ||
| 195 | /** | |
| 196 | * Process an incoming GET request: Builds a WebContext up and then | |
| 197 | * passes it to the handle() method. You can overide this if you want, | |
| 198 | * though for most purposes you are expected to overide handle() | |
| 199 | * instead. | |
| 200 | * <p> | |
| 201 | * @param req the request we got | |
| 202 | * @param resp the response we are generating | |
| 203 | * @exception ServletException if we can't get our configuration | |
| 204 | * @exception IOException if we can't write to the output stream | |
| 205 | */ | |
| 206 | protected void doGet (HttpServletRequest req, HttpServletResponse resp) | |
| 207 | throws ServletException, IOException | |
| 208 | { | |
| 209 | 0 | doRequest(req, resp); |
| 210 | 0 | } |
| 211 | ||
| 212 | /** | |
| 213 | * Behaves exactly like doGet() except that it reads data from POST | |
| 214 | * before doing exactly the same thing. This means that you can use | |
| 215 | * GET and POST interchangeably with WebMacro. You can overide this if | |
| 216 | * you want, though for most purposes you are expected to overide | |
| 217 | * handle() instead. | |
| 218 | * <p> | |
| 219 | * @param req the request we got | |
| 220 | * @param resp the response we are generating | |
| 221 | * @exception ServletException if we can't get our configuration | |
| 222 | * @exception IOException if we can't read/write to the streams we got | |
| 223 | */ | |
| 224 | protected void doPost (HttpServletRequest req, HttpServletResponse resp) | |
| 225 | throws ServletException, IOException | |
| 226 | { | |
| 227 | 0 | doRequest(req, resp); |
| 228 | 0 | } |
| 229 | ||
| 230 | final private void doRequest ( | |
| 231 | HttpServletRequest req, HttpServletResponse resp) | |
| 232 | throws IOException | |
| 233 | { | |
| 234 | ||
| 235 | 0 | WebContext context = null; |
| 236 | ||
| 237 | 0 | if (_problem != null) |
| 238 | { | |
| 239 | 0 | init(); |
| 240 | 0 | if (_problem != null) |
| 241 | { | |
| 242 | try | |
| 243 | { | |
| 244 | 0 | resp.setContentType("text/html"); |
| 245 | 0 | Writer out = resp.getWriter(); |
| 246 | ||
| 247 | 0 | out.write("<html><head><title>WebMacro Error</title></head>"); |
| 248 | 0 | out.write("<body><h1><font color=\"red\">WebMacro Error: "); |
| 249 | 0 | out.write("</font></h1><pre>"); |
| 250 | 0 | out.write(_problem); |
| 251 | 0 | out.write("</pre>"); |
| 252 | 0 | out.write("Please contact the server administrator"); |
| 253 | 0 | out.flush(); |
| 254 | 0 | out.close(); |
| 255 | } | |
| 256 | 0 | catch (Exception e) |
| 257 | { | |
| 258 | 0 | _log.error(_problem, e); |
| 259 | 0 | } |
| 260 | 0 | return; |
| 261 | } | |
| 262 | } | |
| 263 | ||
| 264 | ||
| 265 | 0 | context = newWebContext(req, resp); |
| 266 | try | |
| 267 | { | |
| 268 | Template t; | |
| 269 | 0 | t = handle(context); |
| 270 | ||
| 271 | 0 | if (t != null) |
| 272 | { | |
| 273 | 0 | execute(t, context); |
| 274 | } | |
| 275 | 0 | destroyContext(context); |
| 276 | } | |
| 277 | 0 | catch (HandlerException e) |
| 278 | { | |
| 279 | 0 | _log.error("Your handler failed to handle the request:" + this, e); |
| 280 | 0 | Template tmpl = error(context, |
| 281 | "Your handler was unable to process the request successfully " + | |
| 282 | "for some reason. Here are the details:<p>" + | |
| 283 | "<pre>" + e + "</pre>"); | |
| 284 | 0 | execute(tmpl, context); |
| 285 | } | |
| 286 | 0 | catch (Exception e) |
| 287 | { | |
| 288 | 0 | _log.error("Your handler failed to handle the request:" + this, e); |
| 289 | 0 | Template tmpl = error(context, |
| 290 | "The handler WebMacro used to handle this request failed for " + | |
| 291 | "some reason. This is likely a bug in the handler written " + | |
| 292 | "for this application. Here are the details:<p>" + | |
| 293 | "<pre>" + e + "</pre>"); | |
| 294 | 0 | execute(tmpl, context); |
| 295 | 0 | } |
| 296 | 0 | } |
| 297 | ||
| 298 | ||
| 299 | // CONVENIENCE METHODS & ACCESS TO THE BROKER | |
| 300 | ||
| 301 | /** | |
| 302 | * Create an error template using the built in error handler. | |
| 303 | * This is useful for returning error messages on failure; | |
| 304 | * it is used by WMServlet to display errors resulting from | |
| 305 | * any exception that you may throw from the handle() method. | |
| 306 | * @param context will add error variable to context (see Config) | |
| 307 | * @param error a string explaining what went wrong | |
| 308 | */ | |
| 309 | protected Template error (WebContext context, String error) | |
| 310 | { | |
| 311 | 0 | Template tmpl = null; |
| 312 | //Handler hand = new ErrorHandler(); | |
| 313 | try | |
| 314 | { | |
| 315 | 0 | context.put(getErrorVariableName(), |
| 316 | error); | |
| 317 | //tmpl = hand.accept(context); | |
| 318 | 0 | tmpl = getErrorTemplate(); |
| 319 | } | |
| 320 | 0 | catch (Exception e2) |
| 321 | { | |
| 322 | 0 | _log.error("Unable to use ErrorHandler", e2); |
| 323 | 0 | } |
| 324 | 0 | return tmpl; |
| 325 | } | |
| 326 | ||
| 327 | /** | |
| 328 | * Returns the name of the error variable, as per the config. | |
| 329 | * @return Name to use for the error variable in templates | |
| 330 | */ | |
| 331 | protected String getErrorVariableName () | |
| 332 | { | |
| 333 | 0 | return getConfig(ERROR_VARIABLE, ERROR_VARIABLE_DEFAULT); |
| 334 | } | |
| 335 | ||
| 336 | /** | |
| 337 | * This object is used to access components that have been plugged | |
| 338 | * into WebMacro; it is shared between all instances of this class and | |
| 339 | * its subclasses. It is created when the first instance is initialized, | |
| 340 | * and deleted when the last instance is shut down. If you attempt to | |
| 341 | * access it after the last servlet has been shutdown, it will either | |
| 342 | * be in a shutdown state or else null. | |
| 343 | */ | |
| 344 | public Broker getBroker () | |
| 345 | { | |
| 346 | // this method can be unsynch. because the broker manages its own | |
| 347 | // state, plus the only time the _broker will be shutdown or null | |
| 348 | // is after the last servlet has shutdown--so why would anyone be | |
| 349 | // accessing us then? if they do the _broker will throw exceptions | |
| 350 | // complaining that it has been shut down, or they'll get a null here. | |
| 351 | 0 | return _broker; |
| 352 | } | |
| 353 | ||
| 354 | /** | |
| 355 | * Retrieve a template from the "template" provider. Equivalent to | |
| 356 | * getBroker().get(TemplateProvider.TYPE,key) | |
| 357 | * @exception NotFoundException if the template was not found | |
| 358 | * @exception ResourceException if the template coult not be loaded | |
| 359 | */ | |
| 360 | public Template getTemplate (String key) | |
| 361 | throws ResourceException | |
| 362 | { | |
| 363 | 0 | return _wm.getTemplate(key); |
| 364 | } | |
| 365 | ||
| 366 | /** | |
| 367 | * Retrieve a URL. This is largely equivalent to creating a URL | |
| 368 | * object and requesting its content, though it will sit in | |
| 369 | * WebMacro's cache rather than re-requesting each time. | |
| 370 | * The content will be returned as an Object. | |
| 371 | */ | |
| 372 | public String getURL (String url) | |
| 373 | throws ResourceException | |
| 374 | { | |
| 375 | 0 | return _wm.getURL(url); |
| 376 | } | |
| 377 | ||
| 378 | ||
| 379 | /** | |
| 380 | * Retrieve configuration information from the "config" provider. | |
| 381 | * Equivalent to getBroker().get(Config.TYPE,key) | |
| 382 | * @exception NotFoundException could not locate requested information | |
| 383 | */ | |
| 384 | public String getConfig (String key) | |
| 385 | throws NotFoundException | |
| 386 | { | |
| 387 | 0 | return _wm.getConfig(key); |
| 388 | } | |
| 389 | ||
| 390 | /** | |
| 391 | * Retrieve configuration information from the "config" provider. | |
| 392 | * Return specified default if key could not be found | |
| 393 | */ | |
| 394 | public String getConfig (String key, String defaultValue) | |
| 395 | { | |
| 396 | try | |
| 397 | { | |
| 398 | 0 | return _wm.getConfig(key); |
| 399 | } | |
| 400 | 0 | catch (NotFoundException e) |
| 401 | { | |
| 402 | 0 | return defaultValue; |
| 403 | } | |
| 404 | } | |
| 405 | ||
| 406 | /** | |
| 407 | * Create a new Context object. | |
| 408 | */ | |
| 409 | public Context getContext () | |
| 410 | { | |
| 411 | 0 | return _wm.getContext(); |
| 412 | } | |
| 413 | ||
| 414 | /** | |
| 415 | * Create a new WebContext object; can be overridden. | |
| 416 | */ | |
| 417 | public WebContext getWebContext (HttpServletRequest req, HttpServletResponse res) | |
| 418 | { | |
| 419 | 0 | return _wm.getWebContext(req, res); |
| 420 | } | |
| 421 | ||
| 422 | /** | |
| 423 | * Convenience method for writing a template to an OutputStream. | |
| 424 | * This method takes care of all the typical work involved | |
| 425 | * in writing a template.<p> | |
| 426 | * | |
| 427 | * This method uses the default <code>TemplateOutputEncoding</code> specified in | |
| 428 | * WebMacro.defaults or your custom WebMacro.properties. | |
| 429 | * | |
| 430 | * @param templateName name of Template to write. Must be accessible | |
| 431 | * via TemplatePath | |
| 432 | * @param out where the output of the template should go | |
| 433 | * @param context The Context (can be a WebContext too) used | |
| 434 | * during the template evaluation phase | |
| 435 | * @throws java.io.IOException if the template cannot be written to the | |
| 436 | * specified output stream | |
| 437 | * @throws ResourceException if the template name specified cannot be found | |
| 438 | * @throws PropertyException if a fatal error occured during the Template | |
| 439 | * evaluation phase | |
| 440 | */ | |
| 441 | public void writeTemplate (String templateName, java.io.OutputStream out, | |
| 442 | Context context) | |
| 443 | throws java.io.IOException, ResourceException, PropertyException | |
| 444 | { | |
| 445 | ||
| 446 | 0 | writeTemplate(templateName, out, |
| 447 | getConfig(WMConstants.TEMPLATE_OUTPUT_ENCODING), | |
| 448 | context); | |
| 449 | 0 | } |
| 450 | ||
| 451 | /** | |
| 452 | * Convienence method for writing a template to an OutputStream. | |
| 453 | * This method takes care of all the typical work involved | |
| 454 | * in writing a template. | |
| 455 | * | |
| 456 | * @param templateName name of Template to write. Must be accessible | |
| 457 | * via TemplatePath | |
| 458 | * @param out where the output of the template should go | |
| 459 | * @param encoding character encoding to use when writing the template | |
| 460 | * if the encoding is <code>null</code>, the default | |
| 461 | * <code>TemplateOutputEncoding</code> is used | |
| 462 | * @param context The Context (can be a WebContext too) used | |
| 463 | * during the template evaluation phase | |
| 464 | * @throws java.io.IOException if the template cannot be written to the | |
| 465 | * specified output stream | |
| 466 | * @throws ResourceException if the template name specified cannot be found | |
| 467 | * @throws PropertyException if a fatal error occured during the Template | |
| 468 | * evaluation phase | |
| 469 | */ | |
| 470 | public void writeTemplate (String templateName, java.io.OutputStream out, | |
| 471 | String encoding, Context context) | |
| 472 | throws java.io.IOException, ResourceException, PropertyException | |
| 473 | { | |
| 474 | ||
| 475 | 0 | if (encoding == null) |
| 476 | 0 | encoding = getConfig(WMConstants.TEMPLATE_OUTPUT_ENCODING); |
| 477 | ||
| 478 | 0 | Template tmpl = getTemplate(templateName); |
| 479 | 0 | tmpl.write(out, encoding, context); |
| 480 | 0 | } |
| 481 | ||
| 482 | ||
| 483 | // DELEGATE-TO METHODS -- COMMON THINGS MADE EASIER | |
| 484 | ||
| 485 | /** | |
| 486 | * This method takes a populated context and a template and | |
| 487 | * writes out the interpreted template to the context's output | |
| 488 | * stream. | |
| 489 | */ | |
| 490 | protected void execute (Template tmpl, WebContext c) | |
| 491 | throws IOException | |
| 492 | { | |
| 493 | try | |
| 494 | { | |
| 495 | 0 | HttpServletResponse resp = c.getResponse(); |
| 496 | ||
| 497 | 0 | Locale locale = (Locale) tmpl.getParam( |
| 498 | WMConstants.TEMPLATE_LOCALE); | |
| 499 | 0 | _log.debug("TemplateLocale=" + locale); |
| 500 | 0 | if (locale != null) |
| 501 | { | |
| 502 | 0 | setLocale(resp, locale); |
| 503 | } | |
| 504 | ||
| 505 | 0 | String encoding = (String) tmpl.getParam( |
| 506 | WMConstants.TEMPLATE_OUTPUT_ENCODING); | |
| 507 | 0 | if (encoding == null) |
| 508 | { | |
| 509 | 0 | encoding = resp.getCharacterEncoding(); |
| 510 | } | |
| 511 | ||
| 512 | 0 | _log.debug("Using output encoding " + encoding); |
| 513 | ||
| 514 | // get the bytes before calling getOutputStream | |
| 515 | // this is necessary to be compatible with JSDK 2.3 | |
| 516 | // where you can't call setContentType() after getOutputStream(), | |
| 517 | // which could be happening during the template evaluation | |
| 518 | 0 | byte[] bytes = tmpl.evaluateAsBytes(encoding, c); |
| 519 | ||
| 520 | ||
| 521 | // now write the FW buffer to the response output stream | |
| 522 | 0 | writeResponseBytes(resp, bytes, encoding); |
| 523 | } | |
| 524 | 0 | catch (UnsupportedEncodingException e) |
| 525 | { | |
| 526 | // can be thrown by FastWriter.getInstance | |
| 527 | // rethrow it, because otherwise it would be ignored | |
| 528 | // as an IOException | |
| 529 | 0 | _log.error("tried to use an unsupported encoding", e); |
| 530 | 0 | throw e; |
| 531 | } | |
| 532 | 0 | catch (IOException e) |
| 533 | { | |
| 534 | // ignore disconnect | |
| 535 | } | |
| 536 | 0 | catch (Exception e) |
| 537 | { | |
| 538 | 0 | String error = |
| 539 | "WebMacro encountered an error while executing a template:\n" | |
| 540 | + ((tmpl != null) ? (tmpl + ": " + e + "\n") : | |
| 541 | ("The template failed to load; double check the " | |
| 542 | + "TemplatePath in your webmacro.properties file.")); | |
| 543 | 0 | _log.error(error, e); |
| 544 | try | |
| 545 | { | |
| 546 | 0 | Template errorTemplate = error(c, |
| 547 | "WebMacro encountered an error while executing a template:\n" | |
| 548 | + ((tmpl != null) ? (tmpl + ": ") | |
| 549 | : ("The template failed to load; double check the " | |
| 550 | + "TemplatePath in your webmacro.properties file.")) | |
| 551 | + "\n<pre>" + e + "</pre>\n"); | |
| 552 | ||
| 553 | 0 | String err = errorTemplate.evaluateAsString(c); |
| 554 | 0 | c.getResponse().getWriter().write(err); |
| 555 | } | |
| 556 | 0 | catch (Exception errExcept) |
| 557 | { | |
| 558 | 0 | _log.error("Error writing error template!", errExcept); |
| 559 | 0 | } |
| 560 | 0 | } |
| 561 | 0 | } |
| 562 | ||
| 563 | /** | |
| 564 | * Helper method to write out a FastWriter (that has bufferd | |
| 565 | * the response) to a ServletResponse. This method will try to use | |
| 566 | * the response's OutputStream first and if this fails, fall back | |
| 567 | * to its Writer. | |
| 568 | * @param response where to write fast writer to | |
| 569 | * @param bytes the bytes to write | |
| 570 | */ | |
| 571 | private void writeResponseBytes (HttpServletResponse response, byte[] bytes, String encoding) | |
| 572 | throws IOException | |
| 573 | { | |
| 574 | OutputStream out; | |
| 575 | // We'll check, if the OutputStream is available | |
| 576 | try | |
| 577 | { | |
| 578 | 0 | out = response.getOutputStream(); |
| 579 | } | |
| 580 | 0 | catch (IllegalStateException e) |
| 581 | { | |
| 582 | // Here comes a quick hack, we need a cleaner | |
| 583 | // solution in a future release. (skanthak) | |
| 584 | ||
| 585 | // this means, that the ServletOutputStream is | |
| 586 | // not available, because the Writer has already | |
| 587 | // be used. We have to use it, although its | |
| 588 | // much slower, especially, because we need to | |
| 589 | // revert the encoding process now | |
| 590 | 0 | out = null; |
| 591 | 0 | _log.debug("Using Writer instead of OutputStream"); |
| 592 | 0 | } |
| 593 | 0 | response.setContentLength(bytes.length); |
| 594 | 0 | if (out != null) |
| 595 | { | |
| 596 | 0 | out.write(bytes); |
| 597 | } | |
| 598 | else | |
| 599 | { | |
| 600 | 0 | response.getWriter().write(new String(bytes, encoding)); |
| 601 | } | |
| 602 | 0 | } |
| 603 | ||
| 604 | ||
| 605 | // FRAMEWORK TEMPLATE METHODS--PLUG YOUR CODE IN HERE | |
| 606 | ||
| 607 | ||
| 608 | /** | |
| 609 | * This method is called at the beginning of a request and is | |
| 610 | * responsible for providing a Context for the request. The | |
| 611 | * default implementation calls WebContext.newInstance(req,resp) | |
| 612 | * on the WebContext prototype returned by the initWebContext() method. | |
| 613 | * This is probably suitable for most servlets, though you can override | |
| 614 | * it and do something different if you like. You can throw a | |
| 615 | * HandlerException if something goes wrong. | |
| 616 | */ | |
| 617 | public WebContext newContext ( | |
| 618 | HttpServletRequest req, HttpServletResponse resp) | |
| 619 | throws HandlerException | |
| 620 | { | |
| 621 | 0 | return _wm.getWebContext(req, resp); |
| 622 | //return _wcPrototype.newInstance(req, resp); | |
| 623 | } | |
| 624 | ||
| 625 | /** | |
| 626 | * This method is called to handle the processing of a request. It | |
| 627 | * should analyze the data in the request, put whatever values are | |
| 628 | * required into the context, and return the appropriate view. | |
| 629 | * @param context contains all relevant data structures, incl builtins. | |
| 630 | * @return the template to be rendered by the WebMacro engine | |
| 631 | * @throws HandlerException throw this to produce vanilla error messages | |
| 632 | */ | |
| 633 | public abstract Template handle (WebContext context) | |
| 634 | throws HandlerException; | |
| 635 | ||
| 636 | ||
| 637 | /** | |
| 638 | * This method is called at the end of a request and is responsible | |
| 639 | * for cleaning up the Context at the end of the request. You may | |
| 640 | * not need to do anything here, but it is sometimes important if | |
| 641 | * you have an open database connection in your context that you | |
| 642 | * need to close. The default implementation calls wc.clear(). | |
| 643 | */ | |
| 644 | public void destroyContext (WebContext wc) | |
| 645 | throws HandlerException | |
| 646 | { | |
| 647 | 0 | } |
| 648 | ||
| 649 | ||
| 650 | /** | |
| 651 | * Override this method to implement any startup/init code | |
| 652 | * you require. The broker will have been created before this | |
| 653 | * method is called; the default implementation does nothing. | |
| 654 | * This is called when the servlet environment initializes | |
| 655 | * the servlet for use via the init() method. | |
| 656 | * @exception ServletException to indicate initialization failed | |
| 657 | */ | |
| 658 | protected void start () throws ServletException | |
| 659 | { | |
| 660 | 0 | } |
| 661 | ||
| 662 | /** | |
| 663 | * Override this method to implement any shutdown code you require. | |
| 664 | * The broker may be destroyed just after this method exits. This | |
| 665 | * is called when the servlet environment shuts down the servlet | |
| 666 | * via the shutdown() method. The default implementation does nothing. | |
| 667 | */ | |
| 668 | protected void stop () | |
| 669 | { | |
| 670 | 0 | } |
| 671 | ||
| 672 | ||
| 673 | /** | |
| 674 | * This method returns the WebMacro object which will be used to load, | |
| 675 | * access, and manage the Broker. The default implementation is to | |
| 676 | * return a new WM() object. You could override it and return a WM | |
| 677 | * object constructed with a particular configuration file, or some | |
| 678 | * other implementation of the WebMacro interface. | |
| 679 | */ | |
| 680 | public WebMacro initWebMacro () throws InitException | |
| 681 | { | |
| 682 | 0 | return new WM(this); |
| 683 | } | |
| 684 | ||
| 685 | /** | |
| 686 | * NO LONGER USED | |
| 687 | * Exists only to catch implementations that use it. | |
| 688 | * Use newWebContext instead. | |
| 689 | * @deprecated | |
| 690 | */ | |
| 691 | public final WebContext initWebContext () throws InitException | |
| 692 | { | |
| 693 | 0 | return null; |
| 694 | } | |
| 695 | ||
| 696 | public WebContext newWebContext(HttpServletRequest req, HttpServletResponse resp) { | |
| 697 | 0 | return new WebContext(_broker, req, resp); |
| 698 | } | |
| 699 | ||
| 700 | /** | |
| 701 | * Set the locale on the response. The reflection trickery is because | |
| 702 | * this is only defined for JSDK 2.2+ | |
| 703 | */ | |
| 704 | protected void setLocale (HttpServletResponse resp, Locale locale) | |
| 705 | { | |
| 706 | try | |
| 707 | { | |
| 708 | 0 | Method m = HttpServletResponse.class.getMethod( |
| 709 | "setLocale", | |
| 710 | new Class[] | |
| 711 | {Locale.class}); | |
| 712 | 0 | m.invoke(resp, (Object[])new Locale[]{locale}); |
| 713 | 0 | _log.debug("Successfully set locale to " + locale); |
| 714 | } | |
| 715 | 0 | catch (Exception e) |
| 716 | { | |
| 717 | 0 | _log.debug("Error set locale to " + locale + ": " + e.getClass()); |
| 718 | 0 | } |
| 719 | 0 | } |
| 720 | ||
| 721 | /** | |
| 722 | * Get a new FastWriter. | |
| 723 | * A FastWriter is used when writing templates to an output stream | |
| 724 | * | |
| 725 | * @param out The output stream the FastWriter should write to. Typically | |
| 726 | * this will be your ServletOutputStream | |
| 727 | * @param enctype the Encoding type to use | |
| 728 | * @deprecated | |
| 729 | */ | |
| 730 | public FastWriter getFastWriter (OutputStream out, String enctype) | |
| 731 | throws UnsupportedEncodingException | |
| 732 | { | |
| 733 | 0 | return _wm.getFastWriter(out, enctype); |
| 734 | } | |
| 735 | ||
| 736 | ||
| 737 | private static final String DEFAULT_ERROR_TEXT = | |
| 738 | "<HTML><HEAD><TITLE>Error</TITLE></HEAD>\n" | |
| 739 | + "#set $Response.ContentType = \"text/html\"\n" | |
| 740 | + "<BODY><H1>Error</H1>" | |
| 741 | + "<HR>$error</BODY></HTML>"; | |
| 742 | ||
| 743 | 0 | private Template _errorTemplate = null; |
| 744 | ||
| 745 | /** | |
| 746 | * Gets a template for displaying an error message. | |
| 747 | * Tries to get configured template, then default template | |
| 748 | * and lastly constructs a string template. | |
| 749 | * @return A Template which can be used to format an error message | |
| 750 | */ | |
| 751 | public Template getErrorTemplate () | |
| 752 | { | |
| 753 | 0 | String templateName = getErrorTemplateName(); |
| 754 | ||
| 755 | try | |
| 756 | { | |
| 757 | 0 | _errorTemplate = (Template) _broker.get("template", templateName); |
| 758 | } | |
| 759 | 0 | catch (ResourceException e) |
| 760 | { | |
| 761 | 0 | _errorTemplate = new org.webmacro.engine.StringTemplate(_broker, DEFAULT_ERROR_TEXT, |
| 762 | "WebMacro default error template"); | |
| 763 | 0 | } |
| 764 | 0 | return _errorTemplate; |
| 765 | } | |
| 766 | ||
| 767 | protected String getErrorTemplateName () | |
| 768 | { | |
| 769 | String templateName; | |
| 770 | ||
| 771 | try | |
| 772 | { | |
| 773 | 0 | templateName = (String) _broker.get("config", ERROR_TEMPLATE); |
| 774 | } | |
| 775 | 0 | catch (ResourceException e) |
| 776 | { | |
| 777 | 0 | templateName = ERROR_TEMPLATE_DEFAULT; |
| 778 | 0 | } |
| 779 | 0 | return templateName; |
| 780 | } | |
| 781 | ||
| 782 | ||
| 783 | } | |
| 784 |