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