Source code: org/apache/tapestry/engine/AbstractEngine.java
1 /* $$ Clover has instrumented this file $$ */// Copyright 2004 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.Externalizable;
18 import java.io.IOException;
19 import java.io.ObjectInput;
20 import java.io.ObjectOutput;
21 import java.io.UnsupportedEncodingException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Map;
30
31 import javax.servlet.RequestDispatcher;
32 import javax.servlet.ServletContext;
33 import javax.servlet.ServletException;
34 import javax.servlet.http.HttpServlet;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import javax.servlet.http.HttpSession;
38 import javax.servlet.http.HttpSessionBindingEvent;
39 import javax.servlet.http.HttpSessionBindingListener;
40
41 import org.apache.commons.lang.builder.ToStringBuilder;
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44 import org.apache.hivemind.ApplicationRuntimeException;
45 import org.apache.hivemind.ClassResolver;
46 import org.apache.tapestry.ApplicationServlet;
47 import org.apache.tapestry.Constants;
48 import org.apache.tapestry.IEngine;
49 import org.apache.tapestry.IMarkupWriter;
50 import org.apache.tapestry.INamespace;
51 import org.apache.tapestry.IPage;
52 import org.apache.tapestry.IRequestCycle;
53 import org.apache.tapestry.PageRedirectException;
54 import org.apache.tapestry.RedirectException;
55 import org.apache.tapestry.StaleLinkException;
56 import org.apache.tapestry.StaleSessionException;
57 import org.apache.tapestry.Tapestry;
58 import org.apache.tapestry.listener.ListenerMap;
59 import org.apache.tapestry.request.RequestContext;
60 import org.apache.tapestry.request.ResponseOutputStream;
61 import org.apache.tapestry.services.ComponentMessagesSource;
62 import org.apache.tapestry.services.DataSqueezer;
63 import org.apache.tapestry.services.Infrastructure;
64 import org.apache.tapestry.services.ObjectPool;
65 import org.apache.tapestry.services.TemplateSource;
66 import org.apache.tapestry.services.impl.ComponentMessagesSourceImpl;
67 import org.apache.tapestry.spec.IApplicationSpecification;
68 import org.apache.tapestry.util.exception.ExceptionAnalyzer;
69 import org.apache.tapestry.util.io.DataSqueezerImpl;
70
71 import com.sun.jndi.ldap.pool.Pool;
72
73 /**
74 * Basis for building real Tapestry applications. Immediate subclasses provide different strategies
75 * for managing page state and other resources between request cycles.
76 * <p>
77 * Note: much of this description is <em>in transition</em> as part of Tapestry 3.1. All ad-hoc
78 * singletons and such are being replaced with HiveMind services.
79 * <p>
80 * Uses a shared instance of {@link ITemplateSource},{@link ISpecificationSource},
81 * {@link IScriptSource}and {@link IComponentMessagesSource}stored as attributes of the
82 * {@link ServletContext}(they will be shared by all sessions).
83 * <p>
84 * An engine is designed to be very lightweight. Particularily, it should <b>never </b> hold
85 * references to any {@link IPage}or {@link org.apache.tapestry.IComponent}objects. The entire
86 * system is based upon being able to quickly rebuild the state of any page(s).
87 * <p>
88 * Where possible, instance variables should be transient. They can be restored inside
89 * {@link #setupForRequest(RequestContext)}.
90 * <p>
91 * In practice, a subclass (usually {@link BaseEngine}) is used without subclassing. Instead, a
92 * visit object is specified. To facilitate this, the application specification may include a
93 * property, <code>org.apache.tapestry.visit-class</code> which is the class name to instantiate
94 * when a visit object is first needed. See {@link #createVisit(IRequestCycle)}for more details.
95 * <p>
96 * Some of the classes' behavior is controlled by JVM system properties (typically only used during
97 * development): <table border=1>
98 * <tr>
99 * <th>Property</th>
100 * <th>Description</th>
101 * </tr>
102 * <tr>
103 * <td>org.apache.tapestry.enable-reset-service</td>
104 * <td>If true, enabled an additional service, reset, that allow page, specification and template
105 * caches to be cleared on demand. See {@link #isResetServiceEnabled()}.</td>
106 * </tr>
107 * <tr>
108 * <td>org.apache.tapestry.disable-caching</td>
109 * <td>If true, then the page, specification, template and script caches will be cleared after each
110 * request. This slows things down, but ensures that the latest versions of such files are used.
111 * Care should be taken that the source directories for the files preceeds any versions of the files
112 * available in JARs or WARs.</td>
113 * </tr>
114 * </table>
115 *
116 * @author Howard Lewis Ship
117 */
118
119 public abstract class AbstractEngine implements IEngine, IEngineServiceView, Externalizable,
120 HttpSessionBindingListener
121 {public static com.cortexeb.tools.clover.d __CLOVER_71_0 = com.cortexeb.tools.clover.aq.getRecorder(new char[] {67,58,92,119,111,114,107,115,112,97,99,101,92,106,97,107,97,114,116,97,45,116,97,112,101,115,116,114,121,92,102,114,97,109,101,119,111,114,107,92,116,97,114,103,101,116,92,99,108,111,118,101,114,45,100,98},1096998272901L);
122 private static final Log LOG = LogFactory.getLog(AbstractEngine.class);
123
124 /**
125 * @since 2.0.4
126 */
127
128 private static final long serialVersionUID = 6884834397673817117L;
129
130 /**
131 * The link to the world of HiveMind services.
132 *
133 * @since 3.1
134 */
135 private transient Infrastructure _infrastructure;
136
137 private transient String _contextPath;
138
139 private transient String _servletPath;
140
141 private transient String _clientAddress;
142
143 private transient String _sessionId;
144
145 private transient boolean _stateful;
146
147 private transient ListenerMap _listeners;
148
149 /**
150 * An object used to contain application-specific server side state.
151 */
152
153 private Object _visit;
154
155 /**
156 * The globally shared application object. Typically, this is created when first needed, shared
157 * between sessions and engines, and stored in the {@link ServletContext}.
158 *
159 * @since 2.3
160 */
161
162 private transient Object _global;
163
164 /**
165 * The base name for the servlet context key used to store the application-defined Global
166 * object, if any.
167 *
168 * @since 2.3
169 */
170
171 public static final String GLOBAL_NAME = "org.apache.tapestry.global";
172
173 /**
174 * The name of the application property that will be used to determine the encoding to use when
175 * generating the output
176 *
177 * @since 3.0
178 */
179
180 public static final String OUTPUT_ENCODING_PROPERTY_NAME = "org.apache.tapestry.output-encoding";
181
182 /**
183 * The default encoding that will be used when generating the output. It is used if no output
184 * encoding property has been specified.
185 *
186 * @since 3.0
187 */
188
189 public static final String DEFAULT_OUTPUT_ENCODING = "UTF-8";
190
191 /**
192 * The curent locale for the engine, which may be changed at any time.
193 */
194
195 private Locale _locale;
196
197 /**
198 * Set by {@link #setLocale(Locale)}when the locale is changed; this allows the locale cookie
199 * to be updated.
200 */
201
202 private boolean _localeChanged;
203
204 /**
205 * The name of the application specification property used to specify the class of the visit
206 * object.
207 */
208
209 public static final String VISIT_CLASS_PROPERTY_NAME = "org.apache.tapestry.visit-class";
210
211 /**
212 * If true (set from JVM system parameter <code>org.apache.tapestry.enable-reset-service</code>)
213 * then the reset service will be enabled, allowing the cache of pages, specifications and
214 * template to be cleared on demand.
215 */
216
217 private static final boolean _resetServiceEnabled = Boolean
218 .getBoolean("org.apache.tapestry.enable-reset-service");
219
220 /**
221 * If true (set from the JVM system parameter <code>org.apache.tapestry.disable-caching</code>)
222 * then the cache of pages, specifications and template will be cleared after each request.
223 */
224
225 private static final boolean _disableCaching = Boolean
226 .getBoolean("org.apache.tapestry.disable-caching");
227
228 /**
229 * Set to true when there is a (potential) change to the internal state of the engine, set to
230 * false when the engine is stored into the {@link HttpSession}.
231 *
232 * @since 3.0
233 */
234
235 private transient boolean _dirty;
236
237 /**
238 * The instance of {@link IMonitorFactory}used to create a monitor.
239 *
240 * @since 3.0
241 */
242
243 private transient IMonitorFactory _monitorFactory;
244
245 /**
246 * Sets the Exception page's exception property, then renders the Exception page.
247 * <p>
248 * If the render throws an exception, then copious output is sent to <code>System.err</code>
249 * and a {@link ServletException}is thrown.
250 */
251
252 protected void activateExceptionPage(IRequestCycle cycle, ResponseOutputStream output,
253 Throwable cause) throws ServletException
254 {try { __CLOVER_71_0.M[373]++;
255 __CLOVER_71_0.S[1540]++;try
256 {
257 __CLOVER_71_0.S[1541]++;IPage exceptionPage = cycle.getPage(getExceptionPageName());
258
259 __CLOVER_71_0.S[1542]++;exceptionPage.setProperty("exception", cause);
260
261 __CLOVER_71_0.S[1543]++;cycle.activate(exceptionPage);
262
263 __CLOVER_71_0.S[1544]++;renderResponse(cycle, output);
264
265 }
266 catch (Throwable ex)
267 {
268 // Worst case scenario. The exception page itself is broken, leaving
269 // us with no option but to write the cause to the output.
270
271 __CLOVER_71_0.S[1545]++;reportException(
272 Tapestry.getMessage("AbstractEngine.unable-to-process-client-request"),
273 cause);
274
275 // Also, write the exception thrown when redendering the exception
276 // page, so that can get fixed as well.
277
278 __CLOVER_71_0.S[1546]++;reportException(
279 Tapestry.getMessage("AbstractEngine.unable-to-present-exception-page"),
280 ex);
281
282 // And throw the exception.
283
284 __CLOVER_71_0.S[1547]++;throw new ServletException(ex.getMessage(), ex);
285 }
286 } finally { }}
287
288 /**
289 * Writes a detailed report of the exception to <code>System.err</code>.
290 */
291
292 public void reportException(String reportTitle, Throwable ex)
293 {try { __CLOVER_71_0.M[374]++;
294 __CLOVER_71_0.S[1548]++;LOG.warn(reportTitle, ex);
295
296 __CLOVER_71_0.S[1549]++;System.err.println("\n\n**********************************************************\n\n");
297
298 __CLOVER_71_0.S[1550]++;System.err.println(reportTitle);
299
300 __CLOVER_71_0.S[1551]++;System.err.println("\n\n Session id: " + _sessionId + "\n Client address: "
301 + _clientAddress + "\n\nExceptions:\n");
302
303 __CLOVER_71_0.S[1552]++;new ExceptionAnalyzer().reportException(ex, System.err);
304
305 __CLOVER_71_0.S[1553]++;System.err.println("\n**********************************************************\n");
306
307 } finally { }}
308
309 /**
310 * Invoked at the end of the request cycle to release any resources specific to the request
311 * cycle.
312 */
313
314 protected abstract void cleanupAfterRequest(IRequestCycle cycle);
315
316 /**
317 * Extends the description of the class generated by {@link #toString()}. If a subclass adds
318 * additional instance variables that should be described in the instance description, it may
319 * overide this method. This implementation does nothing.
320 *
321 * @see #toString()
322 */
323
324 protected void extendDescription(ToStringBuilder builder)
325 {try { __CLOVER_71_0.M[375]++;
326
327 } finally { }}
328
329 /**
330 * Returns the locale for the engine. This is initially set by the {@link ApplicationServlet}
331 * but may be updated by the application.
332 */
333
334 public Locale getLocale()
335 {try { __CLOVER_71_0.M[376]++;
336 __CLOVER_71_0.S[1554]++;return _locale;
337 } finally { }}
338
339 /**
340 * Overriden in subclasses that support monitoring. Should create and return an instance of
341 * {@link IMonitor}that is appropriate for the request cycle described by the
342 * {@link RequestContext}.
343 * <p>
344 * The monitor is used to create a {@link RequestCycle}.
345 * <p>
346 * This implementation uses a {@link IMonitorFactory}to create the monitor instance. The
347 * factory is provided as an application extension. If the application extension does not exist,
348 * {@link DefaultMonitorFactory}is used.
349 * <p>
350 * As of release 3.0, this method should <em>not</em> return null.
351 */
352
353 public IMonitor getMonitor(RequestContext context)
354 {try { __CLOVER_71_0.M[377]++;
355 __CLOVER_71_0.S[1555]++;if ((((_monitorFactory == null) && (++__CLOVER_71_0.CT[297] != 0)) || (++__CLOVER_71_0.CF[297] == 0))){
356 {
357 __CLOVER_71_0.S[1556]++;IApplicationSpecification spec = getSpecification();
358
359 __CLOVER_71_0.S[1557]++;if ((((spec.checkExtension(Tapestry.MONITOR_FACTORY_EXTENSION_NAME)) && (++__CLOVER_71_0.CT[298] != 0)) || (++__CLOVER_71_0.CF[298] == 0))){
360 __CLOVER_71_0.S[1558]++;_monitorFactory = (IMonitorFactory) spec.getExtension(
361 Tapestry.MONITOR_FACTORY_EXTENSION_NAME,
362 IMonitorFactory.class);}
363 else{
364 __CLOVER_71_0.S[1559]++;_monitorFactory = DefaultMonitorFactory.SHARED;}
365 }}
366
367 __CLOVER_71_0.S[1560]++;return _monitorFactory.createMonitor(context);
368 } finally { }}
369
370 /** @see Infrastructure#getPageSource() */
371
372 public IPageSource getPageSource()
373 {try { __CLOVER_71_0.M[378]++;
374 __CLOVER_71_0.S[1561]++;return _infrastructure.getPageSource();
375 } finally { }}
376
377 /**
378 * Returns a service with the given name.
379 *
380 * @see Infrastructure#getServiceMap()
381 * @see org.apache.tapestry.services.ServiceMap
382 */
383
384 public IEngineService getService(String name)
385 {try { __CLOVER_71_0.M[379]++;
386 __CLOVER_71_0.S[1562]++;return _infrastructure.getServiceMap().getService(name);
387 } finally { }}
388
389 public String getServletPath()
390 {try { __CLOVER_71_0.M[380]++;
391 __CLOVER_71_0.S[1563]++;return _servletPath;
392 } finally { }}
393
394 /**
395 * Returns the context path, the prefix to apply to any URLs so that they are recognized as
396 * belonging to the Servlet 2.2 context.
397 *
398 * @see org.apache.tapestry.asset.ContextAsset
399 */
400
401 public String getContextPath()
402 {try { __CLOVER_71_0.M[381]++;
403 __CLOVER_71_0.S[1564]++;return _contextPath;
404 } finally { }}
405
406 /** @see Infrastructure#getApplicationSpecification() */
407
408 public IApplicationSpecification getSpecification()
409 {try { __CLOVER_71_0.M[382]++;
410 __CLOVER_71_0.S[1565]++;return _infrastructure.getApplicationSpecification();
411 } finally { }}
412
413 /** @see Infrastructure#getSpecificationSource() */
414
415 public ISpecificationSource getSpecificationSource()
416 {try { __CLOVER_71_0.M[383]++;
417 __CLOVER_71_0.S[1566]++;return _infrastructure.getSpecificationSource();
418 } finally { }}
419
420 /** @see Infrastructure#getTemplateSource() */
421
422 public TemplateSource getTemplateSource()
423 {try { __CLOVER_71_0.M[384]++;
424 __CLOVER_71_0.S[1567]++;return _infrastructure.getTemplateSource();
425 } finally { }}
426
427 /**
428 * Reads the state serialized by {@link #writeExternal(ObjectOutput)}.
429 * <p>
430 * This always set the stateful flag. By default, a deserialized session is stateful (else, it
431 * would not have been serialized).
432 */
433
434 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
435 {try { __CLOVER_71_0.M[385]++;
436 __CLOVER_71_0.S[1568]++;_stateful = true;
437
438 __CLOVER_71_0.S[1569]++;String localeName = in.readUTF();
439 __CLOVER_71_0.S[1570]++;_locale = Tapestry.getLocale(localeName);
440
441 __CLOVER_71_0.S[1571]++;_visit = in.readObject();
442 } finally { }}
443
444 /**
445 * Writes the following properties:
446 * <ul>
447 * <li>locale name ({@link Locale#toString()})
448 * <li>visit
449 * </ul>
450 */
451
452 public void writeExternal(ObjectOutput out) throws IOException
453 {try { __CLOVER_71_0.M[386]++;
454 __CLOVER_71_0.S[1572]++;out.writeUTF(_locale.toString());
455 __CLOVER_71_0.S[1573]++;out.writeObject(_visit);
456 } finally { }}
457
458 /**
459 * Invoked, typically, when an exception occurs while servicing the request. This method resets
460 * the output, sets the new page and renders it.
461 */
462
463 protected void redirect(String pageName, IRequestCycle cycle, ResponseOutputStream out,
464 ApplicationRuntimeException exception) throws IOException, ServletException
465 {try { __CLOVER_71_0.M[387]++;
466 // Discard any output from the previous page.
467
468 __CLOVER_71_0.S[1574]++;out.reset();
469
470 __CLOVER_71_0.S[1575]++;IPage page = cycle.getPage(pageName);
471
472 __CLOVER_71_0.S[1576]++;cycle.activate(page);
473
474 __CLOVER_71_0.S[1577]++;renderResponse(cycle, out);
475 } finally { }}
476
477 public void renderResponse(IRequestCycle cycle, ResponseOutputStream output)
478 throws ServletException, IOException
479 {try { __CLOVER_71_0.M[388]++;
480 __CLOVER_71_0.S[1578]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[299] != 0)) || (++__CLOVER_71_0.CF[299] == 0))){
481 __CLOVER_71_0.S[1579]++;LOG.debug("Begin render response.");}
482
483 // If the locale has changed during this request cycle then
484 // do the work to propogate the locale change into
485 // subsequent request cycles.
486
487 __CLOVER_71_0.S[1580]++;if ((((_localeChanged) && (++__CLOVER_71_0.CT[300] != 0)) || (++__CLOVER_71_0.CF[300] == 0))){
488 {
489 __CLOVER_71_0.S[1581]++;_localeChanged = false;
490
491 __CLOVER_71_0.S[1582]++;RequestContext context = cycle.getRequestContext();
492
493 __CLOVER_71_0.S[1583]++;ApplicationServlet.writeLocaleCookie(_locale, this, context);
494 }}
495
496 // Commit all changes and ignore further changes.
497
498 __CLOVER_71_0.S[1584]++;IPage page = cycle.getPage();
499
500 __CLOVER_71_0.S[1585]++;IMarkupWriter writer = page.getResponseWriter(output);
501
502 __CLOVER_71_0.S[1586]++;output.setContentType(writer.getContentType());
503
504 __CLOVER_71_0.S[1587]++;boolean discard = true;
505
506 __CLOVER_71_0.S[1588]++;try
507 {
508 __CLOVER_71_0.S[1589]++;cycle.renderPage(writer);
509
510 __CLOVER_71_0.S[1590]++;discard = false;
511 }
512 finally
513 {
514 // Closing the writer closes its PrintWriter and a whole stack of
515 // java.io objects,
516 // which tend to stream a lot of output that eventually hits the
517 // ResponseOutputStream. If we are discarding output anyway (due to
518 // an exception
519 // getting thrown during the render), we can save ourselves some
520 // trouble
521 // by ignoring it.
522
523 __CLOVER_71_0.S[1591]++;if ((((discard) && (++__CLOVER_71_0.CT[301] != 0)) || (++__CLOVER_71_0.CF[301] == 0))){
524 __CLOVER_71_0.S[1592]++;output.setDiscard(true);}
525
526 __CLOVER_71_0.S[1593]++;writer.close();
527
528 __CLOVER_71_0.S[1594]++;if ((((discard) && (++__CLOVER_71_0.CT[302] != 0)) || (++__CLOVER_71_0.CF[302] == 0))){
529 __CLOVER_71_0.S[1595]++;output.setDiscard(false);}
530 }
531
532 } finally { }}
533
534 /**
535 * Invalidates the session, then redirects the client web browser to the servlet's prefix,
536 * starting a new visit.
537 * <p>
538 * Subclasses should perform their own restart (if necessary, which is rarely) before invoking
539 * this implementation.
540 */
541
542 public void restart(IRequestCycle cycle) throws IOException
543 {try { __CLOVER_71_0.M[389]++;
544 __CLOVER_71_0.S[1596]++;RequestContext context = cycle.getRequestContext();
545
546 __CLOVER_71_0.S[1597]++;HttpSession session = context.getSession();
547
548 __CLOVER_71_0.S[1598]++;if ((((session != null) && (++__CLOVER_71_0.CT[303] != 0)) || (++__CLOVER_71_0.CF[303] == 0))){
549 {
550 __CLOVER_71_0.S[1599]++;try
551 {
552 __CLOVER_71_0.S[1600]++;session.invalidate();
553 }
554 catch (IllegalStateException ex)
555 {
556 __CLOVER_71_0.S[1601]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[304] != 0)) || (++__CLOVER_71_0.CF[304] == 0))){
557 __CLOVER_71_0.S[1602]++;LOG.debug("Exception thrown invalidating HttpSession.", ex);}
558
559 // Otherwise, ignore it.
560 }
561 }}
562
563 // Make isStateful() return false, so that the servlet doesn't
564 // try to store the engine back into the (now invalid) session.
565
566 __CLOVER_71_0.S[1603]++;_stateful = false;
567
568 __CLOVER_71_0.S[1604]++;String url = context.getAbsoluteURL(_servletPath);
569
570 __CLOVER_71_0.S[1605]++;context.redirect(url);
571 } finally { }}
572
573 /**
574 * Delegate method for the servlet. Services the request.
575 */
576
577 public boolean service(RequestContext context) throws ServletException, IOException
578 {try { __CLOVER_71_0.M[390]++;
579 __CLOVER_71_0.S[1606]++;IRequestCycle cycle = null;
580 __CLOVER_71_0.S[1607]++;ResponseOutputStream output = null;
581 __CLOVER_71_0.S[1608]++;IMonitor monitor = null;
582
583 __CLOVER_71_0.S[1609]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[305] != 0)) || (++__CLOVER_71_0.CF[305] == 0))){
584 __CLOVER_71_0.S[1610]++;LOG.debug("Begin service " + context.getRequestURI());}
585
586 __CLOVER_71_0.S[1611]++;if ((((_infrastructure == null) && (++__CLOVER_71_0.CT[306] != 0)) || (++__CLOVER_71_0.CF[306] == 0))){
587 __CLOVER_71_0.S[1612]++;_infrastructure = (Infrastructure) context.getAttribute(Constants.INFRASTRUCTURE_KEY);}
588
589 // The servlet invokes setLocale() before invoking service(). We want
590 // to ignore that setLocale() ... that is, not force a cookie to be
591 // written.
592
593 __CLOVER_71_0.S[1613]++;_localeChanged = false;
594
595 __CLOVER_71_0.S[1614]++;try
596 {
597 __CLOVER_71_0.S[1615]++;setupForRequest(context);
598
599 __CLOVER_71_0.S[1616]++;monitor = getMonitor(context);
600
601 __CLOVER_71_0.S[1617]++;output = new ResponseOutputStream(context.getResponse());
602 }
603 catch (Exception ex)
604 {
605 __CLOVER_71_0.S[1618]++;reportException(Tapestry.getMessage("AbstractEngine.unable-to-begin-request"), ex);
606
607 __CLOVER_71_0.S[1619]++;throw new ServletException(ex.getMessage(), ex);
608 }
609
610 __CLOVER_71_0.S[1620]++;IEngineService service = null;
611
612 __CLOVER_71_0.S[1621]++;try
613 {
614 __CLOVER_71_0.S[1622]++;try
615 {
616 __CLOVER_71_0.S[1623]++;String serviceName;
617
618 __CLOVER_71_0.S[1624]++;try
619 {
620 __CLOVER_71_0.S[1625]++;serviceName = extractServiceName(context);
621
622 __CLOVER_71_0.S[1626]++;if ((((Tapestry.isBlank(serviceName)) && (++__CLOVER_71_0.CT[307] != 0)) || (++__CLOVER_71_0.CF[307] == 0))){
623 __CLOVER_71_0.S[1627]++;serviceName = Tapestry.HOME_SERVICE;}
624
625 // Must have a service to create the request cycle.
626 // Must have a request cycle to report an exception.
627
628 __CLOVER_71_0.S[1628]++;service = getService(serviceName);
629 }
630 catch (Exception ex)
631 {
632 __CLOVER_71_0.S[1629]++;service = getService(Tapestry.HOME_SERVICE);
633 __CLOVER_71_0.S[1630]++;cycle = createRequestCycle(context, service, monitor);
634
635 __CLOVER_71_0.S[1631]++;throw ex;
636 }
637
638 __CLOVER_71_0.S[1632]++;cycle = createRequestCycle(context, service, monitor);
639
640 __CLOVER_71_0.S[1633]++;monitor.serviceBegin(serviceName, context.getRequestURI());
641
642 // Invoke the service, which returns true if it may have changed
643 // the state of the engine (most do return true).
644
645 __CLOVER_71_0.S[1634]++;service.service(this, cycle, output);
646
647 // Return true only if the engine is actually dirty. This cuts
648 // down
649 // on the number of times the engine is stored into the
650 // session unceccesarily.
651
652 __CLOVER_71_0.S[1635]++;return _dirty;
653 }
654 catch (PageRedirectException ex)
655 {
656 __CLOVER_71_0.S[1636]++;handlePageRedirectException(ex, cycle, output);
657 }
658 catch (RedirectException ex)
659 {
660 __CLOVER_71_0.S[1637]++;handleRedirectException(cycle, ex);
661 }
662 catch (StaleLinkException ex)
663 {
664 __CLOVER_71_0.S[1638]++;handleStaleLinkException(ex, cycle, output);
665 }
666 catch (StaleSessionException ex)
667 {
668 __CLOVER_71_0.S[1639]++;handleStaleSessionException(ex, cycle, output);
669 }
670 }
671 catch (Exception ex)
672 {
673 __CLOVER_71_0.S[1640]++;monitor.serviceException(ex);
674
675 // Discard any output (if possible). If output has already been sent
676 // to
677 // the client, then things get dicey. Note that this block
678 // gets activated if the StaleLink or StaleSession pages throws
679 // any kind of exception.
680
681 // Attempt to switch to the exception page. However, this may itself
682 // fail
683 // for a number of reasons, in which case a ServletException is
684 // thrown.
685
686 __CLOVER_71_0.S[1641]++;output.reset();
687
688 __CLOVER_71_0.S[1642]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[308] != 0)) || (++__CLOVER_71_0.CF[308] == 0))){
689 __CLOVER_71_0.S[1643]++;LOG.debug("Uncaught exception", ex);}
690
691 __CLOVER_71_0.S[1644]++;activateExceptionPage(cycle, output, ex);
692 }
693 finally
694 {
695 __CLOVER_71_0.S[1645]++;if ((((service != null) && (++__CLOVER_71_0.CT[309] != 0)) || (++__CLOVER_71_0.CF[309] == 0))){
696 __CLOVER_71_0.S[1646]++;monitor.serviceEnd(service.getName());}
697
698 __CLOVER_71_0.S[1647]++;try
699 {
700 __CLOVER_71_0.S[1648]++;cycle.cleanup();
701
702 // Closing the buffered output closes the underlying stream as
703 // well.
704
705 __CLOVER_71_0.S[1649]++;if ((((output != null) && (++__CLOVER_71_0.CT[310] != 0)) || (++__CLOVER_71_0.CF[310] == 0))){
706 __CLOVER_71_0.S[1650]++;output.forceFlush();}
707
708 __CLOVER_71_0.S[1651]++;cleanupAfterRequest(cycle);
709 }
710 catch (Exception ex)
711 {
712 __CLOVER_71_0.S[1652]++;reportException(Tapestry.getMessage("AbstractEngine.exception-during-cleanup"), ex);
713 }
714
715 __CLOVER_71_0.S[1653]++;if ((((_disableCaching) && (++__CLOVER_71_0.CT[311] != 0)) || (++__CLOVER_71_0.CF[311] == 0))){
716 {
717 __CLOVER_71_0.S[1654]++;try
718 {
719 __CLOVER_71_0.S[1655]++;clearCachedData();
720 }
721 catch (Exception ex)
722 {
723 __CLOVER_71_0.S[1656]++;reportException(Tapestry
724 .getMessage("AbstractEngine.exception-during-cache-clear"), ex);
725 }
726 }}
727
728 __CLOVER_71_0.S[1657]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[312] != 0)) || (++__CLOVER_71_0.CF[312] == 0))){
729 __CLOVER_71_0.S[1658]++;LOG.debug("End service");}
730
731 }
732
733 __CLOVER_71_0.S[1659]++;return _dirty;
734 } finally { }}
735
736 /**
737 * Handles {@link PageRedirectException}which involves executing
738 * {@link IPage#validate(IRequestCycle)}on the target page (of the exception), until either a
739 * loop is found, or a page succesfully validates and can be activated.
740 * <p>
741 * This should generally not be overriden in subclasses.
742 *
743 * @since 3.0
744 */
745
746 protected void handlePageRedirectException(PageRedirectException ex, IRequestCycle cycle,
747 ResponseOutputStream output) throws IOException, ServletException
748 {try { __CLOVER_71_0.M[391]++;
749 __CLOVER_71_0.S[1660]++;List pageNames = new ArrayList();
750
751 __CLOVER_71_0.S[1661]++;String pageName = ex.getTargetPageName();
752
753 __CLOVER_71_0.S[1662]++;while (true){
754 {
755 __CLOVER_71_0.S[1663]++;if ((((pageNames.contains(pageName)) && (++__CLOVER_71_0.CT[313] != 0)) || (++__CLOVER_71_0.CF[313] == 0))){
756 {
757 // Add the offending page to pageNames so it shows in the
758 // list.
759
760 __CLOVER_71_0.S[1664]++;pageNames.add(pageName);
761
762 __CLOVER_71_0.S[1665]++;StringBuffer buffer = new StringBuffer();
763 __CLOVER_71_0.S[1666]++;int count = pageNames.size();
764
765 __CLOVER_71_0.S[1667]++;for (int i = 0; (((i < count) && (++__CLOVER_71_0.CT[314] != 0)) || (++__CLOVER_71_0.CF[314] == 0)); i++){
766 {
767 __CLOVER_71_0.S[1668]++;if ((((i > 0) && (++__CLOVER_71_0.CT[315] != 0)) || (++__CLOVER_71_0.CF[315] == 0))){
768 __CLOVER_71_0.S[1669]++;buffer.append("; ");}
769
770 __CLOVER_71_0.S[1670]++;buffer.append(pageNames.get(i));
771 }}
772
773 __CLOVER_71_0.S[1671]++;throw new ApplicationRuntimeException(Tapestry.format(
774 "AbstractEngine.validate-cycle",
775 buffer.toString()));
776 }}
777
778 // Record that this page has been a target.
779
780 __CLOVER_71_0.S[1672]++;pageNames.add(pageName);
781
782 __CLOVER_71_0.S[1673]++;try
783 {
784 // Attempt to activate the new page.
785
786 __CLOVER_71_0.S[1674]++;cycle.activate(pageName);
787
788 __CLOVER_71_0.S[1675]++;break;
789 }
790 catch (PageRedirectException ex2)
791 {
792 __CLOVER_71_0.S[1676]++;pageName = ex2.getTargetPageName();
793 }
794 }}
795
796 // Discard any output from the previous page.
797
798 __CLOVER_71_0.S[1677]++;output.reset();
799
800 __CLOVER_71_0.S[1678]++;renderResponse(cycle, output);
801 } finally { }}
802
803 /**
804 * Invoked from {@link #service(RequestContext)}to create an instance of {@link IRequestCycle}
805 * for the current request. This implementation creates an returns an instance of
806 * {@link RequestCycle}.
807 *
808 * @since 3.0
809 */
810
811 protected IRequestCycle createRequestCycle(RequestContext context, IEngineService service,
812 IMonitor monitor)
813 {try { __CLOVER_71_0.M[392]++;
814 __CLOVER_71_0.S[1679]++;return new RequestCycle(this, context, service, monitor);
815 } finally { }}
816
817 /**
818 * Invoked by {@link #service(RequestContext)}if a {@link StaleLinkException}is thrown by the
819 * {@link IEngineService service}. This implementation sets the message property of the
820 * StaleLink page to the message provided in the exception, then invokes
821 * {@link #redirect(String, IRequestCycle, ResponseOutputStream, ApplicationRuntimeException)}
822 * to render the StaleLink page.
823 * <p>
824 * Subclasses may overide this method (without invoking this implementation). A common practice
825 * is to present an error message on the application's Home page.
826 * <p>
827 * Alternately, the application may provide its own version of the StaleLink page, overriding
828 * the framework's implementation (probably a good idea, because the default page hints at
829 * "application errors" and isn't localized). The overriding StaleLink implementation must
830 * implement a message property of type String.
831 *
832 * @since 0.2.10
833 */
834
835 protected void handleStaleLinkException(StaleLinkException ex, IRequestCycle cycle,
836 ResponseOutputStream output) throws IOException, ServletException
837 {try { __CLOVER_71_0.M[393]++;
838 __CLOVER_71_0.S[1680]++;String staleLinkPageName = getStaleLinkPageName();
839 __CLOVER_71_0.S[1681]++;IPage page = cycle.getPage(staleLinkPageName);
840
841 __CLOVER_71_0.S[1682]++;page.setProperty("message", ex.getMessage());
842
843 __CLOVER_71_0.S[1683]++;redirect(staleLinkPageName, cycle, output, ex);
844 } finally { }}
845
846 /**
847 * Invoked by {@link #service(RequestContext)}if a {@link StaleSessionException}is thrown by
848 * the {@link IEngineService service}. This implementation invokes
849 * {@link #redirect(String, IRequestCycle, ResponseOutputStream, ApplicationRuntimeException)}
850 * to render the StaleSession page.
851 * <p>
852 * Subclasses may overide this method (without invoking this implementation). A common practice
853 * is to present an eror message on the application's Home page.
854 *
855 * @since 0.2.10
856 */
857
858 protected void handleStaleSessionException(StaleSessionException ex, IRequestCycle cycle,
859 ResponseOutputStream output) throws IOException, ServletException
860 {try { __CLOVER_71_0.M[394]++;
861 __CLOVER_71_0.S[1684]++;redirect(getStaleSessionPageName(), cycle, output, ex);
862 } finally { }}
863
864 /**
865 * Discards all cached pages, component specifications and templates. Subclasses who override
866 * this method should invoke this implementation as well.
867 *
868 * @since 1.0.1
869 */
870
871 public void clearCachedData()
872 {try { __CLOVER_71_0.M[395]++;
873 __CLOVER_71_0.S[1685]++;_infrastructure.getResetEventCoordinator().fireResetEvent();
874 } finally { }}
875
876 /**
877 * Changes the locale for the engine.
878 */
879
880 public void setLocale(Locale value)
881 {try { __CLOVER_71_0.M[396]++;
882 __CLOVER_71_0.S[1686]++;Tapestry.notNull(value, "locale");
883
884 // Because locale changes are expensive (it involves writing a cookie
885 // and all that),
886 // we're careful not to really change unless there's a true change in
887 // value.
888
889 __CLOVER_71_0.S[1687]++;if ((((!value.equals(_locale)) && (++__CLOVER_71_0.CT[316] != 0)) || (++__CLOVER_71_0.CF[316] == 0))){
890 {
891 __CLOVER_71_0.S[1688]++;_locale = value;
892 __CLOVER_71_0.S[1689]++;_localeChanged = true;
893 __CLOVER_71_0.S[1690]++;markDirty();
894 }}
895 } finally { }}
896
897 /**
898 * Invoked from {@link #service(RequestContext)}to ensure that the engine's instance variables
899 * are setup. This allows the application a chance to restore transient variables that will not
900 * have survived deserialization. Determines the servlet prefix: this is the base URL used by
901 * {@link IEngineService services}to build URLs. It consists of two parts: the context path and
902 * the servlet path.
903 * <p>
904 * The servlet path is retrieved from {@link HttpServletRequest#getServletPath()}.
905 * <p>
906 * The context path is retrieved from {@link HttpServletRequest#getContextPath()}.
907 * <p>
908 * The global object is retrieved from {@link IEngine#getGlobal()}method.
909 * <p>
910 * The final path is available via the {@link #getServletPath()}method.
911 * <p>
912 * In addition, this method locates and/or creates the:
913 * <ul>
914 * <li>{@link IComponentClassEnhancer}
915 * <li>{@link Pool}
916 * <li>{@link ITemplateSource}
917 * <li>{@link ISpecificationSource}
918 * <li>{@link IPageSource}
919 * <li>{@link IEngineService}{@link Map}<ll>{@link IScriptSource}
920 * <li>{@link IComponentMessagesSource}
921 * <li>{@link IPropertySource}
922 * </ul>
923 * <p>
924 * This order is important, because some of the later shared objects depend on some of the
925 * earlier shared objects already having been located or created (especially
926 * {@link #getPool() pool}).
927 * <p>
928 * Subclasses should invoke this implementation first, then perform their own setup.
929 */
930
931 protected void setupForRequest(RequestContext context)
932 {try { __CLOVER_71_0.M[397]++;
933 __CLOVER_71_0.S[1691]++;HttpServlet servlet = context.getServlet();
934 __CLOVER_71_0.S[1692]++;ServletContext servletContext = servlet.getServletContext();
935 __CLOVER_71_0.S[1693]++;HttpServletRequest request = context.getRequest();
936 __CLOVER_71_0.S[1694]++;HttpSession session = context.getSession();
937
938 __CLOVER_71_0.S[1695]++;if ((((session != null) && (++__CLOVER_71_0.CT[317] != 0)) || (++__CLOVER_71_0.CF[317] == 0))){
939 __CLOVER_71_0.S[1696]++;_sessionId = context.getSession().getId();}
940 else{
941 __CLOVER_71_0.S[1697]++;_sessionId = null;}
942
943 // Previously, this used getRemoteHost(), but that requires an
944 // expensive reverse DNS lookup. Possibly, the host name lookup
945 // should occur ... but only if there's an actual error message
946 // to display.
947
948 __CLOVER_71_0.S[1698]++;if ((((_clientAddress == null) && (++__CLOVER_71_0.CT[318] != 0)) || (++__CLOVER_71_0.CF[318] == 0))){
949 __CLOVER_71_0.S[1699]++;_clientAddress = request.getRemoteAddr();}
950
951 // servletPath is null, so this means either we're doing the
952 // first request in this session, or we're handling a subsequent
953 // request in another JVM (i.e. another server in the cluster).
954 // In any case, we have to do some late (re-)initialization.
955
956 __CLOVER_71_0.S[1700]++;if ((((_servletPath == null) && (++__CLOVER_71_0.CT[319] != 0)) || (++__CLOVER_71_0.CF[319] == 0))){
957 {
958 // Get the path *within* the servlet context
959
960 // In rare cases related to the tagsupport service, getServletPath()
961 // is wrong
962 // (its a JSP, which invokes Tapestry as an include, thus muddling
963 // what
964 // the real servlet and servlet path is). In those cases, the JSP
965 // tag
966 // will inform us.
967
968 __CLOVER_71_0.S[1701]++;String path = (String) request
969 .getAttribute(Tapestry.TAG_SUPPORT_SERVLET_PATH_ATTRIBUTE);
970
971 __CLOVER_71_0.S[1702]++;if ((((path == null) && (++__CLOVER_71_0.CT[320] != 0)) || (++__CLOVER_71_0.CF[320] == 0))){
972 __CLOVER_71_0.S[1703]++;path = request.getServletPath();}
973
974 // Get the context path, which may be the empty string
975 // (but won't be null).
976
977 __CLOVER_71_0.S[1704]++;_contextPath = request.getContextPath();
978
979 __CLOVER_71_0.S[1705]++;_servletPath = _contextPath + path;
980 }}
981
982 __CLOVER_71_0.S[1706]++;String servletName = context.getServlet().getServletName();
983
984 __CLOVER_71_0.S[1707]++;if ((((_global == null) && (++__CLOVER_71_0.CT[321] != 0)) || (++__CLOVER_71_0.CF[321] == 0))){
985 {
986 __CLOVER_71_0.S[1708]++;String name = GLOBAL_NAME + ":" + servletName;
987
988 __CLOVER_71_0.S[1709]++;_global = servletContext.getAttribute(name);
989
990 __CLOVER_71_0.S[1710]++;if ((((_global == null) && (++__CLOVER_71_0.CT[322] != 0)) || (++__CLOVER_71_0.CF[322] == 0))){
991 {
992 __CLOVER_71_0.S[1711]++;_global = createGlobal(context);
993
994 __CLOVER_71_0.S[1712]++;servletContext.setAttribute(name, _global);
995 }}
996 }}
997
998 __CLOVER_71_0.S[1713]++;String encoding = request.getCharacterEncoding();
999 __CLOVER_71_0.S[1714]++;if ((((encoding == null) && (++__CLOVER_71_0.CT[323] != 0)) || (++__CLOVER_71_0.CF[323] == 0))){
1000 {
1001 __CLOVER_71_0.S[1715]++;encoding = getOutputEncoding();
1002 __CLOVER_71_0.S[1716]++;try
1003 {
1004 __CLOVER_71_0.S[1717]++;request.setCharacterEncoding(encoding);
1005 }
1006 catch (UnsupportedEncodingException e)
1007 {
1008 __CLOVER_71_0.S[1718]++;throw new IllegalArgumentException(Tapestry.format("illegal-encoding", encoding));
1009 }
1010 catch (NoSuchMethodError e)
1011 {
1012 // Servlet API 2.2 compatibility
1013 // Behave okay if the setCharacterEncoding() method is
1014 // unavailable
1015 }
1016 catch (AbstractMethodError e)
1017 {
1018 // Servlet API 2.2 compatibility
1019 // Behave okay if the setCharacterEncoding() method is
1020 // unavailable
1021 }
1022 }}
1023 } finally { }}
1024
1025 /**
1026 * @see Infrastructure#getClassResolver()
1027 */
1028
1029 public ClassResolver getClassResolver()
1030 {try { __CLOVER_71_0.M[398]++;
1031 __CLOVER_71_0.S[1719]++;return _infrastructure.getClassResolver();
1032 } finally { }}
1033
1034 /**
1035 * Generates a description of the instance. Invokes {@link #extendDescription(ToStringBuilder)}
1036 * to fill in details about the instance.
1037 *
1038 * @see #extendDescription(ToStringBuilder)
1039 */
1040
1041 public String toString()
1042 {try { __CLOVER_71_0.M[399]++;
1043 __CLOVER_71_0.S[1720]++;ToStringBuilder builder = new ToStringBuilder(this);
1044
1045 __CLOVER_71_0.S[1721]++;builder.append("dirty", _dirty);
1046 __CLOVER_71_0.S[1722]++;builder.append("locale", _locale);
1047 __CLOVER_71_0.S[1723]++;builder.append("stateful", _stateful);
1048 __CLOVER_71_0.S[1724]++;builder.append("visit", _visit);
1049
1050 __CLOVER_71_0.S[1725]++;extendDescription(builder);
1051
1052 __CLOVER_71_0.S[1726]++;return builder.toString();
1053 } finally { }}
1054
1055 /**
1056 * Returns true if the reset service is curently enabled.
1057 */
1058
1059 public boolean isResetServiceEnabled()
1060 {try { __CLOVER_71_0.M[400]++;
1061 __CLOVER_71_0.S[1727]++;return _resetServiceEnabled;
1062 } finally { }}
1063
1064 /**
1065 * Implemented by subclasses to return the names of the active pages (pages for which recorders
1066 * exist). May return the empty list, but should not return null.
1067 */
1068
1069 abstract public Collection getActivePageNames();
1070
1071 /**
1072 * Gets the visit object, if it has been created already.
1073 * <p>
1074 * If the visit is non-null then the {@link #isDirty()}flag is set (because the engine can't
1075 * tell what the caller will <i>do </i> with the visit).
1076 */
1077
1078 public Object getVisit()
1079 {try { __CLOVER_71_0.M[401]++;
1080 __CLOVER_71_0.S[1728]++;if ((((_visit != null) && (++__CLOVER_71_0.CT[324] != 0)) || (++__CLOVER_71_0.CF[324] == 0))){
1081 __CLOVER_71_0.S[1729]++;markDirty();}
1082
1083 __CLOVER_71_0.S[1730]++;return _visit;
1084 } finally { }}
1085
1086 /**
1087 * Gets the visit object, invoking {@link #createVisit(IRequestCycle)}to create it lazily if
1088 * needed. If cycle is null, the visit will not be lazily created.
1089 * <p>
1090 * After creating the visit, but before returning, the {@link HttpSession}will be created, and
1091 * {@link #setStateful()}will be invoked.
1092 * <p>
1093 * Sets the {@link #isDirty()}flag, if the return value is not null.
1094 */
1095
1096 public Object getVisit(IRequestCycle cycle)
1097 {try { __CLOVER_71_0.M[402]++;
1098 __CLOVER_71_0.S[1731]++;if ((((_visit == null && cycle != null) && (++__CLOVER_71_0.CT[325] != 0)) || (++__CLOVER_71_0.CF[325] == 0))){
1099 {
1100 __CLOVER_71_0.S[1732]++;_visit = createVisit(cycle);
1101
1102 // Now that a visit object exists, we need to force the creation
1103 // of a HttpSession.
1104
1105 __CLOVER_71_0.S[1733]++;cycle.getRequestContext().createSession();
1106
1107 __CLOVER_71_0.S[1734]++;setStateful();
1108 }}
1109
1110 __CLOVER_71_0.S[1735]++;if ((((_visit != null) && (++__CLOVER_71_0.CT[326] != 0)) || (++__CLOVER_71_0.CF[326] == 0))){
1111 __CLOVER_71_0.S[1736]++;markDirty();}
1112
1113 __CLOVER_71_0.S[1737]++;return _visit;
1114 } finally { }}
1115
1116 /**
1117 * Updates the visit object and sets the {@link #isDirty() dirty flag}.
1118 */
1119
1120 public void setVisit(Object value)
1121 {try { __CLOVER_71_0.M[403]++;
1122 __CLOVER_71_0.S[1738]++;_visit = value;
1123
1124 __CLOVER_71_0.S[1739]++;markDirty();
1125 } finally { }}
1126
1127 public boolean getHasVisit()
1128 {try { __CLOVER_71_0.M[404]++;
1129 __CLOVER_71_0.S[1740]++;return _visit != null;
1130 } finally { }}
1131
1132 /**
1133 * Invoked to lazily create a new visit object when it is first referenced (by
1134 * {@link #getVisit(IRequestCycle)}). This implementation works by looking up the name of the
1135 * class to instantiate in the {@link #getPropertySource() configuration}.
1136 * <p>
1137 * Subclasses may want to overide this method if some other means of instantiating a visit
1138 * object is required.
1139 */
1140
1141 protected Object createVisit(IRequestCycle cycle)
1142 {try { __CLOVER_71_0.M[405]++;
1143 __CLOVER_71_0.S[1741]++;String visitClassName;
1144 __CLOVER_71_0.S[1742]++;Class visitClass;
1145 __CLOVER_71_0.S[1743]++;Object result = null;
1146
1147 __CLOVER_71_0.S[1744]++;visitClassName = getPropertySource().getPropertyValue(VISIT_CLASS_PROPERTY_NAME);
1148
1149 __CLOVER_71_0.S[1745]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[327] != 0)) || (++__CLOVER_71_0.CF[327] == 0))){
1150 __CLOVER_71_0.S[1746]++;LOG.debug("Creating visit object as instance of " + visitClassName);}
1151
1152 __CLOVER_71_0.S[1747]++;visitClass = getClassResolver().findClass(visitClassName);
1153
1154 __CLOVER_71_0.S[1748]++;try
1155 {
1156 __CLOVER_71_0.S[1749]++;result = visitClass.newInstance();
1157 }
1158 catch (Throwable t)
1159 {
1160 __CLOVER_71_0.S[1750]++;throw new ApplicationRuntimeException(Tapestry.format(
1161 "AbstractEngine.unable-to-instantiate-visit",
1162 visitClassName), t);
1163 }
1164
1165 __CLOVER_71_0.S[1751]++;return result;
1166 } finally { }}
1167
1168 /**
1169 * Returns the global object for the application. The global object is created at the start of
1170 * the request ({@link #setupForRequest(RequestContext)}invokes
1171 * {@link #createGlobal(RequestContext)}if needed), and is stored into the
1172 * {@link ServletContext}. All instances of the engine for the application share the global
1173 * object; however, the global object is explicitly <em>not</em> replicated to other servers
1174 * within a cluster.
1175 *
1176 * @since 2.3
1177 */
1178
1179 public Object getGlobal()
1180 {try { __CLOVER_71_0.M[406]++;
1181 __CLOVER_71_0.S[1752]++;return _global;
1182 } finally { }}
1183
1184 public IScriptSource getScriptSource()
1185 {try { __CLOVER_71_0.M[407]++;
1186 __CLOVER_71_0.S[1753]++;return _infrastructure.getScriptSource();
1187 } finally { }}
1188
1189 public boolean isStateful()
1190 {try { __CLOVER_71_0.M[408]++;
1191 __CLOVER_71_0.S[1754]++;return _stateful;
1192 } finally { }}
1193
1194 /**
1195 * Invoked by subclasses to indicate that some state must now be stored in the engine (and that
1196 * the engine should now be stored in the HttpSession). The caller is responsible for actually
1197 * creating the HttpSession (it will have access to the {@link RequestContext}).
1198 *
1199 * @since 1.0.2
1200 */
1201
1202 protected void setStateful()
1203 {try { __CLOVER_71_0.M[409]++;
1204 __CLOVER_71_0.S[1755]++;_stateful = true;
1205 } finally { }}
1206
1207 /**
1208 * Allows subclasses to include listener methods easily.
1209 *
1210 * @since 1.0.2
1211 */
1212
1213 public ListenerMap getListeners()
1214 {try { __CLOVER_71_0.M[410]++;
1215 __CLOVER_71_0.S[1756]++;if ((((_listeners == null) && (++__CLOVER_71_0.CT[328] != 0)) || (++__CLOVER_71_0.CF[328] == 0))){
1216 __CLOVER_71_0.S[1757]++;_listeners = new ListenerMap(this);}
1217
1218 __CLOVER_71_0.S[1758]++;return _listeners;
1219 } finally { }}
1220
1221 /**
1222 * Invoked when a {@link RedirectException}is thrown during the processing of a request.
1223 *
1224 * @throws ApplicationRuntimeException
1225 * if an {@link IOException},{@link ServletException}is thrown by the redirect,
1226 * or if no {@link RequestDispatcher}can be found for local resource.
1227 * @since 2.2
1228 */
1229
1230 protected void handleRedirectException(IRequestCycle cycle, RedirectException ex)
1231 {try { __CLOVER_71_0.M[411]++;
1232 __CLOVER_71_0.S[1759]++;String location = ex.getRedirectLocation();
1233
1234 __CLOVER_71_0.S[1760]++;if ((((LOG.isDebugEnabled()) && (++__CLOVER_71_0.CT[329] != 0)) || (++__CLOVER_71_0.CF[329] == 0))){
1235 __CLOVER_71_0.S[1761]++;LOG.debug("Redirecting to: " + location);}
1236
1237 __CLOVER_71_0.S[1762]++;RedirectAnalyzer analyzer = new RedirectAnalyzer(location);
1238
1239 __CLOVER_71_0.S[1763]++;analyzer.process(cycle);
1240 } finally { }}
1241
1242 /**
1243 * @since 2.0.4
1244 */
1245
1246 public ComponentMessagesSource getComponentMessagesSource()
1247 {try { __CLOVER_71_0.M[412]++;
1248 __CLOVER_71_0.S[1764]++;return _infrastructure.getComponentMessagesSource();
1249 } finally { }}
1250
1251 /**
1252 * @see Infrastructure#getDataSqueezer()
1253 */
1254
1255 public DataSqueezer getDataSqueezer()
1256 {try { __CLOVER_71_0.M[413]++;
1257 __CLOVER_71_0.S[1765]++;return _infrastructure.getDataSqueezer();
1258 } finally { }}
1259
1260 /**
1261 * Invoked from {@link #service(RequestContext)}to extract, from the URL, the name of the
1262 * service. The current implementation expects the first pathInfo element to be the service
1263 * name. At some point in the future, the method of constructing and parsing URLs may be
1264 * abstracted into a developer-selected class.
1265 * <p>
1266 * Subclasses may override this method if the application defines specific services with unusual
1267 * URL encoding rules.
1268 * <p>
1269 * This implementation simply extracts the value for query parameter
1270 * {@link Tapestry#SERVICE_QUERY_PARAMETER_NAME}and extracts the service name from that.
1271 * <p>
1272 * For supporting the JSP tags, this method first checks for attribute
1273 * {@link Tapestry#TAG_SUPPORT_SERVICE_ATTRIBUTE}. If non-null, then
1274 * {@link Tapestry#TAGSUPPORT_SERVICE}is returned.
1275 *
1276 * @since 2.2
1277 */
1278
1279 protected String extractServiceName(RequestContext context)
1280 {try { __CLOVER_71_0.M[414]++;
1281 __CLOVER_71_0.S[1766]++;if ((((context.getRequest().getAttribute(Tapestry.TAG_SUPPORT_SERVICE_ATTRIBUTE) != null) && (++__CLOVER_71_0.CT[330] != 0)) || (++__CLOVER_71_0.CF[330] == 0))){
1282 __CLOVER_71_0.S[1767]++;return Tapestry.TAGSUPPORT_SERVICE;}
1283
1284 __CLOVER_71_0.S[1768]++;String serviceData = context.getParameter(Tapestry.SERVICE_QUERY_PARAMETER_NAME);
1285
1286 __CLOVER_71_0.S[1769]++;if ((((serviceData == null) && (++__CLOVER_71_0.CT[331] != 0)) || (++__CLOVER_71_0.CF[331] == 0))){
1287 __CLOVER_71_0.S[1770]++;return Tapestry.HOME_SERVICE;}
1288
1289 // The service name is anything before the first slash,
1290 // if there is one.
1291
1292 __CLOVER_71_0.S[1771]++;int slashx = serviceData.indexOf('/');
1293
1294 __CLOVER_71_0.S[1772]++;if ((((slashx < 0) && (++__CLOVER_71_0.CT[332] != 0)) || (++__CLOVER_71_0.CF[332] == 0))){
1295 __CLOVER_71_0.S[1773]++;return serviceData;}
1296
1297 __CLOVER_71_0.S[1774]++;return serviceData.substring(0, slashx);
1298 } finally { }}
1299
1300 /** @since 2.3 */
1301
1302 public IPropertySource getPropertySource()
1303 {try { __CLOVER_71_0.M[415]++;
1304 __CLOVER_71_0.S[1775]++;return _infrastructure.getApplicationPropertySource();
1305 } finally { }}
1306
1307 /** @since 3.0 */
1308
1309 protected String getExceptionPageName()
1310 {try { __CLOVER_71_0.M[416]++;
1311 __CLOVER_71_0.S[1776]++;return EXCEPTION_PAGE;
1312 } finally { }}
1313
1314 /** @since 3.0 */
1315
1316 protected String getStaleLinkPageName()
1317 {try { __CLOVER_71_0.M[417]++;
1318 __CLOVER_71_0.S[1777]++;return STALE_LINK_PAGE;
1319 } finally { }}
1320
1321 /** @since 3.0 */
1322
1323 protected String getStaleSessionPageName()
1324 {try { __CLOVER_71_0.M[418]++;
1325 __CLOVER_71_0.S[1778]++;return STALE_SESSION_PAGE;
1326 } finally { }}
1327
1328 /**
1329 * Creates the shared Global object. This implementation looks for an configuration property,
1330 * <code>org.apache.tapestry.global-class</code>, and instantiates that class using a
1331 * no-arguments constructor. If the property is not defined, a synchronized
1332 * {@link java.util.HashMap}is created.
1333 *
1334 * @since 2.3
1335 */
1336
1337 protected Object createGlobal(RequestContext context)
1338 {try { __CLOVER_71_0.M[419]++;
1339 __CLOVER_71_0.S[1779]++;String className = getPropertySource().getPropertyValue("org.apache.tapestry.global-class");
1340
1341 __CLOVER_71_0.S[1780]++;if ((((Tapestry.isBlank(className)) && (++__CLOVER_71_0.CT[333] != 0)) || (++__CLOVER_71_0.CF[333] == 0))){
1342 __CLOVER_71_0.S[1781]++;return Collections.synchronizedMap(new HashMap());}
1343
1344 __CLOVER_71_0.S[1782]++;Class globalClass = getClassResolver().findClass(className);
1345
1346 __CLOVER_71_0.S[1783]++;try
1347 {
1348 __CLOVER_71_0.S[1784]++;return globalClass.newInstance();
1349 }
1350 catch (Exception ex)
1351 {
1352 __CLOVER_71_0.S[1785]++;throw new ApplicationRuntimeException(Tapestry.format(
1353 "AbstractEngine.unable-to-instantiate-global",
1354 className), ex);
1355 }
1356 } finally { }}
1357
1358 /** @see Infrastructure#getObjectPool() */
1359
1360 public ObjectPool getPool()
1361 {try { __CLOVER_71_0.M[420]++;
1362 __CLOVER_71_0.S[1786]++;return _infrastructure.getObjectPool();
1363 } finally { }}
1364
1365 /** @see Infrastructure#getComponentClassEnhancer() */
1366
1367 public IComponentClassEnhancer getComponentClassEnhancer()
1368 {try { __CLOVER_71_0.M[421]++;
1369 __CLOVER_71_0.S[1787]++;return _infrastructure.getComponentClassEnhancer();
1370 } finally { }}
1371
1372 /**
1373 * Returns true if the engine has (potentially) changed state since the last time it was stored
1374 * into the {@link javax.servlet.http.HttpSession}. Various events set this property to true.
1375 *
1376 * @since 3.0
1377 */
1378
1379 public boolean isDirty()
1380 {try { __CLOVER_71_0.M[422]++;
1381 __CLOVER_71_0.S[1788]++;return _dirty;
1382 } finally { }}
1383
1384 /**
1385 * Invoked to set the dirty flag, indicating that the engine should be stored into the
1386 * {@link javax.servlet.http.HttpSession}.
1387 *
1388 * @since 3.0
1389 */
1390
1391 protected void markDirty()
1392 {try { __CLOVER_71_0.M[423]++;
1393 __CLOVER_71_0.S[1789]++;if ((((!_dirty) && (++__CLOVER_71_0.CT[334] != 0)) || (++__CLOVER_71_0.CF[334] == 0))){
1394 __CLOVER_71_0.S[1790]++;LOG.debug("Setting dirty flag.");}
1395
1396 __CLOVER_71_0.S[1791]++;_dirty = true;
1397 } finally { }}
1398
1399 /**
1400 * Clears the dirty flag when a engine is stored into the {@link HttpSession}.
1401 *
1402 * @since 3.0
1403 */
1404
1405 public void valueBound(HttpSessionBindingEvent arg0)
1406 {try { __CLOVER_71_0.M[424]++;
1407 __CLOVER_71_0.S[1792]++;LOG.debug((((_dirty ) && (++__CLOVER_71_0.CT[335] != 0)) || (++__CLOVER_71_0.CF[335] == 0))? "Clearing dirty flag." : "Dirty flag already cleared.");
1408
1409 __CLOVER_71_0.S[1793]++;_dirty = false;
1410 } finally { }}
1411
1412 /**
1413 * Does nothing.
1414 *
1415 * @since 3.0
1416 */
1417
1418 public void valueUnbound(HttpSessionBindingEvent arg0)
1419 {try { __CLOVER_71_0.M[425]++;
1420 } finally { }}
1421
1422 /**
1423 * The encoding to be used if none has been defined using the output encoding property. Override
1424 * this method to change the default.
1425 *
1426 * @return the default output encoding
1427 * @since 3.0
1428 */
1429 protected String getDefaultOutputEncoding()
1430 {try { __CLOVER_71_0.M[426]++;
1431 __CLOVER_71_0.S[1794]++;return DEFAULT_OUTPUT_ENCODING;
1432 } finally { }}
1433
1434 /**
1435 * Returns the encoding to be used to generate the servlet responses and accept the servlet
1436 * requests. The encoding is defined using the org.apache.tapestry.output-encoding and is UTF-8
1437 * by default
1438 *
1439 * @since 3.0
1440 * @see org.apache.tapestry.IEngine#getOutputEncoding()
1441 */
1442 public String getOutputEncoding()
1443 {try { __CLOVER_71_0.M[427]++;
1444 __CLOVER_71_0.S[1795]++;IPropertySource source = getPropertySource();
1445
1446 __CLOVER_71_0.S[1796]++;String encoding = source.getPropertyValue(OUTPUT_ENCODING_PROPERTY_NAME);
1447 __CLOVER_71_0.S[1797]++;if ((((encoding == null) && (++__CLOVER_71_0.CT[336] != 0)) || (++__CLOVER_71_0.CF[336] == 0))){
1448 __CLOVER_71_0.S[1798]++;encoding = getDefaultOutputEncoding();}
1449
1450 __CLOVER_71_0.S[1799]++;return encoding;
1451 } finally { }}
1452
1453}