KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > engine > RequestCycle


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 JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.Map JavaDoc;
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 JavaDoc _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 JavaDoc _pageRecorders;
102
103     private boolean _rewinding = false;
104
105     private Map JavaDoc _attributes = new HashMap JavaDoc();
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 JavaDoc[] _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     public RequestCycle(IEngine engine, QueryParameterMap parameters, IEngineService service,
132             IMonitor monitor, RequestCycleEnvironment environment)
133     {
134         // Variant from instance to instance
135

136         _engine = engine;
137         _parameters = parameters;
138         _service = service;
139         _monitor = monitor;
140
141         // Invariant from instance to instance
142

143         _infrastructure = environment.getInfrastructure();
144         _pageSource = _infrastructure.getPageSource();
145         _strategySource = environment.getStrategySource();
146         _absoluteURLBuilder = environment.getAbsoluteURLBuilder();
147         _requestContext = environment.getRequestContext();
148         _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     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     public void cleanup()
167     {
168         if (_loadedPages == null)
169             return;
170
171         Iterator JavaDoc i = _loadedPages.values().iterator();
172
173         while (i.hasNext())
174         {
175             IPage page = (IPage) i.next();
176
177             _pageSource.releasePage(page);
178         }
179
180         _loadedPages = null;
181         _pageRecorders = null;
182
183     }
184
185     public IEngineService getService()
186     {
187         return _service;
188     }
189
190     public String JavaDoc encodeURL(String JavaDoc URL)
191     {
192         return _infrastructure.getResponse().encodeURL(URL);
193     }
194
195     public IEngine getEngine()
196     {
197         return _engine;
198     }
199
200     public Object JavaDoc getAttribute(String JavaDoc name)
201     {
202         return _attributes.get(name);
203     }
204
205     public IMonitor getMonitor()
206     {
207         return _monitor;
208     }
209
210     public String JavaDoc getNextActionId()
211     {
212         return Integer.toHexString(++_actionId);
213     }
214
215     public IPage getPage()
216     {
217         return _page;
218     }
219
220     /**
221      * Gets the page from the engines's {@link IPageSource}.
222      */

223
224     public IPage getPage(String JavaDoc name)
225     {
226         Defense.notNull(name, "name");
227
228         IPage result = null;
229
230         if (_loadedPages != null)
231             result = (IPage) _loadedPages.get(name);
232
233         if (result == null)
234         {
235             result = loadPage(name);
236
237             if (_loadedPages == null)
238                 _loadedPages = new HashMap JavaDoc();
239
240             _loadedPages.put(name, result);
241         }
242
243         return result;
244     }
245
246     private IPage loadPage(String JavaDoc name)
247     {
248         try
249         {
250             _monitor.pageLoadBegin(name);
251
252             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             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             recorder.rollback(result);
264
265             // Now, have the page use the recorder for any future
266
// property changes.
267

268             result.setChangeObserver(recorder);
269
270             // Now that persistent properties have been restored, we can
271
// attach the page to this request.
272

273             result.attach(_engine, this);
274
275             return result;
276         }
277         finally
278         {
279             _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     protected IPageRecorder getPageRecorder(String JavaDoc name)
290     {
291         if (_pageRecorders == null)
292             _pageRecorders = new HashMap JavaDoc();
293
294         IPageRecorder result = (IPageRecorder) _pageRecorders.get(name);
295
296         if (result == null)
297         {
298             result = new PageRecorderImpl(name, this, _strategySource, _log);
299             _pageRecorders.put(name, result);
300         }
301
302         return result;
303     }
304
305     public boolean isRewinding()
306     {
307         return _rewinding;
308     }
309
310     public boolean isRewound(IComponent component) throws StaleLinkException
311     {
312         // If not rewinding ...
313

314         if (!_rewinding)
315             return false;
316
317         if (_actionId != _targetActionId)
318             return false;
319
320         // OK, we're there, is the page is good order?
321

322         if (component == _targetComponent)
323             return true;
324
325         // Woops. Mismatch.
326

327         throw new StaleLinkException(component, Integer.toHexString(_targetActionId),
328                 _targetComponent.getExtendedId());
329     }
330
331     public void removeAttribute(String JavaDoc name)
332     {
333         if (LOG.isDebugEnabled())
334             LOG.debug("Removing attribute " + name);
335
336         _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     public void renderPage(IMarkupWriter writer)
345     {
346         String JavaDoc pageName = _page.getPageName();
347         _monitor.pageRenderBegin(pageName);
348
349         _rewinding = false;
350         _actionId = -1;
351         _targetActionId = 0;
352
353         try
354         {
355             _page.renderPage(writer, this);
356
357         }
358         catch (ApplicationRuntimeException ex)
359         {
360             // Nothing much to add here.
361

362             throw ex;
363         }
364         catch (Throwable JavaDoc ex)
365         {
366             // But wrap other exceptions in a RequestCycleException ... this
367
// will ensure that some of the context is available.
368

369             throw new ApplicationRuntimeException(ex.getMessage(), _page, null, ex);
370         }
371         finally
372         {
373             reset();
374         }
375
376         _monitor.pageRenderEnd(pageName);
377
378     }
379
380     /**
381      * Resets all internal state after a render or a rewind.
382      */

383
384     private void reset()
385     {
386         _actionId = 0;
387         _targetActionId = 0;
388         _attributes.clear();
389         _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     public void rewindForm(IForm form)
405     {
406         IPage page = form.getPage();
407         String JavaDoc pageName = page.getPageName();
408
409         _rewinding = true;
410
411         _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         _targetActionId = 0;
420         _actionId = -1;
421
422         _targetComponent = form;
423
424         try
425         {
426             page.beginPageRender();
427
428             form.rewind(NullWriter.getSharedInstance(), this);
429
430             // Shouldn't get this far, because the form should
431
// throw the RenderRewoundException.
432

433             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
throw ex;
444         }
445         catch (Throwable JavaDoc ex)
446         {
447             // But wrap other exceptions in a ApplicationRuntimeException ... this
448
// will ensure that some of the context is available.
449

450             throw new ApplicationRuntimeException(ex.getMessage(), page, null, ex);
451         }
452         finally
453         {
454             page.endPageRender();
455
456             _monitor.pageRewindEnd(pageName);
457
458             reset();
459             _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     public void rewindPage(String JavaDoc targetActionId, IComponent targetComponent)
474     {
475         String JavaDoc pageName = _page.getPageName();
476
477         _rewinding = true;
478
479         _monitor.pageRewindBegin(pageName);
480
481         _actionId = -1;
482
483         // Parse the action Id as hex since that's whats generated
484
// by getNextActionId()
485
_targetActionId = Integer.parseInt(targetActionId, 16);
486         _targetComponent = targetComponent;
487
488         try
489         {
490             _page.renderPage(NullWriter.getSharedInstance(), this);
491
492             // Shouldn't get this far, because the target component should
493
// throw the RenderRewoundException.
494

495             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
throw ex;
505         }
506         catch (Throwable JavaDoc ex)
507         {
508             // But wrap other exceptions in a RequestCycleException ... this
509
// will ensure that some of the context is available.
510

511             throw new ApplicationRuntimeException(ex.getMessage(), _page, null, ex);
512         }
513         finally
514         {
515             _monitor.pageRewindEnd(pageName);
516
517             _rewinding = false;
518
519             reset();
520         }
521
522     }
523
524     public void setAttribute(String JavaDoc name, Object JavaDoc value)
525     {
526         if (LOG.isDebugEnabled())
527             LOG.debug("Set attribute " + name + " to " + value);
528
529         _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     public void commitPageChanges()
538     {
539         if (LOG.isDebugEnabled())
540             LOG.debug("Committing page changes");
541
542         if (_pageRecorders == null || _pageRecorders.isEmpty())
543             return;
544
545         Iterator JavaDoc i = _pageRecorders.values().iterator();
546
547         while (i.hasNext())
548         {
549             IPageRecorder recorder = (IPageRecorder) i.next();
550
551             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     public void discardPage(String JavaDoc name)
562     {
563         forgetPage(name);
564     }
565
566     /** @since 2.0.3 * */
567
568     public Object JavaDoc[] getServiceParameters()
569     {
570         return getListenerParameters();
571     }
572
573     /** @since 2.0.3 * */
574
575     public void setServiceParameters(Object JavaDoc[] serviceParameters)
576     {
577         setListenerParameters(serviceParameters);
578     }
579
580     /** @since 4.0 */
581     public Object JavaDoc[] getListenerParameters()
582     {
583         return _listenerParameters;
584     }
585
586     /** @since 4.0 */
587     public void setListenerParameters(Object JavaDoc[] parameters)
588     {
589         _listenerParameters = parameters;
590     }
591
592     /** @since 3.0 * */
593
594     public void activate(String JavaDoc name)
595     {
596         IPage page = getPage(name);
597
598         activate(page);
599     }
600
601     /** @since 3.0 */
602
603     public void activate(IPage page)
604     {
605         Defense.notNull(page, "page");
606
607         if (LOG.isDebugEnabled())
608             LOG.debug("Activating page " + page);
609
610         Tapestry.clearMethodInvocations();
611
612         page.validate(this);
613
614         Tapestry
615                 .checkMethodInvocation(Tapestry.ABSTRACTPAGE_VALIDATE_METHOD_ID, "validate()", page);
616
617         _page = page;
618     }
619
620     /** @since 4.0 */
621     public String JavaDoc getParameter(String JavaDoc name)
622     {
623         return _parameters.getParameterValue(name);
624     }
625
626     /** @since 4.0 */
627     public String JavaDoc[] getParameters(String JavaDoc name)
628     {
629         return _parameters.getParameterValues(name);
630     }
631
632     /**
633      * @since 3.0
634      */

635     public String JavaDoc toString()
636     {
637         ToStringBuilder b = new ToStringBuilder(this);
638
639         b.append("rewinding", _rewinding);
640
641         if (_service != null)
642             b.append("service", _service);
643
644         b.append("serviceParameters", _listenerParameters);
645
646         if (_loadedPages != null)
647             b.append("loadedPages", _loadedPages.keySet());
648
649         b.append("attributes", _attributes);
650         b.append("targetActionId", _targetActionId);
651         b.append("targetComponent", _targetComponent);
652
653         return b.toString();
654     }
655
656     /** @since 4.0 */
657
658     public String JavaDoc getAbsoluteURL(String JavaDoc partialURL)
659     {
660         String JavaDoc contextPath = _infrastructure.getRequest().getContextPath();
661
662         return _absoluteURLBuilder.constructURL(contextPath + partialURL);
663     }
664
665     /** @since 4.0 */
666
667     public void forgetPage(String JavaDoc pageName)
668     {
669         Defense.notNull(pageName, "pageName");
670
671         _strategySource.discardAllStoredChanged(pageName, this);
672     }
673
674     /** @since 4.0 */
675
676     public Infrastructure getInfrastructure()
677     {
678         return _infrastructure;
679     }
680
681     public RequestContext getRequestContext()
682     {
683         return _requestContext;
684     }
685
686     /** @since 4.0 */
687
688     public String JavaDoc getUniqueId(String JavaDoc baseId)
689     {
690         return _idAllocator.allocateId(baseId);
691     }
692 }
Popular Tags