KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > loader > CmsJspLoader


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/loader/CmsJspLoader.java,v $
3  * Date : $Date: 2006/11/30 14:13:53 $
4  * Version: $Revision: 1.101 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.loader;
33
34 import org.opencms.file.CmsFile;
35 import org.opencms.file.CmsObject;
36 import org.opencms.file.CmsPropertyDefinition;
37 import org.opencms.file.CmsResource;
38 import org.opencms.flex.CmsFlexCache;
39 import org.opencms.flex.CmsFlexController;
40 import org.opencms.flex.CmsFlexRequest;
41 import org.opencms.flex.CmsFlexResponse;
42 import org.opencms.i18n.CmsEncoder;
43 import org.opencms.i18n.CmsMessageContainer;
44 import org.opencms.main.CmsException;
45 import org.opencms.main.CmsLog;
46 import org.opencms.main.OpenCms;
47 import org.opencms.staticexport.CmsLinkManager;
48 import org.opencms.util.CmsFileUtil;
49 import org.opencms.util.CmsRequestUtil;
50 import org.opencms.workplace.CmsWorkplaceManager;
51
52 import java.io.File JavaDoc;
53 import java.io.FileNotFoundException JavaDoc;
54 import java.io.FileOutputStream JavaDoc;
55 import java.io.IOException JavaDoc;
56 import java.io.UnsupportedEncodingException JavaDoc;
57 import java.net.SocketException JavaDoc;
58 import java.util.Collections JavaDoc;
59 import java.util.HashSet JavaDoc;
60 import java.util.Locale JavaDoc;
61 import java.util.Map JavaDoc;
62 import java.util.Set JavaDoc;
63 import java.util.TreeMap JavaDoc;
64
65 import javax.servlet.ServletException JavaDoc;
66 import javax.servlet.ServletRequest JavaDoc;
67 import javax.servlet.ServletResponse JavaDoc;
68 import javax.servlet.http.HttpServletRequest JavaDoc;
69 import javax.servlet.http.HttpServletResponse JavaDoc;
70
71 import org.apache.commons.collections.ExtendedProperties;
72 import org.apache.commons.logging.Log;
73
74 /**
75  * The JSP loader which enables the execution of JSP in OpenCms.<p>
76  *
77  * Parameters supported by this loader:<dl>
78  *
79  * <dt>jsp.repository</dt><dd>
80  * (Optional) This is the root directory in the "real" file system where generated JSPs are stored.
81  * The default is the web application path, e.g. in Tomcat if your web application is
82  * names "opencms" it would be <code>${TOMCAT_HOME}/webapps/opencms/</code>.
83  * The <code>jsp.folder</code> (see below) is added to this path.
84  * Usually the <code>jsp.repository</code> is not changed.
85  * </dd>
86  *
87  * <dt>jsp.folder</dt><dd>
88  * (Optional) A path relative to the <code>jsp.repository</code> path where the
89  * JSPs generated by OpenCms are stored. The default is to store the generated JSP in
90  * <code>/WEB-INF/jsp/</code>.
91  * This works well in Tomcat 4, and the JSPs are
92  * not accessible directly from the outside this way, only through the OpenCms servlet.
93  * <i>Please note:</i> Some servlet environments (e.g. BEA Weblogic) do not permit
94  * JSPs to be stored under <code>/WEB-INF</code>. For environments like these,
95  * set the path to some place where JSPs can be accessed, e.g. <code>/jsp/</code> only.
96  * </dd>
97  *
98  * <dt>jsp.errorpage.committed</dt><dd>
99  * (Optional) This parameter controls behaviour of JSP error pages
100  * i.e. <code>&lt;% page errorPage="..." %&gt;</code>. If you find that these don't work
101  * in your servlet environment, you should try to change the value here.
102  * The default <code>true</code> has been tested with Tomcat 4.1 and 5.0.
103  * Older versions of Tomcat like 4.0 require a setting of <code>false</code>.</dd>
104  * </dl>
105  *
106  * @author Alexander Kandzior
107  *
108  * @version $Revision: 1.101 $
109  *
110  * @since 6.0.0
111  *
112  * @see I_CmsResourceLoader
113  */

114 public class CmsJspLoader implements I_CmsResourceLoader, I_CmsFlexCacheEnabledLoader {
115
116     /** Property value for "cache" that indicates that the FlexCache should be bypassed. */
117     public static final String JavaDoc CACHE_PROPERTY_BYPASS = "bypass";
118
119     /** Property value for "cache" that indicates that the ouput should be streamed. */
120     public static final String JavaDoc CACHE_PROPERTY_STREAM = "stream";
121
122     /** Default jsp folder constant. */
123     public static final String JavaDoc DEFAULT_JSP_FOLDER = "/WEB-INF/jsp/";
124
125     /** Special JSP directive tag start (<code>%&gt;</code>). */
126     public static final String JavaDoc DIRECTIVE_END = "%>";
127
128     /** Special JSP directive tag start (<code>&lt;%&#0040;</code>). */
129     public static final String JavaDoc DIRECTIVE_START = "<%@";
130
131     /** Extension for JSP managed by OpenCms (<code>.jsp</code>). */
132     public static final String JavaDoc JSP_EXTENSION = ".jsp";
133
134     /** Cache max age parameter name. */
135     public static final String JavaDoc PARAM_CLIENT_CACHE_MAXAGE = "client.cache.maxage";
136
137     /** Error page commited parameter name. */
138     public static final String JavaDoc PARAM_JSP_ERRORPAGE_COMMITTED = "jsp.errorpage.committed";
139
140     /** Jsp folder parameter name. */
141     public static final String JavaDoc PARAM_JSP_FOLDER = "jsp.folder";
142
143     /** Jsp repository parameter name. */
144     public static final String JavaDoc PARAM_JSP_REPOSITORY = "jsp.repository";
145
146     /** The id of this loader. */
147     public static final int RESOURCE_LOADER_ID = 6;
148
149     /** The log object for this class. */
150     private static final Log LOG = CmsLog.getLog(CmsJspLoader.class);
151
152     /** The maximum age for delivered contents in the clients cache. */
153     private static long m_clientCacheMaxAge;
154
155     /** The directory to store the generated JSP pages in (absolute path). */
156     private static String JavaDoc m_jspRepository;
157
158     /** The directory to store the generated JSP pages in (relative path in web application). */
159     private static String JavaDoc m_jspWebAppRepository;
160
161     /** The CmsFlexCache used to store generated cache entries in. */
162     private CmsFlexCache m_cache;
163
164     /** The resource loader configuration. */
165     private Map JavaDoc m_configuration;
166
167     /** Flag to indicate if error pages are marked as "commited". */
168     // TODO: This is a hack, investigate this issue with different runtime environments
169
private boolean m_errorPagesAreNotCommited; // default false should work for Tomcat > 4.1
170

171     /**
172      * The constructor of the class is empty, the initial instance will be
173      * created by the resource manager upon startup of OpenCms.<p>
174      *
175      * @see org.opencms.loader.CmsResourceManager
176      */

177     public CmsJspLoader() {
178
179         m_configuration = new TreeMap JavaDoc();
180     }
181
182     /**
183      * Returns the absolute path in the "real" file system for the JSP repository
184      * toplevel directory.
185      *
186      * @return The full path to the JSP repository
187      */

188     public static String JavaDoc getJspRepository() {
189
190         return m_jspRepository;
191     }
192
193     /**
194      * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String)
195      */

196     public void addConfigurationParameter(String JavaDoc paramName, String JavaDoc paramValue) {
197
198         m_configuration.put(paramName, paramValue);
199     }
200
201     /** Destroy this ResourceLoder, this is a NOOP so far. */
202     public void destroy() {
203
204         // NOOP
205
}
206
207     /**
208      * @see org.opencms.loader.I_CmsResourceLoader#dump(org.opencms.file.CmsObject, org.opencms.file.CmsResource, java.lang.String, java.util.Locale, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
209      */

210     public byte[] dump(
211         CmsObject cms,
212         CmsResource file,
213         String JavaDoc element,
214         Locale JavaDoc locale,
215         HttpServletRequest JavaDoc req,
216         HttpServletResponse JavaDoc res) throws ServletException JavaDoc, IOException JavaDoc {
217
218         // get the current Flex controller
219
CmsFlexController controller = CmsFlexController.getController(req);
220         CmsFlexController oldController = null;
221
222         if (controller != null) {
223             // for dumping we must create an new "top level" controller, save the old one to be restored later
224
oldController = controller;
225         }
226
227         byte[] result = null;
228         try {
229             // now create a new, temporary Flex controller
230
controller = getController(cms, file, req, res, false, false);
231             if (element != null) {
232                 // add the element parameter to the included request
233
String JavaDoc[] value = new String JavaDoc[] {element};
234                 Map JavaDoc parameters = Collections.singletonMap(I_CmsResourceLoader.PARAMETER_ELEMENT, value);
235                 controller.getCurrentRequest().addParameterMap(parameters);
236             }
237             // dispatch to the JSP
238
result = dispatchJsp(controller);
239             // remove temporary controller
240
CmsFlexController.removeController(req);
241         } finally {
242             if (oldController != null) {
243                 // update "date last modified"
244
oldController.updateDates(controller.getDateLastModified(), controller.getDateExpires());
245                 // reset saved controller
246
CmsFlexController.setController(req, oldController);
247             }
248         }
249
250         return result;
251     }
252
253     /**
254      * @see org.opencms.loader.I_CmsResourceLoader#export(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
255      */

256     public byte[] export(CmsObject cms, CmsResource resource, HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res)
257     throws ServletException JavaDoc, IOException JavaDoc {
258
259         // get the Flex controller
260
CmsFlexController controller = getController(cms, resource, req, res, false, true);
261
262         // dispatch to the JSP
263
byte[] result = dispatchJsp(controller);
264
265         // remove the controller from the request
266
CmsFlexController.removeController(req);
267
268         // return the contents
269
return result;
270     }
271
272     /**
273      * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration()
274      */

275     public Map JavaDoc getConfiguration() {
276
277         // return the configuration in an immutable form
278
return Collections.unmodifiableMap(m_configuration);
279     }
280
281     /**
282      * @see org.opencms.loader.I_CmsResourceLoader#getLoaderId()
283      */

284     public int getLoaderId() {
285
286         return RESOURCE_LOADER_ID;
287     }
288
289     /**
290      * Return a String describing the ResourceLoader,
291      * which is (localized to the system default locale)
292      * <code>"The OpenCms default resource loader for JSP".</code>
293      *
294      * @return a describing String for the ResourceLoader
295      */

296     public String JavaDoc getResourceLoaderInfo() {
297
298         return Messages.get().getBundle().key(Messages.GUI_LOADER_JSP_DEFAULT_DESC_0);
299     }
300
301     /**
302      * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration()
303      */

304     public void initConfiguration() {
305
306         ExtendedProperties config = new ExtendedProperties();
307         config.putAll(m_configuration);
308
309         m_jspRepository = config.getString(PARAM_JSP_REPOSITORY);
310         if (m_jspRepository == null) {
311             m_jspRepository = OpenCms.getSystemInfo().getWebApplicationRfsPath();
312         }
313         m_jspWebAppRepository = config.getString(PARAM_JSP_FOLDER, DEFAULT_JSP_FOLDER);
314         if (!m_jspWebAppRepository.endsWith("/")) {
315             m_jspWebAppRepository += "/";
316         }
317         m_jspRepository = CmsFileUtil.normalizePath(m_jspRepository + m_jspWebAppRepository);
318
319         String JavaDoc maxAge = config.getString(PARAM_CLIENT_CACHE_MAXAGE);
320         if (maxAge == null) {
321             m_clientCacheMaxAge = -1;
322         } else {
323             m_clientCacheMaxAge = Long.parseLong(maxAge);
324         }
325
326         // get the "error pages are commited or not" flag from the configuration
327
m_errorPagesAreNotCommited = config.getBoolean(PARAM_JSP_ERRORPAGE_COMMITTED, true);
328
329         // output setup information
330
if (CmsLog.INIT.isInfoEnabled()) {
331             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_JSP_REPOSITORY_ABS_PATH_1, m_jspRepository));
332             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WEBAPP_PATH_1, m_jspWebAppRepository));
333             CmsLog.INIT.info(Messages.get().getBundle().key(
334                 Messages.INIT_JSP_REPOSITORY_ERR_PAGE_COMMOTED_1,
335                 new Boolean JavaDoc(m_errorPagesAreNotCommited)));
336             if (maxAge != null) {
337                 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_CLIENT_CACHE_MAX_AGE_1, maxAge));
338             }
339
340             CmsLog.INIT.info(Messages.get().getBundle().key(
341                 Messages.INIT_LOADER_INITIALIZED_1,
342                 this.getClass().getName()));
343         }
344     }
345
346     /**
347      * @see org.opencms.loader.I_CmsResourceLoader#isStaticExportEnabled()
348      */

349     public boolean isStaticExportEnabled() {
350
351         return true;
352     }
353
354     /**
355      * @see org.opencms.loader.I_CmsResourceLoader#isStaticExportProcessable()
356      */

357     public boolean isStaticExportProcessable() {
358
359         return true;
360     }
361
362     /**
363      * @see org.opencms.loader.I_CmsResourceLoader#isUsableForTemplates()
364      */

365     public boolean isUsableForTemplates() {
366
367         return true;
368     }
369
370     /**
371      * @see org.opencms.loader.I_CmsResourceLoader#isUsingUriWhenLoadingTemplate()
372      */

373     public boolean isUsingUriWhenLoadingTemplate() {
374
375         return false;
376     }
377
378     /**
379      * @see org.opencms.loader.I_CmsResourceLoader#load(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
380      */

381     public void load(CmsObject cms, CmsResource file, HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res)
382     throws ServletException JavaDoc, IOException JavaDoc, CmsException {
383
384         // load and process the JSP
385
boolean streaming = false;
386         boolean bypass = false;
387
388         // read "cache" property for requested VFS resource to check for special "stream" and "bypass" values
389
String JavaDoc cacheProperty = cms.readPropertyObject(file, CmsPropertyDefinition.PROPERTY_CACHE, true).getValue();
390         if (cacheProperty != null) {
391             cacheProperty = cacheProperty.trim();
392             if (CACHE_PROPERTY_STREAM.equals(cacheProperty)) {
393                 streaming = true;
394             } else if (CACHE_PROPERTY_BYPASS.equals(cacheProperty)) {
395                 streaming = true;
396                 bypass = true;
397             }
398         }
399
400         // get the Flex controller
401
CmsFlexController controller = getController(cms, file, req, res, streaming, true);
402
403         if (bypass || controller.isForwardMode()) {
404             // once in forward mode, always in forward mode (for this request)
405
controller.setForwardMode(true);
406             // bypass Flex cache for this page, update the JSP first if neccessary
407
String JavaDoc target = updateJsp(file, controller, new HashSet JavaDoc());
408             // dispatch to external JSP
409
req.getRequestDispatcher(target).forward(controller.getCurrentRequest(), res);
410         } else {
411             // Flex cache not bypassed, dispatch to internal JSP
412
dispatchJsp(controller);
413         }
414
415         // remove the controller from the request if not forwarding
416
if (!controller.isForwardMode()) {
417             CmsFlexController.removeController(req);
418         }
419     }
420
421     /**
422      * @see org.opencms.loader.I_CmsResourceLoader#service(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.ServletRequest, javax.servlet.ServletResponse)
423      */

424     public void service(CmsObject cms, CmsResource resource, ServletRequest JavaDoc req, ServletResponse JavaDoc res)
425     throws ServletException JavaDoc, IOException JavaDoc, CmsLoaderException {
426
427         CmsFlexController controller = CmsFlexController.getController(req);
428         // get JSP target name on "real" file system
429
String JavaDoc target = updateJsp(resource, controller, new HashSet JavaDoc(8));
430         // important: Indicate that all output must be buffered
431
controller.getCurrentResponse().setOnlyBuffering(true);
432         // dispatch to external file
433
controller.getCurrentRequest().getRequestDispatcherToExternal(cms.getSitePath(resource), target).include(
434             req,
435             res);
436     }
437
438     /**
439      * @see org.opencms.loader.I_CmsFlexCacheEnabledLoader#setFlexCache(org.opencms.flex.CmsFlexCache)
440      */

441     public void setFlexCache(CmsFlexCache cache) {
442
443         m_cache = cache;
444         // output setup information
445
if (CmsLog.INIT.isInfoEnabled()) {
446             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ADD_FLEX_CACHE_0));
447         }
448     }
449
450     /**
451      * Dispatches the current request to the OpenCms internal JSP.<p>
452      *
453      * @param controller the current controller
454      *
455      * @return the content of the processed JSP
456      *
457      * @throws ServletException if inclusion does not work
458      * @throws IOException if inclusion does not work
459      */

460     private byte[] dispatchJsp(CmsFlexController controller) throws ServletException JavaDoc, IOException JavaDoc {
461
462         // get request / response wrappers
463
CmsFlexRequest f_req = controller.getCurrentRequest();
464         CmsFlexResponse f_res = controller.getCurrentResponse();
465
466         try {
467             f_req.getRequestDispatcher(controller.getCmsObject().getSitePath(controller.getCmsResource())).include(
468                 f_req,
469                 f_res);
470         } catch (SocketException JavaDoc e) {
471             // uncritical, might happen if client (browser) does not wait until end of page delivery
472
LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e);
473         }
474
475         byte[] result = null;
476         HttpServletResponse JavaDoc res = controller.getTopResponse();
477
478         if (!controller.isStreaming() && !f_res.isSuspended()) {
479             try {
480                 // if a JSP errorpage was triggered the response will be already committed here
481
if (!res.isCommitted() || m_errorPagesAreNotCommited) {
482
483                     // check if the current request was done by a workplace user
484
boolean isWorkplaceUser = CmsWorkplaceManager.isWorkplaceUser(f_req);
485
486                     // check if the content was modified since the last request
487
if (controller.isTop()
488                         && !isWorkplaceUser
489                         && CmsFlexController.isNotModifiedSince(f_req, controller.getDateLastModified())) {
490                         if (f_req.getParameterMap().size() == 0) {
491                             // only use "expires" header on pages that have no parameters,
492
// otherwise some browsers (e.g. IE 6) will not even try to request
493
// updated versions of the page
494
CmsFlexController.setDateExpiresHeader(
495                                 res,
496                                 controller.getDateExpires(),
497                                 m_clientCacheMaxAge);
498                         }
499                         res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
500                         return null;
501                     }
502
503                     // get the result byte array
504
result = f_res.getWriterBytes();
505                     HttpServletRequest JavaDoc req = controller.getTopRequest();
506                     if (req.getHeader(CmsRequestUtil.HEADER_OPENCMS_EXPORT) != null) {
507                         // this is a non "on-demand" static export request, don't write to the response stream
508
req.setAttribute(CmsRequestUtil.HEADER_OPENCMS_EXPORT, new Long JavaDoc(
509                             controller.getDateLastModified()));
510                     } else if (controller.isTop()) {
511                         // process headers and write output if this is the "top" request/response
512
res.setContentLength(result.length);
513                         // check for preset error code
514
Integer JavaDoc errorCode = (Integer JavaDoc)req.getAttribute(CmsRequestUtil.ATTRIBUTE_ERRORCODE);
515                         if (errorCode == null) {
516                             // set last modified / no cache headers only if this is not an error page
517
if (isWorkplaceUser) {
518                                 res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, System.currentTimeMillis());
519                                 CmsRequestUtil.setNoCacheHeaders(res);
520                             } else {
521                                 // set date last modified header
522
CmsFlexController.setDateLastModifiedHeader(res, controller.getDateLastModified());
523                                 if ((f_req.getParameterMap().size() == 0) && (controller.getDateLastModified() > -1)) {
524                                     // only use "expires" header on pages that have no parameters
525
// and that are cachable (i.e. 'date last modified' is set)
526
// otherwise some browsers (e.g. IE 6) will not even try to request
527
// updated versions of the page
528
CmsFlexController.setDateExpiresHeader(
529                                         res,
530                                         controller.getDateExpires(),
531                                         m_clientCacheMaxAge);
532                                 }
533                             }
534                             // set response status to "200 - OK" (required for static export "on-demand")
535
res.setStatus(HttpServletResponse.SC_OK);
536                         } else {
537                             // set previously saved error code
538
res.setStatus(errorCode.intValue());
539                         }
540                         // proecess the headers
541
CmsFlexResponse.processHeaders(f_res.getHeaders(), res);
542                         res.getOutputStream().write(result);
543                         res.getOutputStream().flush();
544                     }
545                 }
546             } catch (IllegalStateException JavaDoc e) {
547                 // uncritical, might happen if JSP error page was used
548
LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e);
549             } catch (SocketException JavaDoc e) {
550                 // uncritical, might happen if client (browser) does not wait until end of page delivery
551
LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e);
552             }
553         }
554
555         return result;
556     }
557
558     /**
559      * Delivers a Flex controller, either by creating a new one, or by re-using an existing one.<p>
560      *
561      * @param cms the initial CmsObject to wrap in the controller
562      * @param resource the resource requested
563      * @param req the current request
564      * @param res the current response
565      * @param streaming indicates if the response is streaming
566      * @param top indicates if the response is the top response
567      *
568      * @return a Flex controller
569      */

570     private CmsFlexController getController(
571         CmsObject cms,
572         CmsResource resource,
573         HttpServletRequest JavaDoc req,
574         HttpServletResponse JavaDoc res,
575         boolean streaming,
576         boolean top) {
577
578         CmsFlexController controller = null;
579         if (top) {
580             // only check for existing contoller if this is the "top" request/response
581
controller = CmsFlexController.getController(req);
582         }
583         if (controller == null) {
584             // create new request / response wrappers
585
controller = new CmsFlexController(cms, resource, m_cache, req, res, streaming, top);
586             CmsFlexController.setController(req, controller);
587             CmsFlexRequest f_req = new CmsFlexRequest(req, controller);
588             CmsFlexResponse f_res = new CmsFlexResponse(res, controller, streaming, true);
589             controller.push(f_req, f_res);
590         } else if (controller.isForwardMode()) {
591             // reset CmsObject (because of URI) if in forward mode
592
controller = new CmsFlexController(cms, controller);
593             CmsFlexController.setController(req, controller);
594         }
595         return controller;
596     }
597
598     /**
599      * Parses the JSP and modifies OpenCms critical directive information.<p>
600      *
601      * @param byteContent the original JSP content
602      * @param encoding the encoding to use for the JSP
603      * @param controller the controller for the JSP integration
604      * @param includes a Set containing all JSP pages that have been already updated
605      * @param isHardInclude indicated if this page is actually a "hard" include with <code>&lt;%@ include file="..." &gt;</code>
606      * @return the modified JSP content
607      * @throws UnsupportedEncodingException
608      */

609     private byte[] parseJsp(
610         byte[] byteContent,
611         String JavaDoc encoding,
612         CmsFlexController controller,
613         Set JavaDoc includes,
614         boolean isHardInclude) {
615
616         String JavaDoc content;
617         // make sure encoding is set correctly
618
try {
619             content = new String JavaDoc(byteContent, encoding);
620         } catch (UnsupportedEncodingException JavaDoc e) {
621             // encoding property is not set correctly
622
LOG.error(Messages.get().getBundle().key(
623                 Messages.LOG_UNSUPPORTED_ENC_1,
624                 controller.getCurrentRequest().getElementUri()), e);
625             try {
626                 encoding = OpenCms.getSystemInfo().getDefaultEncoding();
627                 content = new String JavaDoc(byteContent, encoding);
628             } catch (UnsupportedEncodingException JavaDoc e2) {
629                 // should not happen since default encoding is always a valid encoding (checked during system startup)
630
content = new String JavaDoc(byteContent);
631             }
632         }
633
634         // parse for special <%@cms file="..." %> tag
635
content = parseJspCmsTag(content, controller, includes);
636         // parse for included files in tags
637
content = parseJspIncludes(content, controller, includes);
638         // parse for <%@page pageEncoding="..." %> tag
639
content = parseJspEncoding(content, encoding, isHardInclude);
640         // convert the result to bytes and return it
641
try {
642             return content.getBytes(encoding);
643         } catch (UnsupportedEncodingException JavaDoc e) {
644             // should not happen since encoding was already checked
645
return content.getBytes();
646         }
647     }
648
649     /**
650      * Parses the JSP content for the special <code>&lt;%cms file="..." %&gt;</code> tag.<p>
651      *
652      * @param content the JSP content to parse
653      * @param controller the current JSP controller
654      * @param includes a set of already parsed includes
655      * @return the parsed JSP content
656      */

657     private String JavaDoc parseJspCmsTag(String JavaDoc content, CmsFlexController controller, Set JavaDoc includes) {
658
659         // check if a JSP directive occurs in the file
660
int i1 = content.indexOf(DIRECTIVE_START);
661         if (i1 < 0) {
662             // no directive occurs
663
return content;
664         }
665
666         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(content.length());
667         int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length(), elen = DIRECTIVE_END.length();
668
669         while (i1 >= 0) {
670             // parse the file and replace JSP filename references
671
i2 = content.indexOf(DIRECTIVE_END, i1 + slen);
672             if (i2 < 0) {
673                 // wrong syntax (missing end directive) - let the JSP compiler produce the error message
674
return content;
675             } else if (i2 > i1) {
676                 String JavaDoc directive = content.substring(i1 + slen, i2);
677                 if (LOG.isDebugEnabled()) {
678                     LOG.debug(Messages.get().getBundle().key(
679                         Messages.LOG_DIRECTIVE_DETECTED_3,
680                         DIRECTIVE_START,
681                         directive,
682                         DIRECTIVE_END));
683                 }
684
685                 int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0;
686                 while (directive.charAt(t1) == ' ') {
687                     t1++;
688                 }
689                 String JavaDoc argument = null;
690                 if (directive.startsWith("cms", t1)) {
691                     if (LOG.isDebugEnabled()) {
692                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "cms"));
693                     }
694                     t2 = directive.indexOf("file", t1 + 3);
695                     t5 = 4;
696                 }
697
698                 if (t2 > 0) {
699                     String JavaDoc sub = directive.substring(t2 + t5);
700                     char c1 = sub.charAt(t3);
701                     while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) {
702                         c1 = sub.charAt(++t3);
703                     }
704                     t4 = t3;
705                     while (c1 != '"') {
706                         c1 = sub.charAt(++t4);
707                     }
708                     if (t4 > t3) {
709                         argument = sub.substring(t3, t4);
710                     }
711                     if (LOG.isDebugEnabled()) {
712                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument));
713                     }
714                 }
715
716                 if (argument != null) {
717                     // try to update the referenced file
718
String JavaDoc jspname = updateJsp(argument, controller, includes);
719                     if (jspname != null) {
720                         directive = jspname;
721                         if (LOG.isDebugEnabled()) {
722                             LOG.debug(Messages.get().getBundle().key(
723                                 Messages.LOG_DIRECTIVE_CHANGED_3,
724                                 DIRECTIVE_START,
725                                 directive,
726                                 DIRECTIVE_END));
727                         }
728                     }
729                     // cms directive was found
730
buf.append(content.substring(p0, i1));
731                     buf.append(directive);
732                     p0 = i2 + elen;
733                     i1 = content.indexOf(DIRECTIVE_START, p0);
734                 } else {
735                     // cms directive was not found
736
buf.append(content.substring(p0, i1 + slen));
737                     buf.append(directive);
738                     p0 = i2;
739                     i1 = content.indexOf(DIRECTIVE_START, p0);
740                 }
741             }
742         }
743         if (i2 > 0) {
744             // the content of the JSP was changed
745
buf.append(content.substring(p0, content.length()));
746             content = buf.toString();
747         }
748         return content;
749     }
750
751     /**
752      * Parses the JSP content for the <code>&lt;%page pageEncoding="..." %&gt;</code> tag
753      * and ensures that the JSP page encoding is set according to the OpenCms
754      * "content-encoding" property value of the JSP.<p>
755      *
756      * @param content the JSP content to parse
757      * @param encoding the encoding to use for the JSP
758      * @param isHardInclude indicated if this page is actually a "hard" include with <code>&lt;%@ include file="..." &gt;</code>
759      * @return the parsed JSP content
760      */

761     private String JavaDoc parseJspEncoding(String JavaDoc content, String JavaDoc encoding, boolean isHardInclude) {
762
763         // check if a JSP directive occurs in the file
764
int i1 = content.indexOf(DIRECTIVE_START);
765         if (i1 < 0) {
766             // no directive occurs
767
if (isHardInclude) {
768                 return content;
769             }
770         }
771
772         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(content.length() + 64);
773         int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length();
774         boolean found = false;
775
776         if (i1 < 0) {
777             // no directive found at all, append content to buffer
778
buf.append(content);
779         }
780
781         while (i1 >= 0) {
782             // parse the file and set/replace page encoding
783
i2 = content.indexOf(DIRECTIVE_END, i1 + slen);
784             if (i2 < 0) {
785                 // wrong syntax (missing end directive) - let the JSP compiler produce the error message
786
return content;
787             } else if (i2 > i1) {
788                 String JavaDoc directive = content.substring(i1 + slen, i2);
789                 if (LOG.isDebugEnabled()) {
790                     LOG.debug(Messages.get().getBundle().key(
791                         Messages.LOG_DIRECTIVE_DETECTED_3,
792                         DIRECTIVE_START,
793                         directive,
794                         DIRECTIVE_END));
795                 }
796
797                 int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0;
798                 while (directive.charAt(t1) == ' ') {
799                     t1++;
800                 }
801                 String JavaDoc argument = null;
802                 if (directive.startsWith("page", t1)) {
803                     if (LOG.isDebugEnabled()) {
804                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "page"));
805                     }
806                     t2 = directive.indexOf("pageEncoding", t1 + 4);
807                     t5 = 12;
808                     if (t2 > 0) {
809                         found = true;
810                     }
811                 }
812
813                 if (t2 > 0) {
814                     String JavaDoc sub = directive.substring(t2 + t5);
815                     char c1 = sub.charAt(t3);
816                     while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) {
817                         c1 = sub.charAt(++t3);
818                     }
819                     t4 = t3;
820                     while (c1 != '"') {
821                         c1 = sub.charAt(++t4);
822                     }
823                     if (t4 > t3) {
824                         argument = sub.substring(t3, t4);
825                     }
826                     if (LOG.isDebugEnabled()) {
827                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument));
828                     }
829                 }
830
831                 if (argument != null) {
832                     // a pageEncoding setting was found, changes have to be made
833
String JavaDoc pre = directive.substring(0, t2 + t3 + t5);
834                     String JavaDoc suf = directive.substring(t2 + t3 + t5 + argument.length());
835                     // change the encoding
836
directive = pre + encoding + suf;
837                     if (LOG.isDebugEnabled()) {
838                         LOG.debug(Messages.get().getBundle().key(
839                             Messages.LOG_DIRECTIVE_CHANGED_3,
840                             DIRECTIVE_START,
841                             directive,
842                             DIRECTIVE_END));
843                     }
844                 }
845
846                 buf.append(content.substring(p0, i1 + slen));
847                 buf.append(directive);
848                 p0 = i2;
849                 i1 = content.indexOf(DIRECTIVE_START, p0);
850             }
851         }
852         if (i2 > 0) {
853             // the content of the JSP was changed
854
buf.append(content.substring(p0, content.length()));
855         }
856         if (found) {
857             content = buf.toString();
858         } else if (!isHardInclude) {
859             // encoding setting was not found
860
// if this is not a "hard" include then add the encoding to the top of the page
861
// checking for the hard include is important to prevent errors with
862
// multiple page encoding settings if a templete is composed from several hard included elements
863
// this is an issue in Tomcat 4.x but not 5.x
864
StringBuffer JavaDoc buf2 = new StringBuffer JavaDoc(buf.length() + 32);
865             buf2.append("<%@ page pageEncoding=\"");
866             buf2.append(encoding);
867             buf2.append("\" %>");
868             buf2.append(buf);
869             content = buf2.toString();
870         }
871         return content;
872     }
873
874     /**
875      * Parses the JSP content for includes and replaces all OpenCms VFS
876      * path information with information for the real FS.<p>
877      *
878      * @param content the JSP content to parse
879      * @param controller the current JSP controller
880      * @param includes a set of already parsed includes
881      * @return the parsed JSP content
882      */

883     private String JavaDoc parseJspIncludes(String JavaDoc content, CmsFlexController controller, Set JavaDoc includes) {
884
885         // check if a JSP directive occurs in the file
886
int i1 = content.indexOf(DIRECTIVE_START);
887         if (i1 < 0) {
888             // no directive occurs
889
return content;
890         }
891
892         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(content.length());
893         int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length();
894
895         while (i1 >= 0) {
896             // parse the file and replace JSP filename references
897
i2 = content.indexOf(DIRECTIVE_END, i1 + slen);
898             if (i2 < 0) {
899                 // wrong syntax (missing end directive) - let the JSP compiler produce the error message
900
return content;
901             } else if (i2 > i1) {
902                 String JavaDoc directive = content.substring(i1 + slen, i2);
903                 if (LOG.isDebugEnabled()) {
904                     LOG.debug(Messages.get().getBundle().key(
905                         Messages.LOG_DIRECTIVE_DETECTED_3,
906                         DIRECTIVE_START,
907                         directive,
908                         DIRECTIVE_END));
909                 }
910
911                 int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0;
912                 while (directive.charAt(t1) == ' ') {
913                     t1++;
914                 }
915                 String JavaDoc argument = null;
916                 if (directive.startsWith("include", t1)) {
917                     if (LOG.isDebugEnabled()) {
918                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "include"));
919                     }
920                     t2 = directive.indexOf("file", t1 + 7);
921                     t5 = 6;
922                 } else if (directive.startsWith("page", t1)) {
923                     if (LOG.isDebugEnabled()) {
924                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "page"));
925                     }
926                     t2 = directive.indexOf("errorPage", t1 + 4);
927                     t5 = 11;
928                 }
929
930                 if (t2 > 0) {
931                     String JavaDoc sub = directive.substring(t2 + t5);
932                     char c1 = sub.charAt(t3);
933                     while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) {
934                         c1 = sub.charAt(++t3);
935                     }
936                     t4 = t3;
937                     while (c1 != '"') {
938                         c1 = sub.charAt(++t4);
939                     }
940                     if (t4 > t3) {
941                         argument = sub.substring(t3, t4);
942                     }
943                     if (LOG.isDebugEnabled()) {
944                         LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument));
945                     }
946                 }
947
948                 if (argument != null) {
949                     // a file was found, changes have to be made
950
String JavaDoc pre = directive.substring(0, t2 + t3 + t5);
951                     String JavaDoc suf = directive.substring(t2 + t3 + t5 + argument.length());
952                     // now try to update the referenced file
953
String JavaDoc jspname = updateJsp(argument, controller, includes);
954                     if (jspname != null) {
955                         // only change something in case no error had occured
956
directive = pre + jspname + suf;
957                         if (LOG.isDebugEnabled()) {
958                             LOG.debug(Messages.get().getBundle().key(
959                                 Messages.LOG_DIRECTIVE_CHANGED_3,
960                                 DIRECTIVE_START,
961                                 directive,
962                                 DIRECTIVE_END));
963                         }
964                     }
965                 }
966
967                 buf.append(content.substring(p0, i1 + slen));
968                 buf.append(directive);
969                 p0 = i2;
970                 i1 = content.indexOf(DIRECTIVE_START, p0);
971             }
972         }
973         if (i2 > 0) {
974             // the content of the JSP was changed
975
buf.append(content.substring(p0, content.length()));
976             content = buf.toString();
977         }
978         return content;
979     }
980
981     /**
982      * Updates a JSP page in the "real" file system in case the VFS resource has changed.<p>
983      *
984      * Also processes the <code>&lt;%@ cms %&gt;</code> tags before the JSP is written to the real FS.
985      * Also recursivly updates all files that are referenced by a <code>&lt;%@ cms %&gt;</code> tag
986      * on this page to make sure the file actually exists in the real FS.
987      * All <code>&lt;%@ include %&gt;</code> tags are parsed and the name in the tag is translated
988      * from the OpenCms VFS path to the path in the real FS.
989      * The same is done for filenames in <code>&lt;%@ page errorPage=... %&gt;</code> tags.
990      *
991      * @param resource the reqested JSP file resource in the VFS
992      * @param controller the controller for the JSP integration
993      * @param updates a Set containing all JSP pages that have been already updated
994      * @return the file name of the updated JSP in the "real" FS
995      *
996      * @throws ServletException might be thrown in the process of including the JSP
997      * @throws IOException might be thrown in the process of including the JSP
998      * @throws CmsLoaderException if the resource type can not be read
999      */

1000    private String JavaDoc updateJsp(CmsResource resource, CmsFlexController controller, Set JavaDoc updates)
1001    throws IOException JavaDoc, ServletException JavaDoc, CmsLoaderException {
1002
1003        String JavaDoc jspVfsName = resource.getRootPath();
1004        String JavaDoc extension;
1005        boolean isHardInclude;
1006        int loaderId = OpenCms.getResourceManager().getResourceType(resource.getTypeId()).getLoaderId();
1007        if ((loaderId == CmsJspLoader.RESOURCE_LOADER_ID) && (!jspVfsName.endsWith(JSP_EXTENSION))) {
1008            // this is a true JSP resource that does not end with ".jsp"
1009
extension = JSP_EXTENSION;
1010            isHardInclude = false;
1011        } else {
1012            // not a JSP resource or already ends with ".jsp"
1013
extension = "";
1014            // if this is a JSP we don't treat it as hard include
1015
isHardInclude = (loaderId != CmsJspLoader.RESOURCE_LOADER_ID);
1016        }
1017
1018        String JavaDoc jspTargetName = CmsFileUtil.getRepositoryName(
1019            m_jspWebAppRepository,
1020            jspVfsName + extension,
1021            controller.getCurrentRequest().isOnline());
1022
1023        // check if page was already updated
1024
if (updates.contains(jspTargetName)) {
1025            // no need to write the already included file to the real FS more then once
1026
return jspTargetName;
1027        }
1028        updates.add(jspTargetName);
1029
1030        String JavaDoc jspPath = CmsFileUtil.getRepositoryName(
1031            m_jspRepository,
1032            jspVfsName + extension,
1033            controller.getCurrentRequest().isOnline());
1034
1035        File d = new File(jspPath).getParentFile();
1036        if ((d == null) || (d.exists() && !(d.isDirectory() && d.canRead()))) {
1037            CmsMessageContainer message = Messages.get().container(Messages.LOG_ACCESS_DENIED_1, jspPath);
1038            LOG.error(message.key());
1039            // can not continue
1040
throw new ServletException JavaDoc(message.key());
1041        }
1042
1043        if (!d.exists()) {
1044            // create directory structure
1045
d.mkdirs();
1046        }
1047
1048        // check if the JSP muse be updated
1049
boolean mustUpdate = false;
1050        File f = new File(jspPath);
1051        if (!f.exists()) {
1052            // file does not exist in real FS
1053
mustUpdate = true;
1054            
1055            // make sure the parent folder exists
1056
File folder = f.getParentFile();
1057            if (!folder.exists()) {
1058                boolean success = folder.mkdirs();
1059                if (!success) {
1060                    LOG.error(org.opencms.db.Messages.get().getBundle().key(
1061                            org.opencms.db.Messages.LOG_CREATE_FOLDER_FAILED_1,
1062                            folder.getAbsolutePath()));
1063                }
1064            }
1065        } else if (f.lastModified() <= resource.getDateLastModified()) {
1066            // file in real FS is older then file in VFS
1067
mustUpdate = true;
1068        } else if (controller.getCurrentRequest().isDoRecompile()) {
1069            // recompile is forced with parameter
1070
mustUpdate = true;
1071        }
1072
1073        if (mustUpdate) {
1074            if (LOG.isDebugEnabled()) {
1075                LOG.debug(Messages.get().getBundle().key(Messages.LOG_WRITING_JSP_1, jspTargetName));
1076            }
1077            byte[] contents;
1078            String JavaDoc encoding;
1079            try {
1080                CmsObject cms = controller.getCmsObject();
1081                contents = CmsFile.upgrade(resource, cms).getContents();
1082                // check the "content-encoding" property for the JSP, use system default if not found on path
1083
encoding = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue();
1084                if (encoding == null) {
1085                    encoding = OpenCms.getSystemInfo().getDefaultEncoding();
1086                } else {
1087                    encoding = CmsEncoder.lookupEncoding(encoding.trim(), encoding);
1088                }
1089            } catch (CmsException e) {
1090                controller.setThrowable(e, jspVfsName);
1091                throw new ServletException JavaDoc(
1092                    Messages.get().getBundle().key(Messages.ERR_LOADER_JSP_ACCESS_1, jspVfsName),
1093                    e);
1094            }
1095
1096            try {
1097                // parse the JSP and modify OpenCms critical directives
1098
contents = parseJsp(contents, encoding, controller, updates, isHardInclude);
1099                // write the parsed JSP content to the real FS
1100
synchronized (this) {
1101                    // this must be done only one file at a time
1102
FileOutputStream JavaDoc fs = new FileOutputStream JavaDoc(f);
1103                    fs.write(contents);
1104                    fs.close();
1105                }
1106                contents = null;
1107
1108                if (LOG.isInfoEnabled()) {
1109                    LOG.info(Messages.get().getBundle().key(Messages.LOG_UPDATED_JSP_2, jspTargetName, jspVfsName));
1110                }
1111            } catch (FileNotFoundException JavaDoc e) {
1112                throw new ServletException JavaDoc(
1113                    Messages.get().getBundle().key(Messages.ERR_LOADER_JSP_WRITE_1, f.getName()),
1114                    e);
1115            }
1116        }
1117
1118        // update "last modified" and "expires" date on controller
1119
controller.updateDates(f.lastModified(), CmsResource.DATE_EXPIRED_DEFAULT);
1120
1121        return jspTargetName;
1122    }
1123
1124    /**
1125     * Updates a JSP page in the "real" file system in case the VFS resource has changed based on the resource name.<p>
1126     *
1127     * Gernates a resource based on the provided name and calls {@link #updateJsp(CmsResource, CmsFlexController, Set)}.<p>
1128     *
1129     * @param vfsName the name of the JSP file resource in the VFS
1130     * @param controller the controller for the JSP integration
1131     * @param includes a Set containing all JSP pages that have been already included (i.e. updated)
1132     * @return the file name of the updated JSP in the "real" FS
1133     */

1134    private String JavaDoc updateJsp(String JavaDoc vfsName, CmsFlexController controller, Set JavaDoc includes) {
1135
1136        String JavaDoc jspVfsName = CmsLinkManager.getAbsoluteUri(vfsName, controller.getCurrentRequest().getElementRootPath());
1137        if (LOG.isDebugEnabled()) {
1138            LOG.debug(Messages.get().getBundle().key(Messages.LOG_UPDATE_JSP_1, jspVfsName));
1139        }
1140        String JavaDoc jspRfsName = null;
1141        try {
1142            // create an OpenCms user context that operates in the root site
1143
CmsObject cms = OpenCms.initCmsObject(controller.getCmsObject());
1144            cms.getRequestContext().setSiteRoot("");
1145            CmsResource includeResource = cms.readResource(jspVfsName);
1146            // make sure the jsp referenced file is generated
1147
jspRfsName = updateJsp(includeResource, controller, includes);
1148            if (LOG.isDebugEnabled()) {
1149                LOG.debug(Messages.get().getBundle().key(Messages.LOG_NAME_REAL_FS_1, jspRfsName));
1150            }
1151        } catch (Exception JavaDoc e) {
1152            jspRfsName = null;
1153            if (LOG.isDebugEnabled()) {
1154                LOG.debug(Messages.get().getBundle().key(Messages.LOG_ERR_UPDATE_1, jspVfsName), e);
1155            }
1156        }
1157        return jspRfsName;
1158    }
1159}
Popular Tags