Clover coverage report - Code Coverage for tapestry release 4.0-beta-10
Coverage timestamp: Sat Oct 8 2005 19:08:05 EDT
file stats: LOC: 711   Methods: 38
NCLOC: 374   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
RequestCycle.java 76.5% 90.2% 94.7% 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.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    */
 143   
 144  129 public RequestCycle(IEngine engine, QueryParameterMap parameters, String serviceName,
 145    IMonitor monitor, RequestCycleEnvironment environment)
 146    {
 147    // Variant from instance to instance
 148   
 149  129 _engine = engine;
 150  129 _parameters = parameters;
 151  129 _serviceName = serviceName;
 152  129 _monitor = monitor;
 153   
 154    // Invariant from instance to instance
 155   
 156  129 _infrastructure = environment.getInfrastructure();
 157  129 _pageSource = _infrastructure.getPageSource();
 158  129 _strategySource = environment.getStrategySource();
 159  129 _absoluteURLBuilder = environment.getAbsoluteURLBuilder();
 160  129 _requestContext = environment.getRequestContext();
 161  129 _log = new ErrorLogImpl(environment.getErrorHandler(), LOG);
 162   
 163    }
 164   
 165    /**
 166    * Alternate constructor used <strong>only for testing purposes</strong>.
 167    *
 168    * @since 4.0
 169    */
 170  2 public RequestCycle()
 171    {
 172    }
 173   
 174    /**
 175    * Called at the end of the request cycle (i.e., after all responses have been sent back to the
 176    * client), to release all pages loaded during the request cycle.
 177    */
 178   
 179  127 public void cleanup()
 180    {
 181  127 if (_loadedPages == null)
 182  1 return;
 183   
 184  126 Iterator i = _loadedPages.values().iterator();
 185   
 186  126 while (i.hasNext())
 187    {
 188  181 IPage page = (IPage) i.next();
 189   
 190  181 _pageSource.releasePage(page);
 191    }
 192   
 193  126 _loadedPages = null;
 194  126 _pageRecorders = null;
 195   
 196    }
 197   
 198  128 public IEngineService getService()
 199    {
 200  128 return _infrastructure.getServiceMap().getService(_serviceName);
 201    }
 202   
 203  120 public String encodeURL(String URL)
 204    {
 205  120 return _infrastructure.getResponse().encodeURL(URL);
 206    }
 207   
 208  681 public IEngine getEngine()
 209    {
 210  681 return _engine;
 211    }
 212   
 213  664 public Object getAttribute(String name)
 214    {
 215  664 return _attributes.get(name);
 216    }
 217   
 218  128 public IMonitor getMonitor()
 219    {
 220  128 return _monitor;
 221    }
 222   
 223  74 public String getNextActionId()
 224    {
 225  74 return Integer.toHexString(++_actionId);
 226    }
 227   
 228  332 public IPage getPage()
 229    {
 230  332 return _page;
 231    }
 232   
 233    /**
 234    * Gets the page from the engines's {@link IPageSource}.
 235    */
 236   
 237  195 public IPage getPage(String name)
 238    {
 239  195 Defense.notNull(name, "name");
 240   
 241  195 IPage result = null;
 242   
 243  195 if (_loadedPages != null)
 244  59 result = (IPage) _loadedPages.get(name);
 245   
 246  195 if (result == null)
 247    {
 248  191 result = loadPage(name);
 249   
 250  181 if (_loadedPages == null)
 251  126 _loadedPages = new HashMap();
 252   
 253  181 _loadedPages.put(name, result);
 254    }
 255   
 256  185 return result;
 257    }
 258   
 259  191 private IPage loadPage(String name)
 260    {
 261  191 try
 262    {
 263  191 _monitor.pageLoadBegin(name);
 264   
 265  191 IPage result = _pageSource.getPage(this, name, _monitor);
 266   
 267    // Get the recorder that will eventually observe and record
 268    // changes to persistent properties of the page.
 269   
 270  181 IPageRecorder recorder = getPageRecorder(name);
 271   
 272    // Have it rollback the page to the prior state. Note that
 273    // the page has a null observer at this time (which keeps
 274    // these changes from being sent to the page recorder).
 275   
 276  181 recorder.rollback(result);
 277   
 278    // Now, have the page use the recorder for any future
 279    // property changes.
 280   
 281  181 result.setChangeObserver(recorder);
 282   
 283    // Now that persistent properties have been restored, we can
 284    // attach the page to this request.
 285   
 286  181 result.attach(_engine, this);
 287   
 288  181 return result;
 289    }
 290    finally
 291    {
 292  191 _monitor.pageLoadEnd(name);
 293    }
 294   
 295    }
 296   
 297    /**
 298    * Returns the page recorder for the named page. Starting with Tapestry 4.0, page recorders are
 299    * shortlived objects managed exclusively by the request cycle.
 300    */
 301   
 302  181 protected IPageRecorder getPageRecorder(String name)
 303    {
 304  181 if (_pageRecorders == null)
 305  126 _pageRecorders = new HashMap();
 306   
 307  181 IPageRecorder result = (IPageRecorder) _pageRecorders.get(name);
 308   
 309  181 if (result == null)
 310    {
 311  181 result = new PageRecorderImpl(name, this, _strategySource, _log);
 312  181 _pageRecorders.put(name, result);
 313    }
 314   
 315  181 return result;
 316    }
 317   
 318  2095 public boolean isRewinding()
 319    {
 320  2095 return _rewinding;
 321    }
 322   
 323  74 public boolean isRewound(IComponent component) throws StaleLinkException
 324    {
 325    // If not rewinding ...
 326   
 327  74 if (!_rewinding)
 328  45 return false;
 329   
 330  29 if (_actionId != _targetActionId)
 331  3 return false;
 332   
 333    // OK, we're there, is the page is good order?
 334   
 335  26 if (component == _targetComponent)
 336  26 return true;
 337   
 338    // Woops. Mismatch.
 339   
 340  0 throw new StaleLinkException(component, Integer.toHexString(_targetActionId),
 341    _targetComponent.getExtendedId());
 342    }
 343   
 344  279 public void removeAttribute(String name)
 345    {
 346  279 if (LOG.isDebugEnabled())
 347  0 LOG.debug("Removing attribute " + name);
 348   
 349  279 _attributes.remove(name);
 350    }
 351   
 352    /**
 353    * Renders the page by invoking {@link IPage#renderPage(IMarkupWriter, IRequestCycle)}. This
 354    * clears all attributes.
 355    */
 356   
 357  136 public void renderPage(IMarkupWriter writer)
 358    {
 359  136 String pageName = _page.getPageName();
 360  136 _monitor.pageRenderBegin(pageName);
 361   
 362  136 _rewinding = false;
 363  136 _actionId = -1;
 364  136 _targetActionId = 0;
 365   
 366  136 try
 367    {
 368  136 _page.renderPage(writer, this);
 369   
 370    }
 371    catch (ApplicationRuntimeException ex)
 372    {
 373    // Nothing much to add here.
 374   
 375  10 throw ex;
 376    }
 377    catch (Throwable ex)
 378    {
 379    // But wrap other exceptions in a RequestCycleException ... this
 380    // will ensure that some of the context is available.
 381   
 382  0 throw new ApplicationRuntimeException(ex.getMessage(), _page, null, ex);
 383    }
 384    finally
 385    {
 386  136 reset();
 387    }
 388   
 389  126 _monitor.pageRenderEnd(pageName);
 390   
 391    }
 392   
 393    /**
 394    * Resets all internal state after a render or a rewind.
 395    */
 396   
 397  162 private void reset()
 398    {
 399  162 _actionId = 0;
 400  162 _targetActionId = 0;
 401  162 _attributes.clear();
 402  162 _idAllocator.clear();
 403    }
 404   
 405    /**
 406    * Rewinds an individual form by invoking {@link IForm#rewind(IMarkupWriter, IRequestCycle)}.
 407    * <p>
 408    * The process is expected to end with a {@link RenderRewoundException}. If the entire page is
 409    * renderred without this exception being thrown, it means that the target action id was not
 410    * valid, and a {@link ApplicationRuntimeException}&nbsp;is thrown.
 411    * <p>
 412    * This clears all attributes.
 413    *
 414    * @since 1.0.2
 415    */
 416   
 417  21 public void rewindForm(IForm form)
 418    {
 419  21 IPage page = form.getPage();
 420  21 String pageName = page.getPageName();
 421   
 422  21 _rewinding = true;
 423   
 424  21 _monitor.pageRewindBegin(pageName);
 425   
 426    // Fake things a little for getNextActionId() / isRewound()
 427    // This used to be more involved (and include service parameters, and a parameter
 428    // to this method), when the actionId was part of the Form name. That's not longer
 429    // necessary (no service parameters), and we can fake things here easily enough with
 430    // fixed actionId of 0.
 431   
 432  21 _targetActionId = 0;
 433  21 _actionId = -1;
 434   
 435  21 _targetComponent = form;
 436   
 437  21 try
 438    {
 439  21 page.beginPageRender();
 440   
 441  21 form.rewind(NullWriter.getSharedInstance(), this);
 442   
 443    // Shouldn't get this far, because the form should
 444    // throw the RenderRewoundException.
 445   
 446  0 throw new StaleLinkException(Tapestry.format("RequestCycle.form-rewind-failure", form
 447    .getExtendedId()), form);
 448    }
 449    catch (RenderRewoundException ex)
 450    {
 451    // This is acceptible and expected.
 452    }
 453    catch (ApplicationRuntimeException ex)
 454    {
 455    // RequestCycleExceptions don't need to be wrapped.
 456  2 throw ex;
 457    }
 458    catch (Throwable ex)
 459    {
 460    // But wrap other exceptions in a ApplicationRuntimeException ... this
 461    // will ensure that some of the context is available.
 462   
 463  0 throw new ApplicationRuntimeException(ex.getMessage(), page, null, ex);
 464    }
 465    finally
 466    {
 467  21 page.endPageRender();
 468   
 469  21 _monitor.pageRewindEnd(pageName);
 470   
 471  21 reset();
 472  21 _rewinding = false;
 473    }
 474    }
 475   
 476    /**
 477    * Rewinds the page by invoking {@link IPage#renderPage(IMarkupWriter, IRequestCycle)}.
 478    * <p>
 479    * The process is expected to end with a {@link RenderRewoundException}. If the entire page is
 480    * renderred without this exception being thrown, it means that the target action id was not
 481    * valid, and a {@link ApplicationRuntimeException}is thrown.
 482    * <p>
 483    * This clears all attributes.
 484    */
 485   
 486  5 public void rewindPage(String targetActionId, IComponent targetComponent)
 487    {
 488  5 String pageName = _page.getPageName();
 489   
 490  5 _rewinding = true;
 491   
 492  5 _monitor.pageRewindBegin(pageName);
 493   
 494  5 _actionId = -1;
 495   
 496    // Parse the action Id as hex since that's whats generated
 497    // by getNextActionId()
 498  5 _targetActionId = Integer.parseInt(targetActionId, 16);
 499  5 _targetComponent = targetComponent;
 500   
 501  5 try
 502    {
 503  5 _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  5 _monitor.pageRewindEnd(pageName);
 529   
 530  5 _rewinding = false;
 531   
 532  5 reset();
 533    }
 534   
 535    }
 536   
 537  374 public void setAttribute(String name, Object value)
 538    {
 539  374 if (LOG.isDebugEnabled())
 540  0 LOG.debug("Set attribute " + name + " to " + value);
 541   
 542  374 _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  136 public void commitPageChanges()
 551    {
 552  136 if (LOG.isDebugEnabled())
 553  0 LOG.debug("Committing page changes");
 554   
 555  136 if (_pageRecorders == null || _pageRecorders.isEmpty())
 556  0 return;
 557   
 558  136 Iterator i = _pageRecorders.values().iterator();
 559   
 560  136 while (i.hasNext())
 561    {
 562  191 IPageRecorder recorder = (IPageRecorder) i.next();
 563   
 564  191 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  1 public void discardPage(String name)
 575    {
 576  1 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  45 public Object[] getListenerParameters()
 595    {
 596  45 return _listenerParameters;
 597    }
 598   
 599    /** @since 4.0 */
 600  44 public void setListenerParameters(Object[] parameters)
 601    {
 602  44 _listenerParameters = parameters;
 603    }
 604   
 605    /** @since 3.0 * */
 606   
 607  95 public void activate(String name)
 608    {
 609  95 IPage page = getPage(name);
 610   
 611  85 activate(page);
 612    }
 613   
 614    /** @since 3.0 */
 615   
 616  179 public void activate(IPage page)
 617    {
 618  179 Defense.notNull(page, "page");
 619   
 620  179 if (LOG.isDebugEnabled())
 621  0 LOG.debug("Activating page " + page);
 622   
 623  179 Tapestry.clearMethodInvocations();
 624   
 625  179 page.validate(this);
 626   
 627  174 Tapestry
 628    .checkMethodInvocation(Tapestry.ABSTRACTPAGE_VALIDATE_METHOD_ID, "validate()", page);
 629   
 630  174 _page = page;
 631    }
 632   
 633    /** @since 4.0 */
 634  364 public String getParameter(String name)
 635    {
 636  364 return _parameters.getParameterValue(name);
 637    }
 638   
 639    /** @since 4.0 */
 640  54 public String[] getParameters(String name)
 641    {
 642  54 return _parameters.getParameterValues(name);
 643    }
 644   
 645    /**
 646    * @since 3.0
 647    */
 648  2 public String toString()
 649    {
 650  2 ToStringBuilder b = new ToStringBuilder(this);
 651   
 652  2 b.append("rewinding", _rewinding);
 653   
 654  2 b.append("serviceName", _serviceName);
 655   
 656  2 b.append("serviceParameters", _listenerParameters);
 657   
 658  2 if (_loadedPages != null)
 659  0 b.append("loadedPages", _loadedPages.keySet());
 660   
 661  2 b.append("attributes", _attributes);
 662  2 b.append("targetActionId", _targetActionId);
 663  2 b.append("targetComponent", _targetComponent);
 664   
 665  2 return b.toString();
 666    }
 667   
 668    /** @since 4.0 */
 669   
 670  112 public String getAbsoluteURL(String partialURL)
 671    {
 672  112 String contextPath = _infrastructure.getRequest().getContextPath();
 673   
 674  112 return _absoluteURLBuilder.constructURL(contextPath + partialURL);
 675    }
 676   
 677    /** @since 4.0 */
 678   
 679  2 public void forgetPage(String pageName)
 680    {
 681  2 Defense.notNull(pageName, "pageName");
 682   
 683  2 _strategySource.discardAllStoredChanged(pageName, this);
 684    }
 685   
 686    /** @since 4.0 */
 687   
 688  46 public Infrastructure getInfrastructure()
 689    {
 690  46 return _infrastructure;
 691    }
 692   
 693  1 public RequestContext getRequestContext()
 694    {
 695  1 return _requestContext;
 696    }
 697   
 698    /** @since 4.0 */
 699   
 700  49 public String getUniqueId(String baseId)
 701    {
 702  49 return _idAllocator.allocateId(baseId);
 703    }
 704   
 705    /** @since 4.0 */
 706  1 public void sendRedirect(String URL)
 707    {
 708  1 throw new RedirectException(URL);
 709    }
 710   
 711    }