KickJava   Java API By Example, From Geeks To Geeks.

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


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.io.IOException JavaDoc;
18 import java.util.ArrayList JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Locale JavaDoc;
21
22 import javax.servlet.RequestDispatcher JavaDoc;
23 import javax.servlet.ServletContext JavaDoc;
24 import javax.servlet.ServletException JavaDoc;
25
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hivemind.ApplicationRuntimeException;
30 import org.apache.hivemind.ClassResolver;
31 import org.apache.hivemind.util.Defense;
32 import org.apache.hivemind.util.ToStringBuilder;
33 import org.apache.tapestry.ApplicationServlet;
34 import org.apache.tapestry.Constants;
35 import org.apache.tapestry.IEngine;
36 import org.apache.tapestry.IPage;
37 import org.apache.tapestry.IRequestCycle;
38 import org.apache.tapestry.PageRedirectException;
39 import org.apache.tapestry.RedirectException;
40 import org.apache.tapestry.StaleLinkException;
41 import org.apache.tapestry.StaleSessionException;
42 import org.apache.tapestry.TapestryConstants;
43 import org.apache.tapestry.listener.ListenerMap;
44 import org.apache.tapestry.request.RequestContext;
45 import org.apache.tapestry.services.DataSqueezer;
46 import org.apache.tapestry.services.Infrastructure;
47 import org.apache.tapestry.spec.IApplicationSpecification;
48 import org.apache.tapestry.web.WebRequest;
49 import org.apache.tapestry.web.WebResponse;
50
51 /**
52  * Basis for building real Tapestry applications. Immediate subclasses provide different strategies
53  * for managing page state and other resources between request cycles.
54  * <p>
55  * Note: much of this description is <em>in transition</em> as part of Tapestry 4.0. All ad-hoc
56  * singletons and such are being replaced with HiveMind services.
57  * <p>
58  * Uses a shared instance of {@link ITemplateSource},{@link ISpecificationSource},
59  * {@link IScriptSource}and {@link IComponentMessagesSource}stored as attributes of the
60  * {@link ServletContext}(they will be shared by all sessions).
61  * <p>
62  * An engine is designed to be very lightweight. Particularily, it should <b>never </b> hold
63  * references to any {@link IPage}or {@link org.apache.tapestry.IComponent}objects. The entire
64  * system is based upon being able to quickly rebuild the state of any page(s).
65  * <p>
66  * Where possible, instance variables should be transient. They can be restored inside
67  * {@link #setupForRequest(RequestContext)}.
68  * <p>
69  * In practice, a subclass (usually {@link BaseEngine}) is used without subclassing. Instead, a
70  * visit object is specified. To facilitate this, the application specification may include a
71  * property, <code>org.apache.tapestry.visit-class</code> which is the class name to instantiate
72  * when a visit object is first needed. See {@link #createVisit(IRequestCycle)}for more details.
73  * <p>
74  * Some of the classes' behavior is controlled by JVM system properties (typically only used during
75  * development): <table border=1>
76  * <tr>
77  * <th>Property</th>
78  * <th>Description</th>
79  * </tr>
80  * <tr>
81  * <td>org.apache.tapestry.enable-reset-service</td>
82  * <td>If true, enabled an additional service, reset, that allow page, specification and template
83  * caches to be cleared on demand. See {@link #isResetServiceEnabled()}.</td>
84  * </tr>
85  * <tr>
86  * <td>org.apache.tapestry.disable-caching</td>
87  * <td>If true, then the page, specification, template and script caches will be cleared after each
88  * request. This slows things down, but ensures that the latest versions of such files are used.
89  * Care should be taken that the source directories for the files preceeds any versions of the files
90  * available in JARs or WARs.</td>
91  * </tr>
92  * </table>
93  *
94  * @author Howard Lewis Ship
95  */

96
97 public abstract class AbstractEngine implements IEngine
98 {
99     private static final Log LOG = LogFactory.getLog(AbstractEngine.class);
100
101     /**
102      * The link to the world of HiveMind services.
103      *
104      * @since 4.0
105      */

106     private Infrastructure _infrastructure;
107
108     private ListenerMap _listeners;
109
110     /**
111      * The curent locale for the engine, which may be changed at any time.
112      */

113
114     private Locale JavaDoc _locale;
115
116     /**
117      * The name of the application specification property used to specify the class of the visit
118      * object.
119      */

120
121     public static final String JavaDoc VISIT_CLASS_PROPERTY_NAME = "org.apache.tapestry.visit-class";
122
123     /**
124      * @see org.apache.tapestry.error.ExceptionPresenter
125      */

126
127     protected void activateExceptionPage(IRequestCycle cycle, Throwable JavaDoc cause)
128     {
129         _infrastructure.getExceptionPresenter().presentException(cycle, cause);
130     }
131
132     /**
133      * Writes a detailed report of the exception to <code>System.err</code>.
134      *
135      * @see org.apache.tapestry.error.RequestExceptionReporter
136      */

137
138     public void reportException(String JavaDoc reportTitle, Throwable JavaDoc ex)
139     {
140         _infrastructure.getRequestExceptionReporter().reportRequestException(reportTitle, ex);
141     }
142
143     /**
144      * Invoked at the end of the request cycle to release any resources specific to the request
145      * cycle. This implementation does nothing and may be overriden freely.
146      */

147
148     protected void cleanupAfterRequest(IRequestCycle cycle)
149     {
150
151     }
152
153     /**
154      * Returns the locale for the engine. This is initially set by the {@link ApplicationServlet}
155      * but may be updated by the application.
156      */

157
158     public Locale JavaDoc getLocale()
159     {
160         return _locale;
161     }
162
163     /**
164      * Returns a service with the given name.
165      *
166      * @see Infrastructure#getServiceMap()
167      * @see org.apache.tapestry.services.ServiceMap
168      */

169
170     public IEngineService getService(String JavaDoc name)
171     {
172         return _infrastructure.getServiceMap().getService(name);
173     }
174
175     /** @see Infrastructure#getApplicationSpecification() */
176
177     public IApplicationSpecification getSpecification()
178     {
179         return _infrastructure.getApplicationSpecification();
180     }
181
182     /** @see Infrastructure#getSpecificationSource() */
183
184     public ISpecificationSource getSpecificationSource()
185     {
186         return _infrastructure.getSpecificationSource();
187     }
188
189     /**
190      * Invoked, typically, when an exception occurs while servicing the request. This method resets
191      * the output, sets the new page and renders it.
192      */

193
194     protected void redirect(String JavaDoc pageName, IRequestCycle cycle,
195             ApplicationRuntimeException exception) throws IOException JavaDoc
196     {
197         IPage page = cycle.getPage(pageName);
198
199         cycle.activate(page);
200
201         renderResponse(cycle);
202     }
203
204     /**
205      * Delegates to
206      * {@link org.apache.tapestry.services.ResponseRenderer#renderResponse(IRequestCycle)}.
207      */

208
209     public void renderResponse(IRequestCycle cycle) throws IOException JavaDoc
210     {
211         _infrastructure.getResponseRenderer().renderResponse(cycle);
212     }
213
214     /**
215      * Delegate method for the servlet. Services the request.
216      */

217
218     public void service(WebRequest request, WebResponse response) throws IOException JavaDoc
219     {
220         IRequestCycle cycle = null;
221         IMonitor monitor = null;
222         IEngineService service = null;
223
224         if (_infrastructure == null)
225             _infrastructure = (Infrastructure) request.getAttribute(Constants.INFRASTRUCTURE_KEY);
226
227         try
228         {
229             try
230             {
231                 cycle = _infrastructure.getRequestCycleFactory().newRequestCycle(this);
232
233                 monitor = cycle.getMonitor();
234                 service = cycle.getService();
235
236                 monitor.serviceBegin(service.getName(), _infrastructure.getRequest()
237                         .getRequestURI());
238
239                 // Let the service handle the rest of the request.
240

241                 service.service(cycle);
242
243                 return;
244             }
245             catch (PageRedirectException ex)
246             {
247                 handlePageRedirectException(cycle, ex);
248             }
249             catch (RedirectException ex)
250             {
251                 handleRedirectException(cycle, ex);
252             }
253             catch (StaleLinkException ex)
254             {
255                 handleStaleLinkException(cycle, ex);
256             }
257             catch (StaleSessionException ex)
258             {
259                 handleStaleSessionException(cycle, ex);
260             }
261         }
262         catch (Exception JavaDoc ex)
263         {
264             monitor.serviceException(ex);
265
266             // Attempt to switch to the exception page. However, this may itself
267
// fail for a number of reasons, in which case an ApplicationRuntimeException is
268
// thrown.
269

270             if (LOG.isDebugEnabled())
271                 LOG.debug("Uncaught exception", ex);
272
273             activateExceptionPage(cycle, ex);
274         }
275         finally
276         {
277             if (service != null)
278                 monitor.serviceEnd(service.getName());
279
280             try
281             {
282                 cycle.cleanup();
283                 _infrastructure.getApplicationStateManager().flush();
284             }
285             catch (Exception JavaDoc ex)
286             {
287                 reportException(EngineMessages.exceptionDuringCleanup(ex), ex);
288             }
289         }
290     }
291
292     /**
293      * Handles {@link PageRedirectException} which involves executing
294      * {@link IRequestCycle#activate(IPage)} on the target page (of the exception), until either a
295      * loop is found, or a page succesfully activates.
296      * <p>
297      * This should generally not be overriden in subclasses.
298      *
299      * @since 3.0
300      */

301
302     protected void handlePageRedirectException(IRequestCycle cycle, PageRedirectException exception)
303             throws IOException JavaDoc
304     {
305         List JavaDoc pageNames = new ArrayList JavaDoc();
306
307         String JavaDoc pageName = exception.getTargetPageName();
308
309         while (true)
310         {
311             if (pageNames.contains(pageName))
312             {
313                 pageNames.add(pageName);
314
315                 throw new ApplicationRuntimeException(EngineMessages.validateCycle(pageNames));
316             }
317
318             // Record that this page has been a target.
319

320             pageNames.add(pageName);
321
322             try
323             {
324                 // Attempt to activate the new page.
325

326                 cycle.activate(pageName);
327
328                 break;
329             }
330             catch (PageRedirectException secondRedirectException)
331             {
332                 pageName = secondRedirectException.getTargetPageName();
333             }
334         }
335
336         renderResponse(cycle);
337     }
338
339     /**
340      * Invoked by {@link #service(WebRequest, WebResponse)} if a {@link StaleLinkException} is
341      * thrown by the {@link IEngineService service}. This implementation sets the message property
342      * of the StaleLink page to the message provided in the exception, then invokes
343      * {@link #redirect(String, IRequestCycle, ApplicationRuntimeException)} to render the StaleLink
344      * page.
345      * <p>
346      * Subclasses may overide this method (without invoking this implementation). A better practice
347      * is to contribute an alternative implementation of
348      * {@link org.apache.tapestry.error.StaleLinkExceptionPresenter} to the
349      * tapestry.InfrastructureOverrides configuration point.
350      * <p>
351      * A common practice is to present an error message on the application's Home page. Alternately,
352      * the application may provide its own version of the StaleLink page, overriding the framework's
353      * implementation (probably a good idea, because the default page hints at "application errors"
354      * and isn't localized). The overriding StaleLink implementation must implement a message
355      * property of type String.
356      *
357      * @since 0.2.10
358      */

359
360     protected void handleStaleLinkException(IRequestCycle cycle, StaleLinkException exception)
361             throws IOException JavaDoc
362     {
363         _infrastructure.getStaleLinkExceptionPresenter()
364                 .presentStaleLinkException(cycle, exception);
365     }
366
367     /**
368      * Invoked by {@link #service(WebRequest, WebResponse)} if a {@link StaleSessionException} is
369      * thrown by the {@link IEngineService service}. This implementation uses the
370      * {@link org.apache.tapestry.error.StaleSessionExceptionPresenter} to render the StaleSession
371      * page.
372      * <p>
373      * Subclasses may overide this method (without invoking this implementation), but it is better
374      * to override the tapestry.error.StaleSessionExceptionReporter service instead (or contribute a
375      * replacement to the tapestry.InfrastructureOverrides configuration point).
376      *
377      * @since 0.2.10
378      */

379
380     protected void handleStaleSessionException(IRequestCycle cycle, StaleSessionException exception)
381     {
382         _infrastructure.getStaleSessionExceptionPresenter().presentStaleSessionException(
383                 cycle,
384                 exception);
385     }
386
387     /**
388      * Changes the locale for the engine.
389      */

390
391     public void setLocale(Locale JavaDoc value)
392     {
393         Defense.notNull(value, "locale");
394
395         _locale = value;
396
397         // The locale may be set before the engine is initialized with the Infrastructure.
398

399         if (_infrastructure != null)
400             _infrastructure.setLocale(value);
401     }
402
403     /**
404      * @see Infrastructure#getClassResolver()
405      */

406
407     public ClassResolver getClassResolver()
408     {
409         return _infrastructure.getClassResolver();
410     }
411
412     /**
413      * Generates a description of the instance. Invokes {@link #extendDescription(ToStringBuilder)}
414      * to fill in details about the instance.
415      *
416      * @see #extendDescription(ToStringBuilder)
417      */

418
419     public String JavaDoc toString()
420     {
421         ToStringBuilder builder = new ToStringBuilder(this);
422
423         builder.append("locale", _locale);
424
425         return builder.toString();
426     }
427
428     /**
429      * Gets the visit object from the
430      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, creating it if it does not
431      * already exist.
432      * <p>
433      * As of Tapestry 4.0, this will always create the visit object, possibly creating a new session
434      * in the process.
435      */

436
437     public Object JavaDoc getVisit()
438     {
439         return _infrastructure.getApplicationStateManager().get("visit");
440     }
441
442     public void setVisit(Object JavaDoc visit)
443     {
444         _infrastructure.getApplicationStateManager().store("visit", visit);
445     }
446
447     /**
448      * Gets the visit object from the
449      * {@link org.apache.tapestry.engine.state.ApplicationStateManager}, which will create it as
450      * necessary.
451      */

452
453     public Object JavaDoc getVisit(IRequestCycle cycle)
454     {
455         return getVisit();
456     }
457
458     public boolean getHasVisit()
459     {
460         return _infrastructure.getApplicationStateManager().exists("visit");
461     }
462
463     /**
464      * Returns the global object for the application. The global object is created at the start of
465      * the request ({@link #setupForRequest(RequestContext)}invokes
466      * {@link #createGlobal(RequestContext)}if needed), and is stored into the
467      * {@link ServletContext}. All instances of the engine for the application share the global
468      * object; however, the global object is explicitly <em>not</em> replicated to other servers
469      * within a cluster.
470      *
471      * @since 2.3
472      */

473
474     public Object JavaDoc getGlobal()
475     {
476         return _infrastructure.getApplicationStateManager().get("global");
477     }
478
479     public IScriptSource getScriptSource()
480     {
481         return _infrastructure.getScriptSource();
482     }
483
484     /**
485      * Allows subclasses to include listener methods easily.
486      *
487      * @since 1.0.2
488      */

489
490     public ListenerMap getListeners()
491     {
492         if (_listeners == null)
493             _listeners = _infrastructure.getListenerMapSource().getListenerMapForObject(this);
494
495         return _listeners;
496     }
497
498     /**
499      * Invoked when a {@link RedirectException} is thrown during the processing of a request.
500      *
501      * @throws ApplicationRuntimeException
502      * if an {@link IOException},{@link ServletException}is thrown by the redirect,
503      * or if no {@link RequestDispatcher}can be found for local resource.
504      * @since 2.2
505      */

506
507     protected void handleRedirectException(IRequestCycle cycle, RedirectException redirectException)
508     {
509         String JavaDoc location = redirectException.getRedirectLocation();
510
511         if (LOG.isDebugEnabled())
512             LOG.debug("Redirecting to: " + location);
513
514         _infrastructure.getRequest().forward(location);
515     }
516
517     /**
518      * @see Infrastructure#getDataSqueezer()
519      */

520
521     public DataSqueezer getDataSqueezer()
522     {
523         return _infrastructure.getDataSqueezer();
524     }
525
526     /** @since 2.3 */
527
528     public IPropertySource getPropertySource()
529     {
530         return _infrastructure.getApplicationPropertySource();
531     }
532
533     /** @since 4.0 */
534     public Infrastructure getInfrastructure()
535     {
536         return _infrastructure;
537     }
538
539     public String JavaDoc getOutputEncoding()
540     {
541         return _infrastructure.getOutputEncoding();
542     }
543 }
Popular Tags