Clover coverage report - Code Coverage for tapestry release 3.1-alpha-1
Coverage timestamp: Mon Feb 21 2005 09:16:14 EST
file stats: LOC: 824   Methods: 33
NCLOC: 364   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
AbstractEngine.java 53.3% 76.2% 72.7% 72%
coverage coverage
 1   
 // Copyright 2004, 2005 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.tapestry.engine;
 16   
 
 17   
 import java.io.IOException;
 18   
 import java.io.UnsupportedEncodingException;
 19   
 import java.util.ArrayList;
 20   
 import java.util.Collection;
 21   
 import java.util.List;
 22   
 import java.util.Locale;
 23   
 
 24   
 import javax.servlet.RequestDispatcher;
 25   
 import javax.servlet.ServletContext;
 26   
 import javax.servlet.ServletException;
 27   
 import javax.servlet.http.HttpServletRequest;
 28   
 
 29   
 import org.apache.commons.lang.builder.ToStringBuilder;
 30   
 import org.apache.commons.logging.Log;
 31   
 import org.apache.commons.logging.LogFactory;
 32   
 import org.apache.hivemind.ApplicationRuntimeException;
 33   
 import org.apache.hivemind.ClassResolver;
 34   
 import org.apache.hivemind.util.Defense;
 35   
 import org.apache.tapestry.ApplicationServlet;
 36   
 import org.apache.tapestry.Constants;
 37   
 import org.apache.tapestry.IEngine;
 38   
 import org.apache.tapestry.IPage;
 39   
 import org.apache.tapestry.IRequestCycle;
 40   
 import org.apache.tapestry.PageRedirectException;
 41   
 import org.apache.tapestry.RedirectException;
 42   
 import org.apache.tapestry.StaleLinkException;
 43   
 import org.apache.tapestry.StaleSessionException;
 44   
 import org.apache.tapestry.Tapestry;
 45   
 import org.apache.tapestry.TapestryConstants;
 46   
 import org.apache.tapestry.listener.ListenerMap;
 47   
 import org.apache.tapestry.request.RequestContext;
 48   
 import org.apache.tapestry.request.ResponseOutputStream;
 49   
 import org.apache.tapestry.services.DataSqueezer;
 50   
 import org.apache.tapestry.services.Infrastructure;
 51   
 import org.apache.tapestry.spec.IApplicationSpecification;
 52   
 
 53   
 /**
 54   
  * Basis for building real Tapestry applications. Immediate subclasses provide different strategies
 55   
  * for managing page state and other resources between request cycles.
 56   
  * <p>
 57   
  * Note: much of this description is <em>in transition</em> as part of Tapestry 3.1. All ad-hoc
 58   
  * singletons and such are being replaced with HiveMind services.
 59   
  * <p>
 60   
  * Uses a shared instance of {@link ITemplateSource},{@link ISpecificationSource},
 61   
  * {@link IScriptSource}and {@link IComponentMessagesSource}stored as attributes of the
 62   
  * {@link ServletContext}(they will be shared by all sessions).
 63   
  * <p>
 64   
  * An engine is designed to be very lightweight. Particularily, it should <b>never </b> hold
 65   
  * references to any {@link IPage}or {@link org.apache.tapestry.IComponent}objects. The entire
 66   
  * system is based upon being able to quickly rebuild the state of any page(s).
 67   
  * <p>
 68   
  * Where possible, instance variables should be transient. They can be restored inside
 69   
  * {@link #setupForRequest(RequestContext)}.
 70   
  * <p>
 71   
  * In practice, a subclass (usually {@link BaseEngine}) is used without subclassing. Instead, a
 72   
  * visit object is specified. To facilitate this, the application specification may include a
 73   
  * property, <code>org.apache.tapestry.visit-class</code> which is the class name to instantiate
 74   
  * when a visit object is first needed. See {@link #createVisit(IRequestCycle)}for more details.
 75   
  * <p>
 76   
  * Some of the classes' behavior is controlled by JVM system properties (typically only used during
 77   
  * development): <table border=1>
 78   
  * <tr>
 79   
  * <th>Property</th>
 80   
  * <th>Description</th>
 81   
  * </tr>
 82   
  * <tr>
 83   
  * <td>org.apache.tapestry.enable-reset-service</td>
 84   
  * <td>If true, enabled an additional service, reset, that allow page, specification and template
 85   
  * caches to be cleared on demand. See {@link #isResetServiceEnabled()}.</td>
 86   
  * </tr>
 87   
  * <tr>
 88   
  * <td>org.apache.tapestry.disable-caching</td>
 89   
  * <td>If true, then the page, specification, template and script caches will be cleared after each
 90   
  * request. This slows things down, but ensures that the latest versions of such files are used.
 91   
  * Care should be taken that the source directories for the files preceeds any versions of the files
 92   
  * available in JARs or WARs.</td>
 93   
  * </tr>
 94   
  * </table>
 95   
  * 
 96   
  * @author Howard Lewis Ship
 97   
  */
 98   
 
 99   
 public abstract class AbstractEngine implements IEngine
 100   
 {
 101   
     private static final Log LOG = LogFactory.getLog(AbstractEngine.class);
 102   
 
 103   
     /**
 104   
      * The link to the world of HiveMind services.
 105   
      * 
 106   
      * @since 3.1
 107   
      */
 108   
     private Infrastructure _infrastructure;
 109   
 
 110   
     private boolean _stateful;
 111   
 
 112   
     private ListenerMap _listeners;
 113   
 
 114   
     /**
 115   
      * The name of the application property that will be used to determine the encoding to use when
 116   
      * generating the output
 117   
      * 
 118   
      * @since 3.0
 119   
      */
 120   
 
 121   
     public static final String OUTPUT_ENCODING_PROPERTY_NAME = "org.apache.tapestry.output-encoding";
 122   
 
 123   
     /**
 124   
      * The default encoding that will be used when generating the output. It is used if no output
 125   
      * encoding property has been specified.
 126   
      * 
 127   
      * @since 3.0
 128   
      */
 129   
 
 130   
     public static final String DEFAULT_OUTPUT_ENCODING = "UTF-8";
 131   
 
 132   
     /**
 133   
      * The curent locale for the engine, which may be changed at any time.
 134   
      */
 135   
 
 136   
     private Locale _locale;
 137   
 
 138   
     /**
 139   
      * The name of the application specification property used to specify the class of the visit
 140   
      * object.
 141   
      */
 142   
 
 143   
     public static final String VISIT_CLASS_PROPERTY_NAME = "org.apache.tapestry.visit-class";
 144   
 
 145   
     /**
 146   
      * If true (set from the JVM system parameter <code>org.apache.tapestry.disable-caching</code>)
 147   
      * then the cache of pages, specifications and template will be cleared after each request.
 148   
      */
 149   
 
 150   
     private static final boolean _disableCaching = Boolean
 151   
             .getBoolean("org.apache.tapestry.disable-caching");
 152   
 
 153   
     /**
 154   
      * The instance of {@link IMonitorFactory}used to create a monitor.
 155   
      * 
 156   
      * @since 3.0
 157   
      */
 158   
 
 159   
     private IMonitorFactory _monitorFactory;
 160   
 
 161   
     /**
 162   
      * Sets the Exception page's exception property, then renders the Exception page.
 163   
      * <p>
 164   
      * If the render throws an exception, then copious output is sent to <code>System.err</code>
 165   
      * and a {@link ServletException}is thrown.
 166   
      */
 167   
 
 168  37
     protected void activateExceptionPage(IRequestCycle cycle, ResponseOutputStream output,
 169   
             Throwable cause) throws ServletException
 170   
     {
 171  37
         try
 172   
         {
 173  37
             IPage exceptionPage = cycle.getPage(getExceptionPageName());
 174   
 
 175  37
             exceptionPage.setProperty("exception", cause);
 176   
 
 177  37
             cycle.activate(exceptionPage);
 178   
 
 179  37
             renderResponse(cycle, output);
 180   
 
 181   
         }
 182   
         catch (Throwable ex)
 183   
         {
 184   
             // Worst case scenario. The exception page itself is broken, leaving
 185   
             // us with no option but to write the cause to the output.
 186   
 
 187  0
             reportException(
 188   
                     Tapestry.getMessage("AbstractEngine.unable-to-process-client-request"),
 189   
                     cause);
 190   
 
 191   
             // Also, write the exception thrown when redendering the exception
 192   
             // page, so that can get fixed as well.
 193   
 
 194  0
             reportException(
 195   
                     Tapestry.getMessage("AbstractEngine.unable-to-present-exception-page"),
 196   
                     ex);
 197   
 
 198   
             // And throw the exception.
 199   
 
 200  0
             throw new ServletException(ex.getMessage(), ex);
 201   
         }
 202   
     }
 203   
 
 204   
     /**
 205   
      * Writes a detailed report of the exception to <code>System.err</code>.
 206   
      */
 207   
 
 208  3
     public void reportException(String reportTitle, Throwable ex)
 209   
     {
 210  3
         _infrastructure.getRequestExceptionReporter().reportRequestException(reportTitle, ex);
 211   
     }
 212   
 
 213   
     /**
 214   
      * Invoked at the end of the request cycle to release any resources specific to the request
 215   
      * cycle.
 216   
      */
 217   
 
 218   
     protected abstract void cleanupAfterRequest(IRequestCycle cycle);
 219   
 
 220   
     /**
 221   
      * Returns the locale for the engine. This is initially set by the {@link ApplicationServlet}
 222   
      * but may be updated by the application.
 223   
      */
 224   
 
 225  835
     public Locale getLocale()
 226   
     {
 227  835
         return _locale;
 228   
     }
 229   
 
 230   
     /**
 231   
      * Overriden in subclasses that support monitoring. Should create and return an instance of
 232   
      * {@link IMonitor}that is appropriate for the request cycle described by the
 233   
      * {@link RequestContext}.
 234   
      * <p>
 235   
      * The monitor is used to create a {@link RequestCycle}.
 236   
      * <p>
 237   
      * This implementation uses a {@link IMonitorFactory}to create the monitor instance. The
 238   
      * factory is provided as an application extension. If the application extension does not exist,
 239   
      * {@link DefaultMonitorFactory}is used.
 240   
      * <p>
 241   
      * As of release 3.0, this method should <em>not</em> return null.
 242   
      */
 243   
 
 244  0
     public IMonitor getMonitor(RequestContext context)
 245   
     {
 246  0
         if (_monitorFactory == null)
 247   
         {
 248  0
             IApplicationSpecification spec = getSpecification();
 249   
 
 250  0
             if (spec.checkExtension(Tapestry.MONITOR_FACTORY_EXTENSION_NAME))
 251  0
                 _monitorFactory = (IMonitorFactory) spec.getExtension(
 252   
                         Tapestry.MONITOR_FACTORY_EXTENSION_NAME,
 253   
                         IMonitorFactory.class);
 254   
             else
 255  0
                 _monitorFactory = DefaultMonitorFactory.SHARED;
 256   
         }
 257   
 
 258  0
         return _monitorFactory.createMonitor(context);
 259   
     }
 260   
 
 261   
     /**
 262   
      * Returns a service with the given name.
 263   
      * 
 264   
      * @see Infrastructure#getServiceMap()
 265   
      * @see org.apache.tapestry.services.ServiceMap
 266   
      */
 267   
 
 268  211
     public IEngineService getService(String name)
 269   
     {
 270  211
         return _infrastructure.getServiceMap().getService(name);
 271   
     }
 272   
 
 273   
     /** @see Infrastructure#getApplicationSpecification() */
 274   
 
 275  6
     public IApplicationSpecification getSpecification()
 276   
     {
 277  6
         return _infrastructure.getApplicationSpecification();
 278   
     }
 279   
 
 280   
     /** @see Infrastructure#getSpecificationSource() */
 281   
 
 282  0
     public ISpecificationSource getSpecificationSource()
 283   
     {
 284  0
         return _infrastructure.getSpecificationSource();
 285   
     }
 286   
 
 287   
     /**
 288   
      * Invoked, typically, when an exception occurs while servicing the request. This method resets
 289   
      * the output, sets the new page and renders it.
 290   
      */
 291   
 
 292  10
     protected void redirect(String pageName, IRequestCycle cycle, ResponseOutputStream out,
 293   
             ApplicationRuntimeException exception) throws IOException, ServletException
 294   
     {
 295   
         // Discard any output from the previous page.
 296   
 
 297  10
         out.reset();
 298   
 
 299  10
         IPage page = cycle.getPage(pageName);
 300   
 
 301  10
         cycle.activate(page);
 302   
 
 303  10
         renderResponse(cycle, out);
 304   
     }
 305   
 
 306   
     /**
 307   
      * Delegates to
 308   
      * {@link org.apache.tapestry.services.ResponseRenderer#renderResponse(IRequestCycle, ResponseOutputStream)}.
 309   
      */
 310   
 
 311  49
     public void renderResponse(IRequestCycle cycle, ResponseOutputStream output)
 312   
             throws ServletException, IOException
 313   
     {
 314  49
         _infrastructure.getResponseRenderer().renderResponse(cycle, output);
 315   
     }
 316   
 
 317   
     /**
 318   
      * Delegate method for the servlet. Services the request.
 319   
      */
 320   
 
 321  185
     public boolean service(RequestContext context) throws ServletException, IOException
 322   
     {
 323  185
         IRequestCycle cycle = null;
 324  185
         IMonitor monitor = null;
 325  185
         IEngineService service = null;
 326  185
         ResponseOutputStream output = null;
 327   
 
 328  185
         if (_infrastructure == null)
 329  60
             _infrastructure = (Infrastructure) context.getAttribute(Constants.INFRASTRUCTURE_KEY);
 330   
 
 331  185
         try
 332   
         {
 333  185
             setupForRequest(context);
 334   
 
 335  185
             output = new ResponseOutputStream(context.getResponse());
 336   
         }
 337   
         catch (Exception ex)
 338   
         {
 339  0
             reportException(Tapestry.getMessage("AbstractEngine.unable-to-begin-request"), ex);
 340   
 
 341  0
             throw new ServletException(ex.getMessage(), ex);
 342   
         }
 343   
 
 344  185
         try
 345   
         {
 346  185
             try
 347   
             {
 348  185
                 cycle = _infrastructure.getRequestCycleFactory().newRequestCycle(this, context);
 349   
 
 350  185
                 monitor = cycle.getMonitor();
 351  185
                 service = cycle.getService();
 352   
 
 353  185
                 monitor.serviceBegin(service.getName(), context.getRequestURI());
 354   
 
 355   
                 // Invoke the service, which returns true if it may have changed
 356   
                 // the state of the engine (most do return true).
 357   
 
 358  185
                 service.service(cycle, output);
 359   
 
 360   
                 // Return true only if the engine is actually dirty. This cuts
 361   
                 // down
 362   
                 // on the number of times the engine is stored into the
 363   
                 // session unceccesarily.
 364   
 
 365  133
                 return false;
 366   
             }
 367   
             catch (PageRedirectException ex)
 368   
             {
 369  3
                 handlePageRedirectException(ex, cycle, output);
 370   
             }
 371   
             catch (RedirectException ex)
 372   
             {
 373  7
                 handleRedirectException(cycle, ex);
 374   
             }
 375   
             catch (StaleLinkException ex)
 376   
             {
 377  4
                 handleStaleLinkException(ex, cycle, output);
 378   
             }
 379   
             catch (StaleSessionException ex)
 380   
             {
 381  6
                 handleStaleSessionException(ex, cycle, output);
 382   
             }
 383   
         }
 384   
         catch (Exception ex)
 385   
         {
 386  37
             monitor.serviceException(ex);
 387   
 
 388   
             // Discard any output (if possible). If output has already been sent
 389   
             // to
 390   
             // the client, then things get dicey. Note that this block
 391   
             // gets activated if the StaleLink or StaleSession pages throws
 392   
             // any kind of exception.
 393   
 
 394   
             // Attempt to switch to the exception page. However, this may itself
 395   
             // fail
 396   
             // for a number of reasons, in which case a ServletException is
 397   
             // thrown.
 398   
 
 399  37
             output.reset();
 400   
 
 401  37
             if (LOG.isDebugEnabled())
 402  0
                 LOG.debug("Uncaught exception", ex);
 403   
 
 404  37
             activateExceptionPage(cycle, output, ex);
 405   
         }
 406   
         finally
 407   
         {
 408  185
             if (service != null)
 409  185
                 monitor.serviceEnd(service.getName());
 410   
 
 411  185
             try
 412   
             {
 413  185
                 cycle.cleanup();
 414  185
                 _infrastructure.getApplicationStateManager().flush();
 415   
 
 416   
                 // Closing the buffered output closes the underlying stream as
 417   
                 // well.
 418   
 
 419  185
                 if (output != null)
 420  185
                     output.forceFlush();
 421   
 
 422  185
                 cleanupAfterRequest(cycle);
 423   
             }
 424   
             catch (Exception ex)
 425   
             {
 426  0
                 reportException(Tapestry.getMessage("AbstractEngine.exception-during-cleanup"), ex);
 427   
             }
 428   
 
 429  185
             if (_disableCaching)
 430   
             {
 431  0
                 try
 432   
                 {
 433  0
                     _infrastructure.getResetEventCoordinator().fireResetEvent();
 434   
                 }
 435   
                 catch (Exception ex)
 436   
                 {
 437  0
                     reportException(Tapestry
 438   
                             .getMessage("AbstractEngine.exception-during-cache-clear"), ex);
 439   
                 }
 440   
             }
 441   
 
 442   
         }
 443   
 
 444  52
         return false;
 445   
     }
 446   
 
 447   
     /**
 448   
      * Handles {@link PageRedirectException}which involves executing
 449   
      * {@link IPage#validate(IRequestCycle)}on the target page (of the exception), until either a
 450   
      * loop is found, or a page succesfully validates and can be activated.
 451   
      * <p>
 452   
      * This should generally not be overriden in subclasses.
 453   
      * 
 454   
      * @since 3.0
 455   
      */
 456   
 
 457  3
     protected void handlePageRedirectException(PageRedirectException ex, IRequestCycle cycle,
 458   
             ResponseOutputStream output) throws IOException, ServletException
 459   
     {
 460  3
         List pageNames = new ArrayList();
 461   
 
 462  3
         String pageName = ex.getTargetPageName();
 463   
 
 464  3
         while (true)
 465   
         {
 466  5
             if (pageNames.contains(pageName))
 467   
             {
 468   
                 // Add the offending page to pageNames so it shows in the
 469   
                 // list.
 470   
 
 471  1
                 pageNames.add(pageName);
 472   
 
 473  1
                 StringBuffer buffer = new StringBuffer();
 474  1
                 int count = pageNames.size();
 475   
 
 476  1
                 for (int i = 0; i < count; i++)
 477   
                 {
 478  3
                     if (i > 0)
 479  2
                         buffer.append("; ");
 480   
 
 481  3
                     buffer.append(pageNames.get(i));
 482   
                 }
 483   
 
 484  1
                 throw new ApplicationRuntimeException(Tapestry.format(
 485   
                         "AbstractEngine.validate-cycle",
 486   
                         buffer.toString()));
 487   
             }
 488   
 
 489   
             // Record that this page has been a target.
 490   
 
 491  4
             pageNames.add(pageName);
 492   
 
 493  4
             try
 494   
             {
 495   
                 // Attempt to activate the new page.
 496   
 
 497  4
                 cycle.activate(pageName);
 498   
 
 499  2
                 break;
 500   
             }
 501   
             catch (PageRedirectException ex2)
 502   
             {
 503  2
                 pageName = ex2.getTargetPageName();
 504   
             }
 505   
         }
 506   
 
 507   
         // Discard any output from the previous page.
 508   
 
 509  2
         output.reset();
 510   
 
 511  2
         renderResponse(cycle, output);
 512   
     }
 513   
 
 514   
     /**
 515   
      * Invoked by {@link #service(RequestContext)}if a {@link StaleLinkException}is thrown by the
 516   
      * {@link IEngineService service}. This implementation sets the message property of the
 517   
      * StaleLink page to the message provided in the exception, then invokes
 518   
      * {@link #redirect(String, IRequestCycle, ResponseOutputStream, ApplicationRuntimeException)}
 519   
      * to render the StaleLink page.
 520   
      * <p>
 521   
      * Subclasses may overide this method (without invoking this implementation). A common practice
 522   
      * is to present an error message on the application's Home page.
 523   
      * <p>
 524   
      * Alternately, the application may provide its own version of the StaleLink page, overriding
 525   
      * the framework's implementation (probably a good idea, because the default page hints at
 526   
      * "application errors" and isn't localized). The overriding StaleLink implementation must
 527   
      * implement a message property of type String.
 528   
      * 
 529   
      * @since 0.2.10
 530   
      */
 531   
 
 532  4
     protected void handleStaleLinkException(StaleLinkException ex, IRequestCycle cycle,
 533   
             ResponseOutputStream output) throws IOException, ServletException
 534   
     {
 535  4
         String staleLinkPageName = getStaleLinkPageName();
 536  4
         IPage page = cycle.getPage(staleLinkPageName);
 537   
 
 538  4
         page.setProperty("message", ex.getMessage());
 539   
 
 540  4
         redirect(staleLinkPageName, cycle, output, ex);
 541   
     }
 542   
 
 543   
     /**
 544   
      * Invoked by {@link #service(RequestContext)}if a {@link StaleSessionException}is thrown by
 545   
      * the {@link IEngineService service}. This implementation invokes
 546   
      * {@link #redirect(String, IRequestCycle, ResponseOutputStream, ApplicationRuntimeException)}
 547   
      * to render the StaleSession page.
 548   
      * <p>
 549   
      * Subclasses may overide this method (without invoking this implementation). A common practice
 550   
      * is to present an eror message on the application's Home page.
 551   
      * 
 552   
      * @since 0.2.10
 553   
      */
 554   
 
 555  6
     protected void handleStaleSessionException(StaleSessionException ex, IRequestCycle cycle,
 556   
             ResponseOutputStream output) throws IOException, ServletException
 557   
     {
 558  6
         redirect(getStaleSessionPageName(), cycle, output, ex);
 559   
     }
 560   
 
 561   
     /**
 562   
      * Changes the locale for the engine.
 563   
      */
 564   
 
 565  74
     public void setLocale(Locale value)
 566   
     {
 567  74
         Defense.notNull(value, "locale");
 568   
 
 569   
         // Because locale changes are expensive (it involves writing a cookie
 570   
         // and all that),
 571   
         // we're careful not to really change unless there's a true change in
 572   
         // value.
 573   
 
 574  74
         if (!value.equals(_locale))
 575  74
             _locale = value;
 576   
 
 577   
     }
 578   
 
 579   
     /**
 580   
      * Invoked from {@link #service(RequestContext)}to ensure that the engine's instance variables
 581   
      * are setup. This allows the application a chance to restore variables that will not have
 582   
      * survived deserialization. Determines the servlet prefix: this is the base URL used by
 583   
      * {@link IEngineService services}to build URLs. It consists of two parts: the context path and
 584   
      * the servlet path.
 585   
      * <p>
 586   
      * The servlet path is retrieved from {@link HttpServletRequest#getServletPath()}.
 587   
      * <p>
 588   
      * The context path is retrieved from {@link HttpServletRequest#getContextPath()}.
 589   
      * <p>
 590   
      * The global object is retrieved from {@link IEngine#getGlobal()}method.
 591   
      * <p>
 592   
      * The final path is available via the {@link #getServletPath()}method.
 593   
      * <p>
 594   
      * Subclasses should invoke this implementation first, then perform their own setup.
 595   
      */
 596   
 
 597  185
     protected void setupForRequest(RequestContext context)
 598   
     {
 599  185
         HttpServletRequest request = context.getRequest();
 600   
 
 601  185
         String encoding = request.getCharacterEncoding();
 602  185
         if (encoding == null)
 603   
         {
 604  185
             encoding = getOutputEncoding();
 605  185
             try
 606   
             {
 607  185
                 request.setCharacterEncoding(encoding);
 608   
             }
 609   
             catch (UnsupportedEncodingException e)
 610   
             {
 611  0
                 throw new IllegalArgumentException(Tapestry.format("illegal-encoding", encoding));
 612   
             }
 613   
             catch (NoSuchMethodError e)
 614   
             {
 615   
                 // Servlet API 2.2 compatibility
 616   
                 // Behave okay if the setCharacterEncoding() method is
 617   
                 // unavailable
 618   
             }
 619   
             catch (AbstractMethodError e)
 620   
             {
 621   
                 // Servlet API 2.2 compatibility
 622   
                 // Behave okay if the setCharacterEncoding() method is
 623   
                 // unavailable
 624   
             }
 625   
         }
 626   
     }
 627   
 
 628   
     /**
 629   
      * @see Infrastructure#getClassResolver()
 630   
      */
 631   
 
 632  52
     public ClassResolver getClassResolver()
 633   
     {
 634  52
         return _infrastructure.getClassResolver();
 635   
     }
 636   
 
 637   
     /**
 638   
      * Generates a description of the instance. Invokes {@link #extendDescription(ToStringBuilder)}
 639   
      * to fill in details about the instance.
 640   
      * 
 641   
      * @see #extendDescription(ToStringBuilder)
 642   
      */
 643   
 
 644  0
     public String toString()
 645   
     {
 646  0
         ToStringBuilder builder = new ToStringBuilder(this);
 647   
 
 648  0
         builder.append("locale", _locale);
 649   
 
 650  0
         return builder.toString();
 651   
     }
 652   
 
 653   
     /**
 654   
      * Implemented by subclasses to return the names of the active pages (pages for which recorders
 655   
      * exist). May return the empty list, but should not return null.
 656   
      */
 657   
 
 658   
     abstract public Collection getActivePageNames();
 659   
 
 660   
     /**
 661   
      * Gets the visit object from the
 662   
      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, creating it if it does not
 663   
      * already exist.
 664   
      * <p>
 665   
      * As of Tapestry 3.1, this will always create the visit object, possibly creating a new session
 666   
      * in the process.
 667   
      */
 668   
 
 669  3
     public Object getVisit()
 670   
     {
 671  3
         return _infrastructure.getApplicationStateManager().get("visit");
 672   
     }
 673   
 
 674  0
     public void setVisit(Object visit)
 675   
     {
 676  0
         _infrastructure.getApplicationStateManager().store("visit", visit);
 677   
     }
 678   
 
 679   
     /**
 680   
      * Gets the visit object from the
 681   
      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, which will create it as
 682   
      * necessary.
 683   
      */
 684   
 
 685  2
     public Object getVisit(IRequestCycle cycle)
 686   
     {
 687  2
         return getVisit();
 688   
     }
 689   
 
 690  0
     public boolean getHasVisit()
 691   
     {
 692  0
         return _infrastructure.getApplicationStateManager().exists("visit");
 693   
     }
 694   
 
 695   
     /**
 696   
      * Returns the global object for the application. The global object is created at the start of
 697   
      * the request ({@link #setupForRequest(RequestContext)}invokes
 698   
      * {@link #createGlobal(RequestContext)}if needed), and is stored into the
 699   
      * {@link ServletContext}. All instances of the engine for the application share the global
 700   
      * object; however, the global object is explicitly <em>not</em> replicated to other servers
 701   
      * within a cluster.
 702   
      * 
 703   
      * @since 2.3
 704   
      */
 705   
 
 706  1
     public Object getGlobal()
 707   
     {
 708  1
         return _infrastructure.getApplicationStateManager().get("global");
 709   
     }
 710   
 
 711  7
     public IScriptSource getScriptSource()
 712   
     {
 713  7
         return _infrastructure.getScriptSource();
 714   
     }
 715   
 
 716   
     /**
 717   
      * Allows subclasses to include listener methods easily.
 718   
      * 
 719   
      * @since 1.0.2
 720   
      */
 721   
 
 722  0
     public ListenerMap getListeners()
 723   
     {
 724  0
         if (_listeners == null)
 725  0
             _listeners = new ListenerMap(this);
 726   
 
 727  0
         return _listeners;
 728   
     }
 729   
 
 730   
     /**
 731   
      * Invoked when a {@link RedirectException}is thrown during the processing of a request.
 732   
      * 
 733   
      * @throws ApplicationRuntimeException
 734   
      *             if an {@link IOException},{@link ServletException}is thrown by the redirect,
 735   
      *             or if no {@link RequestDispatcher}can be found for local resource.
 736   
      * @since 2.2
 737   
      */
 738   
 
 739  7
     protected void handleRedirectException(IRequestCycle cycle, RedirectException ex)
 740   
     {
 741  7
         String location = ex.getRedirectLocation();
 742   
 
 743  7
         if (LOG.isDebugEnabled())
 744  0
             LOG.debug("Redirecting to: " + location);
 745   
 
 746  7
         RedirectAnalyzer analyzer = new RedirectAnalyzer(location);
 747   
 
 748  7
         analyzer.process(cycle);
 749   
     }
 750   
 
 751   
     /**
 752   
      * @see Infrastructure#getDataSqueezer()
 753   
      */
 754   
 
 755  0
     public DataSqueezer getDataSqueezer()
 756   
     {
 757  0
         return _infrastructure.getDataSqueezer();
 758   
     }
 759   
 
 760   
     /** @since 2.3 */
 761   
 
 762  617
     public IPropertySource getPropertySource()
 763   
     {
 764  617
         return _infrastructure.getApplicationPropertySource();
 765   
     }
 766   
 
 767   
     /** @since 3.0 */
 768   
 
 769  34
     protected String getExceptionPageName()
 770   
     {
 771  34
         return TapestryConstants.EXCEPTION_PAGE;
 772   
     }
 773   
 
 774   
     /** @since 3.0 */
 775   
 
 776  3
     protected String getStaleLinkPageName()
 777   
     {
 778  3
         return TapestryConstants.STALE_LINK_PAGE;
 779   
     }
 780   
 
 781   
     /** @since 3.0 */
 782   
 
 783  3
     protected String getStaleSessionPageName()
 784   
     {
 785  3
         return TapestryConstants.STALE_SESSION_PAGE;
 786   
     }
 787   
 
 788   
     /**
 789   
      * The encoding to be used if none has been defined using the output encoding property. Override
 790   
      * this method to change the default.
 791   
      * 
 792   
      * @return the default output encoding
 793   
      * @since 3.0
 794   
      */
 795  0
     protected String getDefaultOutputEncoding()
 796   
     {
 797  0
         return DEFAULT_OUTPUT_ENCODING;
 798   
     }
 799   
 
 800   
     /**
 801   
      * Returns the encoding to be used to generate the servlet responses and accept the servlet
 802   
      * requests. The encoding is defined using the org.apache.tapestry.output-encoding and is UTF-8
 803   
      * by default
 804   
      * 
 805   
      * @since 3.0
 806   
      * @see org.apache.tapestry.IEngine#getOutputEncoding()
 807   
      */
 808  617
     public String getOutputEncoding()
 809   
     {
 810  617
         IPropertySource source = getPropertySource();
 811   
 
 812  617
         String encoding = source.getPropertyValue(OUTPUT_ENCODING_PROPERTY_NAME);
 813  617
         if (encoding == null)
 814  0
             encoding = getDefaultOutputEncoding();
 815   
 
 816  617
         return encoding;
 817   
     }
 818   
 
 819   
     /** @since 3.1 */
 820  0
     public Infrastructure getInfrastructure()
 821   
     {
 822  0
         return _infrastructure;
 823   
     }
 824   
 }