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