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: 692   Methods: 37
NCLOC: 370   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
RequestCycle.java 75% 90.8% 94.6% 88.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.util.HashMap;
 18    import java.util.Iterator;
 19    import java.util.Map;
 20   
 21    import org.apache.commons.logging.Log;
 22    import org.apache.commons.logging.LogFactory;
 23    import org.apache.hivemind.ApplicationRuntimeException;
 24    import org.apache.hivemind.ErrorLog;
 25    import org.apache.hivemind.impl.ErrorLogImpl;
 26    import org.apache.hivemind.util.Defense;
 27    import org.apache.hivemind.util.ToStringBuilder;
 28    import org.apache.tapestry.IComponent;
 29    import org.apache.tapestry.IEngine;
 30    import org.apache.tapestry.IForm;
 31    import org.apache.tapestry.IMarkupWriter;
 32    import org.apache.tapestry.IPage;
 33    import org.apache.tapestry.IRequestCycle;
 34    import org.apache.tapestry.RenderRewoundException;
 35    import org.apache.tapestry.StaleLinkException;
 36    import org.apache.tapestry.Tapestry;
 37    import org.apache.tapestry.record.PageRecorderImpl;
 38    import org.apache.tapestry.record.PropertyPersistenceStrategySource;
 39    import org.apache.tapestry.request.RequestContext;
 40    import org.apache.tapestry.services.AbsoluteURLBuilder;
 41    import org.apache.tapestry.services.Infrastructure;
 42    import org.apache.tapestry.util.IdAllocator;
 43    import org.apache.tapestry.util.QueryParameterMap;
 44   
 45    /**
 46    * Provides the logic for processing a single request cycle. Provides access to the
 47    * {@link IEngine engine} and the {@link RequestContext}.
 48    *
 49    * @author Howard Lewis Ship
 50    */
 51   
 52    public class RequestCycle implements IRequestCycle
 53    {
 54    private static final Log LOG = LogFactory.getLog(RequestCycle.class);
 55   
 56    private IPage _page;
 57   
 58    private IEngine _engine;
 59   
 60    private IEngineService _service;
 61   
 62    private IMonitor _monitor;
 63   
 64    /** @since 4.0 */
 65   
 66    private PropertyPersistenceStrategySource _strategySource;
 67   
 68    /** @since 4.0 */
 69   
 70    private IPageSource _pageSource;
 71   
 72    /** @since 4.0 */
 73   
 74    private Infrastructure _infrastructure;
 75   
 76    /**
 77    * Contains parameters extracted from the request context, plus any decoded by any
 78    * {@link ServiceEncoder}s.
 79    *
 80    * @since 4.0
 81    */
 82   
 83    private QueryParameterMap _parameters;
 84   
 85    /** @since 4.0 */
 86   
 87    private AbsoluteURLBuilder _absoluteURLBuilder;
 88   
 89    /**
 90    * A mapping of pages loaded during the current request cycle. Key is the page name, value is
 91    * the {@link IPage}instance.
 92    */
 93   
 94    private Map _loadedPages;
 95   
 96    /**
 97    * A mapping of page recorders for the current request cycle. Key is the page name, value is the
 98    * {@link IPageRecorder}instance.
 99    */
 100   
 101    private Map _pageRecorders;
 102   
 103    private boolean _rewinding = false;
 104   
 105    private Map _attributes = new HashMap();
 106   
 107    private int _actionId;
 108   
 109    private int _targetActionId;
 110   
 111    private IComponent _targetComponent;
 112   
 113    /** @since 2.0.3 * */
 114   
 115    private Object[] _listenerParameters;
 116   
 117    /** @since 4.0 */
 118   
 119    private ErrorLog _log;
 120   
 121    private RequestContext _requestContext;
 122   
 123    /** @since 4.0 */
 124   
 125    private IdAllocator _idAllocator = new IdAllocator();
 126   
 127    /**
 128    * Standard constructor used to render a response page.
 129    */
 130   
 131  134 public RequestCycle(IEngine engine, QueryParameterMap parameters, IEngineService service,
 132    IMonitor monitor, RequestCycleEnvironment environment)
 133    {
 134    // Variant from instance to instance
 135   
 136  134 _engine = engine;
 137  134 _parameters = parameters;
 138  134 _service = service;
 139  134 _monitor = monitor;
 140   
 141    // Invariant from instance to instance
 142   
 143  134 _infrastructure = environment.getInfrastructure();
 144  134 _pageSource = _infrastructure.getPageSource();
 145  134 _strategySource = environment.getStrategySource();
 146  134 _absoluteURLBuilder = environment.getAbsoluteURLBuilder();
 147  134 _requestContext = environment.getRequestContext();
 148  134 _log = new ErrorLogImpl(environment.getErrorHandler(), LOG);
 149   
 150    }
 151   
 152    /**
 153    * Alternate constructor used <b>only for testing purposes </b>.
 154    *
 155    * @since 4.0
 156    */
 157  1 public RequestCycle()
 158    {
 159    }
 160   
 161    /**
 162    * Called at the end of the request cycle (i.e., after all responses have been sent back to the
 163    * client), to release all pages loaded during the request cycle.
 164    */
 165   
 166  132 public void cleanup()
 167    {
 168  132 if (_loadedPages == null)
 169  1 return;
 170   
 171  131 Iterator i = _loadedPages.values().iterator();
 172   
 173  131 while (i.hasNext())
 174    {
 175  188 IPage page = (IPage) i.next();
 176   
 177  188 _pageSource.releasePage(page);
 178    }
 179   
 180  131 _loadedPages = null;
 181  131 _pageRecorders = null;
 182   
 183    }
 184   
 185  133 public IEngineService getService()
 186    {
 187  133 return _service;
 188    }
 189   
 190  136 public String encodeURL(String URL)
 191    {
 192  136 return _infrastructure.getResponse().encodeURL(URL);
 193    }
 194   
 195  726 public IEngine getEngine()
 196    {
 197  726 return _engine;
 198    }
 199   
 200  625 public Object getAttribute(String name)
 201    {
 202  625 return _attributes.get(name);
 203    }
 204   
 205  133 public IMonitor getMonitor()
 206    {
 207  133 return _monitor;
 208    }
 209   
 210  72 public String getNextActionId()
 211    {
 212  72 return Integer.toHexString(++_actionId);
 213    }
 214   
 215  361 public IPage getPage()
 216    {
 217  361 return _page;
 218    }
 219   
 220    /**
 221    * Gets the page from the engines's {@link IPageSource}.
 222    */
 223   
 224  202 public IPage getPage(String name)
 225    {
 226  202 Defense.notNull(name, "name");
 227   
 228  202 IPage result = null;
 229   
 230  202 if (_loadedPages != null)
 231  61 result = (IPage) _loadedPages.get(name);
 232   
 233  202 if (result == null)
 234    {
 235  198 result = loadPage(name);
 236   
 237  188 if (_loadedPages == null)
 238  131 _loadedPages = new HashMap();
 239   
 240  188 _loadedPages.put(name, result);
 241    }
 242   
 243  192 return result;
 244    }
 245   
 246  198 private IPage loadPage(String name)
 247    {
 248  198 try
 249    {
 250  198 _monitor.pageLoadBegin(name);
 251   
 252  198 IPage result = _pageSource.getPage(this, name, _monitor);
 253   
 254    // Get the recorder that will eventually observe and record
 255    // changes to persistent properties of the page.
 256   
 257  188 IPageRecorder recorder = getPageRecorder(name);
 258   
 259    // Have it rollback the page to the prior state. Note that
 260    // the page has a null observer at this time (which keeps
 261    // these changes from being sent to the page recorder).
 262   
 263  188 recorder.rollback(result);
 264   
 265    // Now, have the page use the recorder for any future
 266    // property changes.
 267   
 268  188 result.setChangeObserver(recorder);
 269   
 270    // Now that persistent properties have been restored, we can
 271    // attach the page to this request.
 272   
 273  188 result.attach(_engine, this);
 274   
 275  188 return result;
 276    }
 277    finally
 278    {
 279  198 _monitor.pageLoadEnd(name);
 280    }
 281   
 282    }
 283   
 284    /**
 285    * Returns the page recorder for the named page. Starting with Tapestry 4.0, page recorders are
 286    * shortlived objects managed exclusively by the request cycle.
 287    */
 288   
 289  188 protected IPageRecorder getPageRecorder(String name)
 290    {
 291  188 if (_pageRecorders == null)
 292  131 _pageRecorders = new HashMap();
 293   
 294  188 IPageRecorder result = (IPageRecorder) _pageRecorders.get(name);
 295   
 296  188 if (result == null)
 297    {
 298  188 result = new PageRecorderImpl(name, this, _strategySource, _log);
 299  188 _pageRecorders.put(name, result);
 300    }
 301   
 302  188 return result;
 303    }
 304   
 305  2034 public boolean isRewinding()
 306    {
 307  2034 return _rewinding;
 308    }
 309   
 310  72 public boolean isRewound(IComponent component) throws StaleLinkException
 311    {
 312    // If not rewinding ...
 313   
 314  72 if (!_rewinding)
 315  44 return false;
 316   
 317  28 if (_actionId != _targetActionId)
 318  3 return false;
 319   
 320    // OK, we're there, is the page is good order?
 321   
 322  25 if (component == _targetComponent)
 323  25 return true;
 324   
 325    // Woops. Mismatch.
 326   
 327  0 throw new StaleLinkException(component, Integer.toHexString(_targetActionId),
 328    _targetComponent.getExtendedId());
 329    }
 330   
 331  283 public void removeAttribute(String name)
 332    {
 333  283 if (LOG.isDebugEnabled())
 334  0 LOG.debug("Removing attribute " + name);
 335   
 336  283 _attributes.remove(name);
 337    }
 338   
 339    /**
 340    * Renders the page by invoking {@link IPage#renderPage(IMarkupWriter, IRequestCycle)}. This
 341    * clears all attributes.
 342    */
 343   
 344  141 public void renderPage(IMarkupWriter writer)
 345    {
 346  141 String pageName = _page.getPageName();
 347  141 _monitor.pageRenderBegin(pageName);
 348   
 349  141 _rewinding = false;
 350  141 _actionId = -1;
 351  141 _targetActionId = 0;
 352   
 353  141 try
 354    {
 355  141 _page.renderPage(writer, this);
 356   
 357    }
 358    catch (ApplicationRuntimeException ex)
 359    {
 360    // Nothing much to add here.
 361   
 362  10 throw ex;
 363    }
 364    catch (Throwable ex)
 365    {
 366    // But wrap other exceptions in a RequestCycleException ... this
 367    // will ensure that some of the context is available.
 368   
 369  0 throw new ApplicationRuntimeException(ex.getMessage(), _page, null, ex);
 370    }
 371    finally
 372    {
 373  141 reset();
 374    }
 375   
 376  131 _monitor.pageRenderEnd(pageName);
 377   
 378    }
 379   
 380    /**
 381    * Resets all internal state after a render or a rewind.
 382    */
 383   
 384  166 private void reset()
 385    {
 386  166 _actionId = 0;
 387  166 _targetActionId = 0;
 388  166 _attributes.clear();
 389  166 _idAllocator.clear();
 390    }
 391   
 392    /**
 393    * Rewinds an individual form by invoking {@link IForm#rewind(IMarkupWriter, IRequestCycle)}.
 394    * <p>
 395    * The process is expected to end with a {@link RenderRewoundException}. If the entire page is
 396    * renderred without this exception being thrown, it means that the target action id was not
 397    * valid, and a {@link ApplicationRuntimeException}&nbsp;is thrown.
 398    * <p>
 399    * This clears all attributes.
 400    *
 401    * @since 1.0.2
 402    */
 403   
 404  20 public void rewindForm(IForm form)
 405    {
 406  20 IPage page = form.getPage();
 407  20 String pageName = page.getPageName();
 408   
 409  20 _rewinding = true;
 410   
 411  20 _monitor.pageRewindBegin(pageName);
 412   
 413    // Fake things a little for getNextActionId() / isRewound()
 414    // This used to be more involved (and include service parameters, and a parameter
 415    // to this method), when the actionId was part of the Form name. That's not longer
 416    // necessary (no service parameters), and we can fake things here easily enough with
 417    // fixed actionId of 0.
 418   
 419  20 _targetActionId = 0;
 420  20 _actionId = -1;
 421   
 422  20 _targetComponent = form;
 423   
 424  20 try
 425    {
 426  20 page.beginPageRender();
 427   
 428  20 form.rewind(NullWriter.getSharedInstance(), this);
 429   
 430    // Shouldn't get this far, because the form should
 431    // throw the RenderRewoundException.
 432   
 433  0 throw new StaleLinkException(Tapestry.format("RequestCycle.form-rewind-failure", form
 434    .getExtendedId()), form);
 435    }
 436    catch (RenderRewoundException ex)
 437    {
 438    // This is acceptible and expected.
 439    }
 440    catch (ApplicationRuntimeException ex)
 441    {
 442    // RequestCycleExceptions don't need to be wrapped.
 443  1 throw ex;
 444    }
 445    catch (Throwable ex)
 446    {
 447    // But wrap other exceptions in a ApplicationRuntimeException ... this
 448    // will ensure that some of the context is available.
 449   
 450  1 throw new ApplicationRuntimeException(ex.getMessage(), page, null, ex);
 451    }
 452    finally
 453    {
 454  20 page.endPageRender();
 455   
 456  20 _monitor.pageRewindEnd(pageName);
 457   
 458  20 reset();
 459  20 _rewinding = false;
 460    }
 461    }
 462   
 463    /**
 464    * Rewinds the page by invoking {@link IPage#renderPage(IMarkupWriter, IRequestCycle)}.
 465    * <p>
 466    * The process is expected to end with a {@link RenderRewoundException}. If the entire page is
 467    * renderred without this exception being thrown, it means that the target action id was not
 468    * valid, and a {@link ApplicationRuntimeException}is thrown.
 469    * <p>
 470    * This clears all attributes.
 471    */
 472   
 473  5 public void rewindPage(String targetActionId, IComponent targetComponent)
 474    {
 475  5 String pageName = _page.getPageName();
 476   
 477  5 _rewinding = true;
 478   
 479  5 _monitor.pageRewindBegin(pageName);
 480   
 481  5 _actionId = -1;
 482   
 483    // Parse the action Id as hex since that's whats generated
 484    // by getNextActionId()
 485  5 _targetActionId = Integer.parseInt(targetActionId, 16);
 486  5 _targetComponent = targetComponent;
 487   
 488  5 try
 489    {
 490  5 _page.renderPage(NullWriter.getSharedInstance(), this);
 491   
 492    // Shouldn't get this far, because the target component should
 493    // throw the RenderRewoundException.
 494   
 495  0 throw new StaleLinkException(_page, targetActionId, targetComponent.getExtendedId());
 496    }
 497    catch (RenderRewoundException ex)
 498    {
 499    // This is acceptible and expected.
 500    }
 501    catch (ApplicationRuntimeException ex)
 502    {
 503    // ApplicationRuntimeExceptions don't need to be wrapped.
 504  0 throw ex;
 505    }
 506    catch (Throwable ex)
 507    {
 508    // But wrap other exceptions in a RequestCycleException ... this
 509    // will ensure that some of the context is available.
 510   
 511  0 throw new ApplicationRuntimeException(ex.getMessage(), _page, null, ex);
 512    }
 513    finally
 514    {
 515  5 _monitor.pageRewindEnd(pageName);
 516   
 517  5 _rewinding = false;
 518   
 519  5 reset();
 520    }
 521   
 522    }
 523   
 524  391 public void setAttribute(String name, Object value)
 525    {
 526  391 if (LOG.isDebugEnabled())
 527  0 LOG.debug("Set attribute " + name + " to " + value);
 528   
 529  391 _attributes.put(name, value);
 530    }
 531   
 532    /**
 533    * Invokes {@link IPageRecorder#commit()}on each page recorder loaded during the request cycle
 534    * (even recorders marked for discard).
 535    */
 536   
 537  141 public void commitPageChanges()
 538    {
 539  141 if (LOG.isDebugEnabled())
 540  0 LOG.debug("Committing page changes");
 541   
 542  141 if (_pageRecorders == null || _pageRecorders.isEmpty())
 543  0 return;
 544   
 545  141 Iterator i = _pageRecorders.values().iterator();
 546   
 547  141 while (i.hasNext())
 548    {
 549  198 IPageRecorder recorder = (IPageRecorder) i.next();
 550   
 551  198 recorder.commit();
 552    }
 553    }
 554   
 555    /**
 556    * As of 4.0, just a synonym for {@link #forgetPage(String)}.
 557    *
 558    * @since 2.0.2
 559    */
 560   
 561  1 public void discardPage(String name)
 562    {
 563  1 forgetPage(name);
 564    }
 565   
 566    /** @since 2.0.3 * */
 567   
 568  0 public Object[] getServiceParameters()
 569    {
 570  0 return getListenerParameters();
 571    }
 572   
 573    /** @since 2.0.3 * */
 574   
 575  0 public void setServiceParameters(Object[] serviceParameters)
 576    {
 577  0 setListenerParameters(serviceParameters);
 578    }
 579   
 580    /** @since 4.0 */
 581  45 public Object[] getListenerParameters()
 582    {
 583  45 return _listenerParameters;
 584    }
 585   
 586    /** @since 4.0 */
 587  47 public void setListenerParameters(Object[] parameters)
 588    {
 589  47 _listenerParameters = parameters;
 590    }
 591   
 592    /** @since 3.0 * */
 593   
 594  97 public void activate(String name)
 595    {
 596  97 IPage page = getPage(name);
 597   
 598  87 activate(page);
 599    }
 600   
 601    /** @since 3.0 */
 602   
 603  186 public void activate(IPage page)
 604    {
 605  186 Defense.notNull(page, "page");
 606   
 607  186 if (LOG.isDebugEnabled())
 608  0 LOG.debug("Activating page " + page);
 609   
 610  186 Tapestry.clearMethodInvocations();
 611   
 612  186 page.validate(this);
 613   
 614  181 Tapestry
 615    .checkMethodInvocation(Tapestry.ABSTRACTPAGE_VALIDATE_METHOD_ID, "validate()", page);
 616   
 617  181 _page = page;
 618    }
 619   
 620    /** @since 4.0 */
 621  386 public String getParameter(String name)
 622    {
 623  386 return _parameters.getParameterValue(name);
 624    }
 625   
 626    /** @since 4.0 */
 627  52 public String[] getParameters(String name)
 628    {
 629  52 return _parameters.getParameterValues(name);
 630    }
 631   
 632    /**
 633    * @since 3.0
 634    */
 635  2 public String toString()
 636    {
 637  2 ToStringBuilder b = new ToStringBuilder(this);
 638   
 639  2 b.append("rewinding", _rewinding);
 640   
 641  2 if (_service != null)
 642  2 b.append("service", _service);
 643   
 644  2 b.append("serviceParameters", _listenerParameters);
 645   
 646  2 if (_loadedPages != null)
 647  0 b.append("loadedPages", _loadedPages.keySet());
 648   
 649  2 b.append("attributes", _attributes);
 650  2 b.append("targetActionId", _targetActionId);
 651  2 b.append("targetComponent", _targetComponent);
 652   
 653  2 return b.toString();
 654    }
 655   
 656    /** @since 4.0 */
 657   
 658  118 public String getAbsoluteURL(String partialURL)
 659    {
 660  118 String contextPath = _infrastructure.getRequest().getContextPath();
 661   
 662  118 return _absoluteURLBuilder.constructURL(contextPath + partialURL);
 663    }
 664   
 665    /** @since 4.0 */
 666   
 667  2 public void forgetPage(String pageName)
 668    {
 669  2 Defense.notNull(pageName, "pageName");
 670   
 671  2 _strategySource.discardAllStoredChanged(pageName, this);
 672    }
 673   
 674    /** @since 4.0 */
 675   
 676  1 public Infrastructure getInfrastructure()
 677    {
 678  1 return _infrastructure;
 679    }
 680   
 681  1 public RequestContext getRequestContext()
 682    {
 683  1 return _requestContext;
 684    }
 685   
 686    /** @since 4.0 */
 687   
 688  48 public String getUniqueId(String baseId)
 689    {
 690  48 return _idAllocator.allocateId(baseId);
 691    }
 692    }