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