KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > flex > CmsFlexRequestDispatcher


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/flex/CmsFlexRequestDispatcher.java,v $
3  * Date : $Date: 2006/03/27 14:52:35 $
4  * Version: $Revision: 1.44 $
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.flex;
33
34 import org.opencms.file.CmsObject;
35 import org.opencms.file.CmsPropertyDefinition;
36 import org.opencms.file.CmsResource;
37 import org.opencms.file.CmsVfsResourceNotFoundException;
38 import org.opencms.loader.I_CmsResourceLoader;
39 import org.opencms.main.CmsException;
40 import org.opencms.main.CmsLog;
41 import org.opencms.main.OpenCms;
42
43 import java.io.IOException JavaDoc;
44
45 import javax.servlet.RequestDispatcher JavaDoc;
46 import javax.servlet.ServletException JavaDoc;
47 import javax.servlet.ServletRequest JavaDoc;
48 import javax.servlet.ServletResponse JavaDoc;
49 import javax.servlet.http.HttpServletRequest JavaDoc;
50 import javax.servlet.http.HttpServletResponse JavaDoc;
51
52 import org.apache.commons.logging.Log;
53
54 /**
55  * Implementation of the <code>{@link javax.servlet.RequestDispatcher}</code> interface to allow JSPs to be loaded
56  * from the OpenCms VFS.<p>
57  *
58  * This dispatcher will load data from 3 different data sources:
59  * <ol>
60  * <li>Form the "real" system Filesystem (e.g. for JSP pages)
61  * <li>From the OpenCms VFS
62  * <li>From the Flex cache
63  * </ol>
64  * <p>
65  *
66  * @author Alexander Kandzior
67  *
68  * @version $Revision: 1.44 $
69  *
70  * @since 6.0.0
71  */

72 public class CmsFlexRequestDispatcher implements RequestDispatcher JavaDoc {
73
74     /** The log object for this class. */
75     private static final Log LOG = CmsLog.getLog(CmsFlexRequestDispatcher.class);
76
77     /** The external target that will be included by the RequestDispatcher, needed if this is not a dispatcher to a cms resource. */
78     private String JavaDoc m_extTarget;
79
80     /** The "real" RequestDispatcher, used when a true include (to the file system) is needed. */
81     private RequestDispatcher JavaDoc m_rd;
82
83     /** The OpenCms VFS target that will be included by the RequestDispatcher. */
84     private String JavaDoc m_vfsTarget;
85
86     /**
87      * Creates a new instance of CmsFlexRequestDispatcher.<p>
88      *
89      * @param rd the "real" dispatcher, used for include call to file system
90      * @param vfs_target the cms resource that represents the external target
91      * @param ext_target the external target that the request will be dispatched to
92      */

93     public CmsFlexRequestDispatcher(RequestDispatcher JavaDoc rd, String JavaDoc vfs_target, String JavaDoc ext_target) {
94
95         m_rd = rd;
96         m_vfsTarget = vfs_target;
97         m_extTarget = ext_target;
98     }
99
100     /**
101      * Wrapper for the standard servlet API call.<p>
102      *
103      * Forward calls are actually NOT wrapped by OpenCms as of now.
104      * So they should not be used in JSP pages or servlets.<p>
105      *
106      * @param req the servlet request
107      * @param res the servlet response
108      * @throws ServletException in case something goes wrong
109      * @throws IOException in case something goes wrong
110      *
111      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
112      */

113     public void forward(ServletRequest JavaDoc req, ServletResponse JavaDoc res) throws ServletException JavaDoc, IOException JavaDoc {
114
115         CmsFlexController controller = CmsFlexController.getController(req);
116         controller.setForwardMode(true);
117         m_rd.forward(req, res);
118     }
119
120     /**
121      * Wrapper for dispatching to a file from the OpenCms VFS.<p>
122      *
123      * This method will dispatch to cache, to real file system or
124      * to the OpenCms VFS, whatever is needed.<p>
125      *
126      * This method is much more complex than it should be because of the internal standard
127      * buffering of JSP pages.
128      * Because of that I can not just intercept and buffer the stream, since I don't have
129      * access to it (it is wrapped internally in the JSP pages, which have their own buffer).
130      * That leads to a solution where the data is first written to the bufferd stream,
131      * but without includes. Then it is parsed again later
132      * in the <code>{@link CmsFlexResponse}</code>, enriched with the
133      * included elements that have been ommitted in the first case.
134      * I would love to see a simpler solution, but this works for now.<p>
135      *
136      * @param req the servlet request
137      * @param res the servlet response
138      *
139      * @throws ServletException in case something goes wrong
140      * @throws IOException in case something goes wrong
141      */

142     public void include(ServletRequest JavaDoc req, ServletResponse JavaDoc res) throws ServletException JavaDoc, IOException JavaDoc {
143
144         if (LOG.isDebugEnabled()) {
145             LOG.debug(Messages.get().getBundle().key(
146                 Messages.LOG_FLEXREQUESTDISPATCHER_INCLUDING_TARGET_2,
147                 m_vfsTarget,
148                 m_extTarget));
149         }
150
151         CmsFlexController controller = CmsFlexController.getController(req);
152         CmsObject cms = controller.getCmsObject();
153         CmsResource resource = null;
154
155         if ((m_extTarget == null) && (controller != null)) {
156             // check if the file exists in the VFS, if not set external target
157
try {
158                 resource = cms.readResource(m_vfsTarget);
159             } catch (CmsVfsResourceNotFoundException e) {
160                 // file not found in VFS, treat it as external file
161
m_extTarget = m_vfsTarget;
162             } catch (CmsException e) {
163                 // if other OpenCms exception occured we are in trouble
164
throw new ServletException JavaDoc(Messages.get().getBundle().key(
165                     Messages.ERR_FLEXREQUESTDISPATCHER_VFS_ACCESS_EXCEPTION_0), e);
166             }
167         }
168
169         if ((m_extTarget != null) || (controller == null)) {
170             includeExternal(req, res);
171         } else if (controller.isForwardMode()) {
172             includeInternalNoCache(req, res, controller, cms, resource);
173         } else {
174             includeInternalWithCache(req, res, controller, cms, resource);
175         }
176     }
177
178     /**
179      * Include an external (non-OpenCms) file using the standard dispatcher.<p>
180      *
181      * @param req the servlet request
182      * @param res the servlet response
183      * @throws ServletException in case something goes wrong
184      * @throws IOException in case something goes wrong
185      */

186     private void includeExternal(ServletRequest JavaDoc req, ServletResponse JavaDoc res) throws ServletException JavaDoc, IOException JavaDoc {
187
188         // This is an external include, probably to a JSP page, dispatch with system dispatcher
189
if (LOG.isDebugEnabled()) {
190             LOG.debug(Messages.get().getBundle().key(
191                 Messages.LOG_FLEXREQUESTDISPATCHER_INCLUDING_EXTERNAL_TARGET_1,
192                 m_extTarget));
193         }
194         m_rd.include(req, res);
195     }
196
197     /**
198      * Includes the requested resouce, ignoring the Flex cache.<p>
199      *
200      * @param req the servlet request
201      * @param res the servlet response
202      * @param controller the flex controller
203      * @param cms the cms context
204      * @param resource the requested resource (may be <code>null</code>)
205      *
206      * @throws ServletException in case something goes wrong
207      * @throws IOException in case something goes wrong
208      */

209     private void includeInternalNoCache(
210         ServletRequest JavaDoc req,
211         ServletResponse JavaDoc res,
212         CmsFlexController controller,
213         CmsObject cms,
214         CmsResource resource) throws ServletException JavaDoc, IOException JavaDoc {
215
216         // load target with the internal resource loader
217
I_CmsResourceLoader loader;
218
219         try {
220             if (resource == null) {
221                 resource = cms.readResource(m_vfsTarget);
222             }
223             if (LOG.isDebugEnabled()) {
224                 LOG.debug(Messages.get().getBundle().key(
225                     Messages.LOG_FLEXREQUESTDISPATCHER_LOADING_RESOURCE_TYPE_1,
226                     new Integer JavaDoc(resource.getTypeId())));
227             }
228             loader = OpenCms.getResourceManager().getLoader(resource);
229         } catch (CmsException e) {
230             // file might not exist or no read permissions
231
controller.setThrowable(e, m_vfsTarget);
232             throw new ServletException JavaDoc(Messages.get().getBundle().key(
233                 Messages.ERR_FLEXREQUESTDISPATCHER_ERROR_READING_RESOURCE_1,
234                 m_vfsTarget), e);
235         }
236
237         if (LOG.isDebugEnabled()) {
238             LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLEXREQUESTDISPATCHER_INCLUDE_RESOURCE_1, m_vfsTarget));
239         }
240         try {
241             loader.service(cms, resource, req, res);
242         } catch (CmsException e) {
243             // an error occured durion access to OpenCms
244
controller.setThrowable(e, m_vfsTarget);
245             throw new ServletException JavaDoc(e);
246         }
247     }
248
249     /**
250      * Includes the requested resouce, ignoring the Flex cache.<p>
251      *
252      * @param req the servlet request
253      * @param res the servlet response
254      * @param controller the Flex controller
255      * @param cms the current users OpenCms context
256      * @param resource the requested resource (may be <code>null</code>)
257      *
258      * @throws ServletException in case something goes wrong
259      * @throws IOException in case something goes wrong
260      */

261     private void includeInternalWithCache(
262         ServletRequest JavaDoc req,
263         ServletResponse JavaDoc res,
264         CmsFlexController controller,
265         CmsObject cms,
266         CmsResource resource) throws ServletException JavaDoc, IOException JavaDoc {
267
268         CmsFlexCache cache = controller.getCmsCache();
269
270         // this is a request through the CMS
271
CmsFlexRequest f_req = controller.getCurrentRequest();
272         CmsFlexResponse f_res = controller.getCurrentResponse();
273
274         if (f_req.containsIncludeCall(m_vfsTarget)) {
275             // this resource was already included earlier, so we have a (probably endless) inclusion loop
276
throw new ServletException JavaDoc(Messages.get().getBundle().key(
277                 Messages.ERR_FLEXREQUESTDISPATCHER_INCLUSION_LOOP_1,
278                 m_vfsTarget));
279         } else {
280             f_req.addInlucdeCall(m_vfsTarget);
281         }
282
283         // do nothing if response is already finished (probably as a result of an earlier redirect)
284
if (f_res.isSuspended()) {
285             // remove this include call if response is suspended (e.g. because of redirect)
286
f_res.setCmsIncludeMode(false);
287             f_req.removeIncludeCall(m_vfsTarget);
288             return;
289         }
290
291         // indicate to response that all further output or headers are result of include calls
292
f_res.setCmsIncludeMode(true);
293
294         // create wrapper for request & response
295
CmsFlexRequest w_req = new CmsFlexRequest((HttpServletRequest JavaDoc)req, controller, m_vfsTarget);
296         CmsFlexResponse w_res = new CmsFlexResponse((HttpServletResponse JavaDoc)res, controller);
297
298         // push req/res to controller stack
299
controller.push(w_req, w_res);
300
301         // now that the req/res are on the stack, we need to make sure that they are removed later
302
// that's why we have this try { ... } finaly { ... } clause here
303
try {
304             CmsFlexCacheEntry entry = null;
305             if (f_req.isCacheable()) {
306                 // caching is on, check if requested resource is already in cache
307
entry = cache.get(w_req.getCmsCacheKey());
308                 if (entry != null) {
309                     // the target is already in the cache
310
try {
311                         if (LOG.isDebugEnabled()) {
312                             LOG.debug(Messages.get().getBundle().key(
313                                 Messages.LOG_FLEXREQUESTDISPATCHER_LOADING_RESOURCE_FROM_CACHE_1,
314                                 m_vfsTarget));
315                         }
316                         controller.updateDates(entry.getDateLastModified(), entry.getDateExpires());
317                         entry.service(w_req, w_res);
318                     } catch (CmsException e) {
319                         Throwable JavaDoc t;
320                         if (e.getCause() != null) {
321                             t = e.getCause();
322                         } else {
323                             t = e;
324                         }
325                         t = controller.setThrowable(e, m_vfsTarget);
326                         throw new ServletException JavaDoc(Messages.get().getBundle().key(
327                             Messages.ERR_FLEXREQUESTDISPATCHER_ERROR_LOADING_RESOURCE_FROM_CACHE_1,
328                             m_vfsTarget), t);
329                     }
330                 } else {
331                     // cache is on and resource is not yet cached, so we need to read the cache key for the response
332
CmsFlexCacheKey res_key = cache.getKey(CmsFlexCacheKey.getKeyName(m_vfsTarget, w_req.isOnline()));
333                     if (res_key != null) {
334                         // key already in cache, reuse it
335
w_res.setCmsCacheKey(res_key);
336                     } else {
337                         // cache key is unknown, read key from properties
338
String JavaDoc cacheProperty = null;
339                         try {
340                             // read caching property from requested VFS resource
341
if (resource == null) {
342                                 resource = cms.readResource(m_vfsTarget);
343                             }
344                             cacheProperty = cms.readPropertyObject(resource, CmsPropertyDefinition.PROPERTY_CACHE, true).getValue();
345                             if (cacheProperty == null) {
346                                 // caching property not set, use default for resource type
347
cacheProperty = OpenCms.getResourceManager().getResourceType(resource.getTypeId()).getCachePropertyDefault();
348                             }
349                             cache.putKey(w_res.setCmsCacheKey(
350                                 cms.getRequestContext().addSiteRoot(m_vfsTarget),
351                                 cacheProperty,
352                                 f_req.isOnline()));
353                         } catch (CmsFlexCacheException e) {
354
355                             // invalid key is ignored but logged, used key is cache=never
356
if (LOG.isWarnEnabled()) {
357                                 LOG.warn(Messages.get().getBundle().key(
358                                     Messages.LOG_FLEXREQUESTDISPATCHER_INVALID_CACHE_KEY_2,
359                                     m_vfsTarget,
360                                     cacheProperty));
361                             }
362                             // there will be a vaild key in the response ("cache=never") even after an exception
363
cache.putKey(w_res.getCmsCacheKey());
364                         } catch (CmsException e) {
365
366                             // all other errors are not handled here
367
controller.setThrowable(e, m_vfsTarget);
368                             throw new ServletException JavaDoc(Messages.get().getBundle().key(
369                                 Messages.ERR_FLEXREQUESTDISPATCHER_ERROR_LOADING_CACHE_PROPERTIES_1,
370                                 m_vfsTarget), e);
371                         }
372                         if (LOG.isDebugEnabled()) {
373                             LOG.debug(Messages.get().getBundle().key(
374                                 Messages.LOG_FLEXREQUESTDISPATCHER_ADDING_CACHE_PROPERTIES_2,
375                                 m_vfsTarget,
376                                 cacheProperty));
377                         }
378                     }
379                 }
380             }
381
382             if (entry == null) {
383                 // the target is not cached (or caching off), so load it with the internal resource loader
384
I_CmsResourceLoader loader = null;
385
386                 String JavaDoc variation = null;
387                 // check cache keys to see if the result can be cached
388
if (w_req.isCacheable()) {
389                     variation = w_res.getCmsCacheKey().matchRequestKey(w_req.getCmsCacheKey());
390                 }
391                 // indicate to the response if caching is not required
392
w_res.setCmsCachingRequired(!controller.isForwardMode() && (variation != null));
393
394                 try {
395                     if (resource == null) {
396                         resource = cms.readResource(m_vfsTarget);
397                     }
398                     if (LOG.isDebugEnabled()) {
399                         LOG.debug(Messages.get().getBundle().key(
400                             Messages.LOG_FLEXREQUESTDISPATCHER_LOADING_RESOURCE_TYPE_1,
401                             new Integer JavaDoc(resource.getTypeId())));
402                     }
403                     loader = OpenCms.getResourceManager().getLoader(resource);
404                 } catch (ClassCastException JavaDoc e) {
405                     controller.setThrowable(e, m_vfsTarget);
406                     throw new ServletException JavaDoc(Messages.get().getBundle().key(
407                         Messages.ERR_FLEXREQUESTDISPATCHER_CLASSCAST_EXCEPTION_1,
408                         m_vfsTarget), e);
409                 } catch (CmsException e) {
410                     // file might not exist or no read permissions
411
controller.setThrowable(e, m_vfsTarget);
412                     throw new ServletException JavaDoc(Messages.get().getBundle().key(
413                         Messages.ERR_FLEXREQUESTDISPATCHER_ERROR_READING_RESOURCE_1,
414                         m_vfsTarget), e);
415                 }
416
417                 if (LOG.isDebugEnabled()) {
418                     LOG.debug(Messages.get().getBundle().key(
419                         Messages.LOG_FLEXREQUESTDISPATCHER_INCLUDE_RESOURCE_1,
420                         m_vfsTarget));
421                 }
422                 try {
423                     loader.service(cms, resource, w_req, w_res);
424                 } catch (CmsException e) {
425                     // an error occured durion access to OpenCms
426
controller.setThrowable(e, m_vfsTarget);
427                     throw new ServletException JavaDoc(e);
428                 }
429
430                 entry = w_res.processCacheEntry();
431                 if ((entry != null) && (variation != null) && w_req.isCacheable()) {
432                     // the result can be cached
433
if (w_res.getCmsCacheKey().getTimeout() > 0) {
434                         // cache entry has a timeout, set last modified to time of last creation
435
entry.setDateLastModifiedToPreviousTimeout(w_res.getCmsCacheKey().getTimeout());
436                         entry.setDateExpiresToNextTimeout(w_res.getCmsCacheKey().getTimeout());
437                         controller.updateDates(entry.getDateLastModified(), entry.getDateExpires());
438                     } else {
439                         // no timeout, use last modified date from files in VFS
440
entry.setDateLastModified(controller.getDateLastModified());
441                         entry.setDateExpires(controller.getDateExpires());
442                     }
443                     cache.put(w_res.getCmsCacheKey(), entry, variation);
444                 } else {
445                     // result can not be cached, do not use "last modified" optimization
446
controller.updateDates(-1, controller.getDateExpires());
447                 }
448             }
449
450             if (f_res.hasIncludeList()) {
451                 // special case: this indicates that the output was not yet displayed
452
java.util.Map JavaDoc headers = w_res.getHeaders();
453                 byte[] result = w_res.getWriterBytes();
454                 if (LOG.isDebugEnabled()) {
455                     LOG.debug(Messages.get().getBundle().key(
456                         Messages.LOG_FLEXREQUESTDISPATCHER_RESULT_1,
457                         new String JavaDoc(result)));
458                 }
459                 CmsFlexResponse.processHeaders(headers, f_res);
460                 f_res.addToIncludeResults(result);
461                 result = null;
462             }
463         } finally {
464             // indicate to response that include is finished
465
f_res.setCmsIncludeMode(false);
466             f_req.removeIncludeCall(m_vfsTarget);
467
468             // pop req/res from controller stack
469
controller.pop();
470         }
471     }
472 }
Popular Tags