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: 692   Methods: 37
NCLOC: 370   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
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.lang.builder.ToStringBuilder;
 22   
 import org.apache.commons.logging.Log;
 23   
 import org.apache.commons.logging.LogFactory;
 24   
 import org.apache.hivemind.ApplicationRuntimeException;
 25   
 import org.apache.hivemind.ErrorLog;
 26   
 import org.apache.hivemind.impl.ErrorLogImpl;
 27   
 import org.apache.hivemind.util.Defense;
 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  150
     public RequestCycle(IEngine engine, QueryParameterMap parameters, IEngineService service,
 132   
             IMonitor monitor, RequestCycleEnvironment environment)
 133   
     {
 134   
         // Variant from instance to instance
 135   
 
 136  150
         _engine = engine;
 137  150
         _parameters = parameters;
 138  150
         _service = service;
 139  150
         _monitor = monitor;
 140   
 
 141   
         // Invariant from instance to instance
 142   
 
 143  150
         _infrastructure = environment.getInfrastructure();
 144  150
         _pageSource = _infrastructure.getPageSource();
 145  150
         _strategySource = environment.getStrategySource();
 146  150
         _absoluteURLBuilder = environment.getAbsoluteURLBuilder();
 147  150
         _requestContext = environment.getRequestContext();
 148  150
         _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  148
     public void cleanup()
 167   
     {
 168  148
         if (_loadedPages == null)
 169  1
             return;
 170   
 
 171  147
         Iterator i = _loadedPages.values().iterator();
 172   
 
 173  147
         while (i.hasNext())
 174   
         {
 175  212
             IPage page = (IPage) i.next();
 176   
 
 177  212
             _pageSource.releasePage(page);
 178   
         }
 179   
 
 180  147
         _loadedPages = null;
 181  147
         _pageRecorders = null;
 182   
 
 183   
     }
 184   
 
 185  149
     public IEngineService getService()
 186   
     {
 187  149
         return _service;
 188   
     }
 189   
 
 190  155
     public String encodeURL(String URL)
 191   
     {
 192  155
         return _infrastructure.getResponse().encodeURL(URL);
 193   
     }
 194   
 
 195  845
     public IEngine getEngine()
 196   
     {
 197  845
         return _engine;
 198   
     }
 199   
 
 200  653
     public Object getAttribute(String name)
 201   
     {
 202  653
         return _attributes.get(name);
 203   
     }
 204   
 
 205  149
     public IMonitor getMonitor()
 206   
     {
 207  149
         return _monitor;
 208   
     }
 209   
 
 210  99
     public String getNextActionId()
 211   
     {
 212  99
         return Integer.toHexString(++_actionId);
 213   
     }
 214   
 
 215  414
     public IPage getPage()
 216   
     {
 217  414
         return _page;
 218   
     }
 219   
 
 220   
     /**
 221   
      * Gets the page from the engines's {@link IPageSource}.
 222   
      */
 223   
 
 224  226
     public IPage getPage(String name)
 225   
     {
 226  226
         Defense.notNull(name, "name");
 227   
 
 228  226
         IPage result = null;
 229   
 
 230  226
         if (_loadedPages != null)
 231  69
             result = (IPage) _loadedPages.get(name);
 232   
 
 233  226
         if (result == null)
 234   
         {
 235  222
             result = loadPage(name);
 236   
 
 237  212
             if (_loadedPages == null)
 238  147
                 _loadedPages = new HashMap();
 239   
 
 240  212
             _loadedPages.put(name, result);
 241   
         }
 242   
 
 243  216
         return result;
 244   
     }
 245   
 
 246  222
     private IPage loadPage(String name)
 247   
     {
 248  222
         try
 249   
         {
 250  222
             _monitor.pageLoadBegin(name);
 251   
 
 252  222
             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  212
             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  212
             recorder.rollback(result);
 264   
 
 265   
             // Now, have the page use the recorder for any future
 266   
             // property changes.
 267   
 
 268  212
             result.setChangeObserver(recorder);
 269   
 
 270   
             // Now that persistent properties have been restored, we can
 271   
             // attach the page to this request.
 272   
 
 273  212
             result.attach(_engine, this);
 274   
 
 275  212
             return result;
 276   
         }
 277   
         finally
 278   
         {
 279  222
             _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  212
     protected IPageRecorder getPageRecorder(String name)
 290   
     {
 291  212
         if (_pageRecorders == null)
 292  147
             _pageRecorders = new HashMap();
 293   
 
 294  212
         IPageRecorder result = (IPageRecorder) _pageRecorders.get(name);
 295   
 
 296  212
         if (result == null)
 297   
         {
 298  212
             result = new PageRecorderImpl(name, this, _strategySource, _log);
 299  212
             _pageRecorders.put(name, result);
 300   
         }
 301   
 
 302  212
         return result;
 303   
     }
 304   
 
 305  2095
     public boolean isRewinding()
 306   
     {
 307  2095
         return _rewinding;
 308   
     }
 309   
 
 310  99
     public boolean isRewound(IComponent component) throws StaleLinkException
 311   
     {
 312   
         // If not rewinding ...
 313   
 
 314  99
         if (!_rewinding)
 315  58
             return false;
 316   
 
 317  41
         if (_actionId != _targetActionId)
 318  5
             return false;
 319   
 
 320   
         // OK, we're there, is the page is good order?
 321   
 
 322  36
         if (component == _targetComponent)
 323  36
             return true;
 324   
 
 325   
         // Woops. Mismatch.
 326   
 
 327  0
         throw new StaleLinkException(component, Integer.toHexString(_targetActionId),
 328   
                 _targetComponent.getExtendedId());
 329   
     }
 330   
 
 331  346
     public void removeAttribute(String name)
 332   
     {
 333  346
         if (LOG.isDebugEnabled())
 334  0
             LOG.debug("Removing attribute " + name);
 335   
 
 336  346
         _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  158
     public void renderPage(IMarkupWriter writer)
 345   
     {
 346  158
         String pageName = _page.getPageName();
 347  158
         _monitor.pageRenderBegin(pageName);
 348   
 
 349  158
         _rewinding = false;
 350  158
         _actionId = -1;
 351  158
         _targetActionId = 0;
 352   
 
 353  158
         try
 354   
         {
 355  158
             _page.renderPage(writer, this);
 356   
 
 357   
         }
 358   
         catch (ApplicationRuntimeException ex)
 359   
         {
 360   
             // Nothing much to add here.
 361   
 
 362  11
             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  158
             reset();
 374   
         }
 375   
 
 376  147
         _monitor.pageRenderEnd(pageName);
 377   
 
 378   
     }
 379   
 
 380   
     /**
 381   
      * Resets all internal state after a render or a rewind.
 382   
      */
 383   
 
 384  194
     private void reset()
 385   
     {
 386  194
         _actionId = 0;
 387  194
         _targetActionId = 0;
 388  194
         _attributes.clear();
 389  194
         _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  29
     public void rewindForm(IForm form)
 405   
     {
 406  29
         IPage page = form.getPage();
 407  29
         String pageName = page.getPageName();
 408   
 
 409  29
         _rewinding = true;
 410   
 
 411  29
         _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  29
         _targetActionId = 0;
 420  29
         _actionId = -1;
 421   
 
 422  29
         _targetComponent = form;
 423   
 
 424  29
         try
 425   
         {
 426  29
             page.beginPageRender();
 427   
 
 428  29
             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  29
             page.endPageRender();
 455   
 
 456  29
             _monitor.pageRewindEnd(pageName);
 457   
 
 458  29
             reset();
 459  29
             _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  7
     public void rewindPage(String targetActionId, IComponent targetComponent)
 474   
     {
 475  7
         String pageName = _page.getPageName();
 476   
 
 477  7
         _rewinding = true;
 478   
 
 479  7
         _monitor.pageRewindBegin(pageName);
 480   
 
 481  7
         _actionId = -1;
 482   
 
 483   
         // Parse the action Id as hex since that's whats generated
 484   
         // by getNextActionId()
 485  7
         _targetActionId = Integer.parseInt(targetActionId, 16);
 486  7
         _targetComponent = targetComponent;
 487   
 
 488  7
         try
 489   
         {
 490  7
             _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  7
             _monitor.pageRewindEnd(pageName);
 516   
 
 517  7
             _rewinding = false;
 518   
 
 519  7
             reset();
 520   
         }
 521   
 
 522   
     }
 523   
 
 524  358
     public void setAttribute(String name, Object value)
 525   
     {
 526  358
         if (LOG.isDebugEnabled())
 527  0
             LOG.debug("Set attribute " + name + " to " + value);
 528   
 
 529  358
         _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  158
     public void commitPageChanges()
 538   
     {
 539  158
         if (LOG.isDebugEnabled())
 540  0
             LOG.debug("Committing page changes");
 541   
 
 542  158
         if (_pageRecorders == null || _pageRecorders.isEmpty())
 543  0
             return;
 544   
 
 545  158
         Iterator i = _pageRecorders.values().iterator();
 546   
 
 547  158
         while (i.hasNext())
 548   
         {
 549  223
             IPageRecorder recorder = (IPageRecorder) i.next();
 550   
 
 551  223
             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  55
     public Object[] getListenerParameters()
 582   
     {
 583  55
         return _listenerParameters;
 584   
     }
 585   
 
 586   
     /** @since 4.0 */
 587  57
     public void setListenerParameters(Object[] parameters)
 588   
     {
 589  57
         _listenerParameters = parameters;
 590   
     }
 591   
 
 592   
     /** @since 3.0 * */
 593   
 
 594  101
     public void activate(String name)
 595   
     {
 596  101
         IPage page = getPage(name);
 597   
 
 598  91
         activate(page);
 599   
     }
 600   
 
 601   
     /** @since 3.0 */
 602   
 
 603  210
     public void activate(IPage page)
 604   
     {
 605  210
         Defense.notNull(page, "page");
 606   
 
 607  210
         if (LOG.isDebugEnabled())
 608  0
             LOG.debug("Activating page " + page);
 609   
 
 610  210
         Tapestry.clearMethodInvocations();
 611   
 
 612  210
         page.validate(this);
 613   
 
 614  205
         Tapestry
 615   
                 .checkMethodInvocation(Tapestry.ABSTRACTPAGE_VALIDATE_METHOD_ID, "validate()", page);
 616   
 
 617  205
         _page = page;
 618   
     }
 619   
 
 620   
     /** @since 4.0 */
 621  444
     public String getParameter(String name)
 622   
     {
 623  444
         return _parameters.getParameterValue(name);
 624   
     }
 625   
 
 626   
     /** @since 4.0 */
 627  63
     public String[] getParameters(String name)
 628   
     {
 629  63
         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  135
     public String getAbsoluteURL(String partialURL)
 659   
     {
 660  135
         String contextPath = _infrastructure.getRequest().getContextPath();
 661   
 
 662  135
         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  46
     public String getUniqueId(String baseId)
 689   
     {
 690  46
         return _idAllocator.allocateId(baseId);
 691   
     }
 692   
 }