Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 542   Methods: 28
NCLOC: 241   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 57.1% 71.1% 60.7% 66.9%
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.util.ArrayList;
 19   
 import java.util.List;
 20   
 import java.util.Locale;
 21   
 
 22   
 import javax.servlet.RequestDispatcher;
 23   
 import javax.servlet.ServletContext;
 24   
 import javax.servlet.ServletException;
 25   
 
 26   
 import org.apache.commons.lang.builder.ToStringBuilder;
 27   
 import org.apache.commons.logging.Log;
 28   
 import org.apache.commons.logging.LogFactory;
 29   
 import org.apache.hivemind.ApplicationRuntimeException;
 30   
 import org.apache.hivemind.ClassResolver;
 31   
 import org.apache.hivemind.util.Defense;
 32   
 import org.apache.tapestry.ApplicationServlet;
 33   
 import org.apache.tapestry.Constants;
 34   
 import org.apache.tapestry.IEngine;
 35   
 import org.apache.tapestry.IPage;
 36   
 import org.apache.tapestry.IRequestCycle;
 37   
 import org.apache.tapestry.PageRedirectException;
 38   
 import org.apache.tapestry.RedirectException;
 39   
 import org.apache.tapestry.StaleLinkException;
 40   
 import org.apache.tapestry.StaleSessionException;
 41   
 import org.apache.tapestry.TapestryConstants;
 42   
 import org.apache.tapestry.listener.ListenerMap;
 43   
 import org.apache.tapestry.request.RequestContext;
 44   
 import org.apache.tapestry.services.DataSqueezer;
 45   
 import org.apache.tapestry.services.Infrastructure;
 46   
 import org.apache.tapestry.spec.IApplicationSpecification;
 47   
 import org.apache.tapestry.web.WebRequest;
 48   
 import org.apache.tapestry.web.WebResponse;
 49   
 
 50   
 /**
 51   
  * Basis for building real Tapestry applications. Immediate subclasses provide different strategies
 52   
  * for managing page state and other resources between request cycles.
 53   
  * <p>
 54   
  * Note: much of this description is <em>in transition</em> as part of Tapestry 4.0. All ad-hoc
 55   
  * singletons and such are being replaced with HiveMind services.
 56   
  * <p>
 57   
  * Uses a shared instance of {@link ITemplateSource},{@link ISpecificationSource},
 58   
  * {@link IScriptSource}and {@link IComponentMessagesSource}stored as attributes of the
 59   
  * {@link ServletContext}(they will be shared by all sessions).
 60   
  * <p>
 61   
  * An engine is designed to be very lightweight. Particularily, it should <b>never </b> hold
 62   
  * references to any {@link IPage}or {@link org.apache.tapestry.IComponent}objects. The entire
 63   
  * system is based upon being able to quickly rebuild the state of any page(s).
 64   
  * <p>
 65   
  * Where possible, instance variables should be transient. They can be restored inside
 66   
  * {@link #setupForRequest(RequestContext)}.
 67   
  * <p>
 68   
  * In practice, a subclass (usually {@link BaseEngine}) is used without subclassing. Instead, a
 69   
  * visit object is specified. To facilitate this, the application specification may include a
 70   
  * property, <code>org.apache.tapestry.visit-class</code> which is the class name to instantiate
 71   
  * when a visit object is first needed. See {@link #createVisit(IRequestCycle)}for more details.
 72   
  * <p>
 73   
  * Some of the classes' behavior is controlled by JVM system properties (typically only used during
 74   
  * development): <table border=1>
 75   
  * <tr>
 76   
  * <th>Property</th>
 77   
  * <th>Description</th>
 78   
  * </tr>
 79   
  * <tr>
 80   
  * <td>org.apache.tapestry.enable-reset-service</td>
 81   
  * <td>If true, enabled an additional service, reset, that allow page, specification and template
 82   
  * caches to be cleared on demand. See {@link #isResetServiceEnabled()}.</td>
 83   
  * </tr>
 84   
  * <tr>
 85   
  * <td>org.apache.tapestry.disable-caching</td>
 86   
  * <td>If true, then the page, specification, template and script caches will be cleared after each
 87   
  * request. This slows things down, but ensures that the latest versions of such files are used.
 88   
  * Care should be taken that the source directories for the files preceeds any versions of the files
 89   
  * available in JARs or WARs.</td>
 90   
  * </tr>
 91   
  * </table>
 92   
  * 
 93   
  * @author Howard Lewis Ship
 94   
  */
 95   
 
 96   
 public abstract class AbstractEngine implements IEngine
 97   
 {
 98   
     private static final Log LOG = LogFactory.getLog(AbstractEngine.class);
 99   
 
 100   
     /**
 101   
      * The link to the world of HiveMind services.
 102   
      * 
 103   
      * @since 4.0
 104   
      */
 105   
     private Infrastructure _infrastructure;
 106   
 
 107   
     private ListenerMap _listeners;
 108   
 
 109   
     /**
 110   
      * The curent locale for the engine, which may be changed at any time.
 111   
      */
 112   
 
 113   
     private Locale _locale;
 114   
 
 115   
     /**
 116   
      * The name of the application specification property used to specify the class of the visit
 117   
      * object.
 118   
      */
 119   
 
 120   
     public static final String VISIT_CLASS_PROPERTY_NAME = "org.apache.tapestry.visit-class";
 121   
 
 122   
     /**
 123   
      * @see org.apache.tapestry.error.ExceptionPresenter
 124   
      */
 125   
 
 126  25
     protected void activateExceptionPage(IRequestCycle cycle, Throwable cause)
 127   
     {
 128  25
         _infrastructure.getExceptionPresenter().presentException(cycle, cause);
 129   
     }
 130   
 
 131   
     /**
 132   
      * Writes a detailed report of the exception to <code>System.err</code>.
 133   
      * 
 134   
      * @see org.apache.tapestry.error.RequestExceptionReporter
 135   
      */
 136   
 
 137  0
     public void reportException(String reportTitle, Throwable ex)
 138   
     {
 139  0
         _infrastructure.getRequestExceptionReporter().reportRequestException(reportTitle, ex);
 140   
     }
 141   
 
 142   
     /**
 143   
      * Invoked at the end of the request cycle to release any resources specific to the request
 144   
      * cycle. This implementation does nothing and may be overriden freely.
 145   
      */
 146   
 
 147  0
     protected void cleanupAfterRequest(IRequestCycle cycle)
 148   
     {
 149   
 
 150   
     }
 151   
 
 152   
     /**
 153   
      * Returns the locale for the engine. This is initially set by the {@link ApplicationServlet}
 154   
      * but may be updated by the application.
 155   
      */
 156   
 
 157  512
     public Locale getLocale()
 158   
     {
 159  512
         return _locale;
 160   
     }
 161   
 
 162   
     /**
 163   
      * Returns a service with the given name.
 164   
      * 
 165   
      * @see Infrastructure#getServiceMap()
 166   
      * @see org.apache.tapestry.services.ServiceMap
 167   
      */
 168   
 
 169  89
     public IEngineService getService(String name)
 170   
     {
 171  89
         return _infrastructure.getServiceMap().getService(name);
 172   
     }
 173   
 
 174   
     /** @see Infrastructure#getApplicationSpecification() */
 175   
 
 176  6
     public IApplicationSpecification getSpecification()
 177   
     {
 178  6
         return _infrastructure.getApplicationSpecification();
 179   
     }
 180   
 
 181   
     /** @see Infrastructure#getSpecificationSource() */
 182   
 
 183  0
     public ISpecificationSource getSpecificationSource()
 184   
     {
 185  0
         return _infrastructure.getSpecificationSource();
 186   
     }
 187   
 
 188   
     /**
 189   
      * Invoked, typically, when an exception occurs while servicing the request. This method resets
 190   
      * the output, sets the new page and renders it.
 191   
      */
 192   
 
 193  0
     protected void redirect(String pageName, IRequestCycle cycle,
 194   
             ApplicationRuntimeException exception) throws IOException
 195   
     {
 196  0
         IPage page = cycle.getPage(pageName);
 197   
 
 198  0
         cycle.activate(page);
 199   
 
 200  0
         renderResponse(cycle);
 201   
     }
 202   
 
 203   
     /**
 204   
      * Delegates to
 205   
      * {@link org.apache.tapestry.services.ResponseRenderer#renderResponse(IRequestCycle)}.
 206   
      */
 207   
 
 208  2
     public void renderResponse(IRequestCycle cycle) throws IOException
 209   
     {
 210  2
         _infrastructure.getResponseRenderer().renderResponse(cycle);
 211   
     }
 212   
 
 213   
     /**
 214   
      * Delegate method for the servlet. Services the request.
 215   
      */
 216   
 
 217  148
     public void service(WebRequest request, WebResponse response) throws IOException
 218   
     {
 219  148
         IRequestCycle cycle = null;
 220  148
         IMonitor monitor = null;
 221  148
         IEngineService service = null;
 222   
 
 223  148
         if (_infrastructure == null)
 224  53
             _infrastructure = (Infrastructure) request.getAttribute(Constants.INFRASTRUCTURE_KEY);
 225   
 
 226  148
         try
 227   
         {
 228  148
             try
 229   
             {
 230  148
                 cycle = _infrastructure.getRequestCycleFactory().newRequestCycle(this);
 231   
 
 232  148
                 monitor = cycle.getMonitor();
 233  148
                 service = cycle.getService();
 234   
 
 235  148
                 monitor.serviceBegin(service.getName(), _infrastructure.getRequest()
 236   
                         .getRequestURI());
 237   
 
 238   
                 // Let the service handle the rest of the request.
 239   
 
 240  148
                 service.service(cycle);
 241   
 
 242  117
                 return;
 243   
             }
 244   
             catch (PageRedirectException ex)
 245   
             {
 246  3
                 handlePageRedirectException(cycle, ex);
 247   
             }
 248   
             catch (RedirectException ex)
 249   
             {
 250  0
                 handleRedirectException(cycle, ex);
 251   
             }
 252   
             catch (StaleLinkException ex)
 253   
             {
 254  1
                 handleStaleLinkException(cycle, ex);
 255   
             }
 256   
             catch (StaleSessionException ex)
 257   
             {
 258  3
                 handleStaleSessionException(cycle, ex);
 259   
             }
 260   
         }
 261   
         catch (Exception ex)
 262   
         {
 263  25
             monitor.serviceException(ex);
 264   
 
 265   
             // Attempt to switch to the exception page. However, this may itself
 266   
             // fail for a number of reasons, in which case an ApplicationRuntimeException is
 267   
             // thrown.
 268   
 
 269  25
             if (LOG.isDebugEnabled())
 270  0
                 LOG.debug("Uncaught exception", ex);
 271   
 
 272  25
             activateExceptionPage(cycle, ex);
 273   
         }
 274   
         finally
 275   
         {
 276  148
             if (service != null)
 277  148
                 monitor.serviceEnd(service.getName());
 278   
 
 279  148
             try
 280   
             {
 281  148
                 cycle.cleanup();
 282  148
                 _infrastructure.getApplicationStateManager().flush();
 283   
             }
 284   
             catch (Exception ex)
 285   
             {
 286  0
                 reportException(EngineMessages.exceptionDuringCleanup(ex), ex);
 287   
             }
 288   
         }
 289   
     }
 290   
 
 291   
     /**
 292   
      * Handles {@link PageRedirectException} which involves executing
 293   
      * {@link IRequestCycle#activate(IPage)} on the target page (of the exception), until either a
 294   
      * loop is found, or a page succesfully activates.
 295   
      * <p>
 296   
      * This should generally not be overriden in subclasses.
 297   
      * 
 298   
      * @since 3.0
 299   
      */
 300   
 
 301  3
     protected void handlePageRedirectException(IRequestCycle cycle, PageRedirectException exception)
 302   
             throws IOException
 303   
     {
 304  3
         List pageNames = new ArrayList();
 305   
 
 306  3
         String pageName = exception.getTargetPageName();
 307   
 
 308  3
         while (true)
 309   
         {
 310  5
             if (pageNames.contains(pageName))
 311   
             {
 312  1
                 pageNames.add(pageName);
 313   
 
 314  1
                 throw new ApplicationRuntimeException(EngineMessages.validateCycle(pageNames));
 315   
             }
 316   
 
 317   
             // Record that this page has been a target.
 318   
 
 319  4
             pageNames.add(pageName);
 320   
 
 321  4
             try
 322   
             {
 323   
                 // Attempt to activate the new page.
 324   
 
 325  4
                 cycle.activate(pageName);
 326   
 
 327  2
                 break;
 328   
             }
 329   
             catch (PageRedirectException secondRedirectException)
 330   
             {
 331  2
                 pageName = secondRedirectException.getTargetPageName();
 332   
             }
 333   
         }
 334   
 
 335  2
         renderResponse(cycle);
 336   
     }
 337   
 
 338   
     /**
 339   
      * Invoked by {@link #service(WebRequest, WebResponse)} if a {@link StaleLinkException} is
 340   
      * thrown by the {@link IEngineService service}. This implementation sets the message property
 341   
      * of the StaleLink page to the message provided in the exception, then invokes
 342   
      * {@link #redirect(String, IRequestCycle, ApplicationRuntimeException)} to render the StaleLink
 343   
      * page.
 344   
      * <p>
 345   
      * Subclasses may overide this method (without invoking this implementation). A better practice
 346   
      * is to contribute an alternative implementation of
 347   
      * {@link org.apache.tapestry.error.StaleLinkExceptionPresenter} to the
 348   
      * tapestry.InfrastructureOverrides configuration point.
 349   
      * <p>
 350   
      * A common practice is to present an error message on the application's Home page. Alternately,
 351   
      * the application may provide its own version of the StaleLink page, overriding the framework's
 352   
      * implementation (probably a good idea, because the default page hints at "application errors"
 353   
      * and isn't localized). The overriding StaleLink implementation must implement a message
 354   
      * property of type String.
 355   
      * 
 356   
      * @since 0.2.10
 357   
      */
 358   
 
 359  1
     protected void handleStaleLinkException(IRequestCycle cycle, StaleLinkException exception)
 360   
             throws IOException
 361   
     {
 362  1
         _infrastructure.getStaleLinkExceptionPresenter()
 363   
                 .presentStaleLinkException(cycle, exception);
 364   
     }
 365   
 
 366   
     /**
 367   
      * Invoked by {@link #service(WebRequest, WebResponse)} if a {@link StaleSessionException} is
 368   
      * thrown by the {@link IEngineService service}. This implementation uses the
 369   
      * {@link org.apache.tapestry.error.StaleSessionExceptionPresenter} to render the StaleSession
 370   
      * page.
 371   
      * <p>
 372   
      * Subclasses may overide this method (without invoking this implementation), but it is better
 373   
      * to override the tapestry.error.StaleSessionExceptionReporter service instead (or contribute a
 374   
      * replacement to the tapestry.InfrastructureOverrides configuration point).
 375   
      * 
 376   
      * @since 0.2.10
 377   
      */
 378   
 
 379  3
     protected void handleStaleSessionException(IRequestCycle cycle, StaleSessionException exception)
 380   
     {
 381  3
         _infrastructure.getStaleSessionExceptionPresenter().presentStaleSessionException(
 382   
                 cycle,
 383   
                 exception);
 384   
     }
 385   
 
 386   
     /**
 387   
      * Changes the locale for the engine.
 388   
      */
 389   
 
 390  67
     public void setLocale(Locale value)
 391   
     {
 392  67
         Defense.notNull(value, "locale");
 393   
 
 394  67
         _locale = value;
 395   
 
 396   
         // The locale may be set before the engine is initialized with the Infrastructure.
 397   
 
 398  67
         if (_infrastructure != null)
 399  12
             _infrastructure.setLocale(value);
 400   
     }
 401   
 
 402   
     /**
 403   
      * @see Infrastructure#getClassResolver()
 404   
      */
 405   
 
 406  64
     public ClassResolver getClassResolver()
 407   
     {
 408  64
         return _infrastructure.getClassResolver();
 409   
     }
 410   
 
 411   
     /**
 412   
      * Generates a description of the instance. Invokes {@link #extendDescription(ToStringBuilder)}
 413   
      * to fill in details about the instance.
 414   
      * 
 415   
      * @see #extendDescription(ToStringBuilder)
 416   
      */
 417   
 
 418  0
     public String toString()
 419   
     {
 420  0
         ToStringBuilder builder = new ToStringBuilder(this);
 421   
 
 422  0
         builder.append("locale", _locale);
 423   
 
 424  0
         return builder.toString();
 425   
     }
 426   
 
 427   
     /**
 428   
      * Gets the visit object from the
 429   
      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, creating it if it does not
 430   
      * already exist.
 431   
      * <p>
 432   
      * As of Tapestry 4.0, this will always create the visit object, possibly creating a new session
 433   
      * in the process.
 434   
      */
 435   
 
 436  3
     public Object getVisit()
 437   
     {
 438  3
         return _infrastructure.getApplicationStateManager().get("visit");
 439   
     }
 440   
 
 441  0
     public void setVisit(Object visit)
 442   
     {
 443  0
         _infrastructure.getApplicationStateManager().store("visit", visit);
 444   
     }
 445   
 
 446   
     /**
 447   
      * Gets the visit object from the
 448   
      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, which will create it as
 449   
      * necessary.
 450   
      */
 451   
 
 452  2
     public Object getVisit(IRequestCycle cycle)
 453   
     {
 454  2
         return getVisit();
 455   
     }
 456   
 
 457  0
     public boolean getHasVisit()
 458   
     {
 459  0
         return _infrastructure.getApplicationStateManager().exists("visit");
 460   
     }
 461   
 
 462   
     /**
 463   
      * Returns the global object for the application. The global object is created at the start of
 464   
      * the request ({@link #setupForRequest(RequestContext)}invokes
 465   
      * {@link #createGlobal(RequestContext)}if needed), and is stored into the
 466   
      * {@link ServletContext}. All instances of the engine for the application share the global
 467   
      * object; however, the global object is explicitly <em>not</em> replicated to other servers
 468   
      * within a cluster.
 469   
      * 
 470   
      * @since 2.3
 471   
      */
 472   
 
 473  1
     public Object getGlobal()
 474   
     {
 475  1
         return _infrastructure.getApplicationStateManager().get("global");
 476   
     }
 477   
 
 478  5
     public IScriptSource getScriptSource()
 479   
     {
 480  5
         return _infrastructure.getScriptSource();
 481   
     }
 482   
 
 483   
     /**
 484   
      * Allows subclasses to include listener methods easily.
 485   
      * 
 486   
      * @since 1.0.2
 487   
      */
 488   
 
 489  0
     public ListenerMap getListeners()
 490   
     {
 491  0
         if (_listeners == null)
 492  0
             _listeners = _infrastructure.getListenerMapSource().getListenerMapForObject(this);
 493   
 
 494  0
         return _listeners;
 495   
     }
 496   
 
 497   
     /**
 498   
      * Invoked when a {@link RedirectException} is thrown during the processing of a request.
 499   
      * 
 500   
      * @throws ApplicationRuntimeException
 501   
      *             if an {@link IOException},{@link ServletException}is thrown by the redirect,
 502   
      *             or if no {@link RequestDispatcher}can be found for local resource.
 503   
      * @since 2.2
 504   
      */
 505   
 
 506  0
     protected void handleRedirectException(IRequestCycle cycle, RedirectException redirectException)
 507   
     {
 508  0
         String location = redirectException.getRedirectLocation();
 509   
 
 510  0
         if (LOG.isDebugEnabled())
 511  0
             LOG.debug("Redirecting to: " + location);
 512   
 
 513  0
         _infrastructure.getRequest().forward(location);
 514   
     }
 515   
 
 516   
     /**
 517   
      * @see Infrastructure#getDataSqueezer()
 518   
      */
 519   
 
 520  0
     public DataSqueezer getDataSqueezer()
 521   
     {
 522  0
         return _infrastructure.getDataSqueezer();
 523   
     }
 524   
 
 525   
     /** @since 2.3 */
 526   
 
 527  0
     public IPropertySource getPropertySource()
 528   
     {
 529  0
         return _infrastructure.getApplicationPropertySource();
 530   
     }
 531   
 
 532   
     /** @since 4.0 */
 533  28
     public Infrastructure getInfrastructure()
 534   
     {
 535  28
         return _infrastructure;
 536   
     }
 537   
 
 538  376
     public String getOutputEncoding()
 539   
     {
 540  376
         return _infrastructure.getOutputEncoding();
 541   
     }
 542   
 }