Source code: com/opencms/flex/cache/CmsFlexRequestDispatcher.java
1 /*
2 * File : $Source: /usr/local/cvs/opencms/src/com/opencms/flex/cache/Attic/CmsFlexRequestDispatcher.java,v $
3 * Date : $Date: 2003/05/13 13:18:20 $
4 * Version: $Revision: 1.7.2.1 $
5 *
6 * This library is part of OpenCms -
7 * the Open Source Content Mananagement System
8 *
9 * Copyright (C) 2002 - 2003 Alkacon Software (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, 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 com.opencms.flex.cache;
33
34 import com.opencms.boot.I_CmsLogChannels;
35 import com.opencms.core.A_OpenCms;
36 import com.opencms.core.CmsException;
37 import com.opencms.file.CmsObject;
38
39 import java.io.IOException;
40
41 import javax.servlet.RequestDispatcher;
42 import javax.servlet.ServletException;
43 import javax.servlet.ServletRequest;
44 import javax.servlet.ServletResponse;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpServletResponse;
47
48 /**
49 * Implementation of the javax.servlet.RequestDispatcher interface to allow JSPs to be loaded
50 * from OpenCms.<p>
51 *
52 * This dispatcher will load data from 3 different data sources:
53 * <ol>
54 * <li>Form the "real" system Filesystem (e.g. for JSP pages)
55 * <li>From the OpenCms VFS
56 * <li>From the Flex cache
57 * </ol>
58 *
59 * @author Alexander Kandzior (a.kandzior@alkacon.com)
60 * @version $Revision: 1.7.2.1 $
61 */
62 public class CmsFlexRequestDispatcher implements RequestDispatcher {
63
64 /** The "real" RequestDispatcher, used when a true include (to the file system) is needed. */
65 private RequestDispatcher m_rd = null;
66
67 /** The OpenCms VFS target that will be included by the RequestDispatcher. */
68 private String m_vfs_target = null;
69
70 /** The external target that will be included by the RequestDispatcher, needed if this is not a dispatcher to a cms resource */
71 private String m_ext_target = null;
72
73 /** Internal DEBUG flag. Set to 9 for maximum verbosity. */
74 private static final int DEBUG = 0;
75
76 /**
77 * Creates a new instance of CmsFlexRequestDispatcher.<p>
78 *
79 * @param rd the "real" dispatcher, used for include call to file system
80 * @param vfs_target the cms resource that represents the external target
81 * @param ext_target the external target that the request will be dispatched to
82 */
83 public CmsFlexRequestDispatcher(
84 RequestDispatcher rd,
85 String vfs_target,
86 String ext_target
87 ) {
88 m_rd = rd;
89 m_vfs_target = vfs_target;
90 m_ext_target = ext_target;
91 }
92
93 /**
94 * Wrapper for the standard servlet API call.<p>
95 *
96 * Forward calls are actually NOT wrapped by OpenCms as of now.
97 * So they should not be used in JSP pages or servlets.<p>
98 *
99 * @param req the servlet request
100 * @param res the servlet response
101 * @throws ServletException in case something goes wrong
102 * @throws IOException in case something goes wrong
103 *
104 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
105 */
106 public void forward(
107 ServletRequest req,
108 ServletResponse res
109 ) throws ServletException, IOException {
110 m_rd.forward(req, res);
111 }
112
113 /**
114 * Wrapper for dispatching to a file from the OpenCms VFS.<p>
115 *
116 * This method will dispatch to cache, to real file system or
117 * to the OpenCms VFS, whatever is needed.<p>
118 *
119 * This method is much more complex then it sould be because of the internal standard
120 * buffering of JSP pages.
121 * Because of that I can not just intercept and buffer the stream, since I don't have
122 * access to it (it is wrapped internally in the JSP pages, which have their own buffer).
123 * That leads to a solution where the data is first written to the bufferd stream,
124 * but without includes. Then it is parsed again later
125 * (in response.processCacheEntry()), enriched with the
126 * included elements that have been ommitted in the first case.
127 * I would love to see a simpler solution, but this works for now.<p>
128 *
129 * @param req the servlet request
130 * @param res the servlet response
131 * @throws ServletException in case something goes wrong
132 * @throws IOException in case something goes wrong
133 */
134 public void include(
135 ServletRequest req,
136 ServletResponse res
137 ) throws ServletException, IOException {
138
139 if (DEBUG > 0) System.err.println("FlexDispatcher: Include called with target=" + m_vfs_target + " (ext_target=" + m_ext_target + ")");
140 CmsFlexController controller = (CmsFlexController)req.getAttribute(CmsFlexController.ATTRIBUTE_NAME);
141 CmsObject cms = controller.getCmsObject();
142
143 if ((m_ext_target == null) && (controller != null)) {
144 // Check if the file exists in the VFS, if not set external target
145 try {
146 cms.readFileHeader(m_vfs_target);
147 } catch (CmsException e) {
148 if (e.getType() == CmsException.C_NOT_FOUND) {
149 // File not found in VFS, treat it as external file
150 m_ext_target = m_vfs_target;
151 }
152 }
153 }
154
155 if ((m_ext_target != null) || (controller == null)) {
156 // This is an external include, probably to a JSP page, dispatch with system dispatcher
157 if (DEBUG > 0) System.err.println("FlexDispatcher: Dispatching to external target " + m_ext_target);
158 m_rd.include(req, res);
159 return;
160 }
161
162 CmsFlexCache cache = controller.getCmsCache();
163
164 // this is a request through the CMS
165 CmsFlexRequest f_req = controller.getCurrentRequest();
166 CmsFlexResponse f_res = controller.getCurrentResponse();
167
168 if (f_req.containsIncludeCall(m_vfs_target)) {
169 // This resource was already included earlier, so we have a (probably endless) inclusion loop
170 throw new ServletException("FlexDispatcher: Dectected inclusion loop for target " + m_vfs_target);
171 } else {
172 f_req.addInlucdeCall(m_vfs_target);
173 }
174
175 // Do nothing if response is already finished (probably as a result of an earlier redirect)
176 if (f_res.isSuspended()) return;
177
178 // Indicate to response that all further output or headers are result of include calls
179 f_res.setCmsIncludeMode(true);
180
181 // Create wrapper for request & response
182 CmsFlexRequest w_req = new CmsFlexRequest((HttpServletRequest)req, controller, m_vfs_target);
183 CmsFlexResponse w_res = new CmsFlexResponse((HttpServletResponse)res, controller);
184
185 // Push req/res to controller queue
186 controller.pushRequest(w_req);
187 controller.pushResponse(w_res);
188
189 CmsFlexCacheEntry entry = null;
190 if (f_req.isCacheable()) {
191 // Caching is on, check if requested resource is already in cache
192 entry = cache.get(w_req.getCmsCacheKey());
193 if (entry != null) {
194 // The target is already in the cache
195 try {
196 if (DEBUG > 0) System.err.println("FlexDispatcher: Loading file from cache for " + m_vfs_target);
197 entry.service(w_req, w_res);
198 } catch (com.opencms.core.CmsException e) {
199 throw new ServletException("FlexDispatcher: Error while loading file from cache for " + m_vfs_target + "\n" + e, e);
200 }
201 } else {
202 // Cache is on and resource is not yet cached, so we need to read the cache key for the response
203 CmsFlexCacheKey res_key = cache.getKey(CmsFlexCacheKey.getKeyName(m_vfs_target, w_req.isOnline()));
204 if (res_key != null) {
205 // Key already in cache, reuse it
206 w_res.setCmsCacheKey(res_key);
207 } else {
208 // Cache key is unknown, read key from properties
209 String cacheProperty = null;
210 try {
211 // Read caching property from requested VFS resource
212 cacheProperty = cms.readProperty(m_vfs_target, com.opencms.flex.I_CmsResourceLoader.C_LOADER_CACHEPROPERTY);
213 cache.putKey(w_res.setCmsCacheKey(m_vfs_target, cacheProperty, f_req.isOnline()));
214 } catch (com.opencms.core.CmsException e) {
215 if (e.getType() == CmsException.C_FLEX_CACHE) {
216 // Invalid key is ignored but logged, used key is cache=never
217 if (I_CmsLogChannels.C_LOGGING && A_OpenCms.isLogging(I_CmsLogChannels.C_OPENCMS_INFO))
218 A_OpenCms.log(I_CmsLogChannels.C_OPENCMS_INFO, "[FlexCache] Invalid cache key for external resource \"" + m_vfs_target + "\": " + cacheProperty);
219 // There will be a vaild key in the response ("cache=never") even after an exception
220 cache.putKey(w_res.getCmsCacheKey());
221 } else {
222 // All other errors are not handled here
223 throw new ServletException("FlexDispatcher: Error while loading cache properties for " + m_vfs_target + "\n" + e, e);
224 }
225 }
226 if (DEBUG > 1) System.err.println("FlexDispatcher: Cache properties for file " + m_vfs_target + " are: " + cacheProperty);
227 }
228 }
229 }
230
231 if (entry == null) {
232 // The target is not cached (or caching off), so load it with the internal resource loader
233 com.opencms.launcher.CmsLauncherManager manager = cms.getLauncherManager();
234 com.opencms.flex.I_CmsResourceLoader loader = null;
235
236 String variation = null;
237 // Check cache keys to see if the result can be cached
238 if (w_req.isCacheable()) variation = w_res.getCmsCacheKey().matchRequestKey(w_req.getCmsCacheKey());
239 // Indicate to the response if caching is not required
240 w_res.setCmsCachingRequired(variation != null);
241
242 com.opencms.file.CmsResource resource = null;
243 try {
244 resource = cms.readFileHeader(m_vfs_target);
245 int type = resource.getLauncherType();
246 if (DEBUG > 0) System.err.println("FlexDispatcher: Loading resource type " + type);
247 loader = (com.opencms.flex.I_CmsResourceLoader)manager.getLauncher(type);
248 } catch (java.lang.ClassCastException e) {
249 throw new ServletException("FlexDispatcher: CmsResourceLoader interface not implemented for cms resource " + m_vfs_target + "\n" + e, e);
250 } catch (com.opencms.core.CmsException e) {
251 // File might not exist or no read permissions
252 throw new ServletException("FlexDispatcher: Error while reading header for cms resource " + m_vfs_target + "\n" + e, e);
253 }
254
255 if (DEBUG > 0) System.err.println("FlexDispatcher: Internal call, loading file using loader.service() for " + m_vfs_target);
256 loader.service(cms, resource, w_req, w_res);
257
258 entry = w_res.processCacheEntry();
259 if ((entry != null) && (variation != null) && w_req.isCacheable()) {
260 cache.put(w_res.getCmsCacheKey(), entry, variation);
261 }
262 }
263
264 if (f_res.hasIncludeList()) {
265 // Special case: This indicates that the output was not yet displayed
266 java.util.Map headers = w_res.getHeaders();
267 byte[] result = w_res.getWriterBytes();
268 if (DEBUG > 3) System.err.println("Non-display include call - Result of include is:\n" + new String(result));
269 CmsFlexResponse.processHeaders(headers, f_res);
270 f_res.addToIncludeResults(result);
271 }
272
273 // Indicate to response that include is finished
274 f_res.setCmsIncludeMode(false);
275 f_req.removeIncludeCall(m_vfs_target);
276
277 // Pop req/res from controller queue
278 controller.popRequest();
279 controller.popResponse();
280 }
281 }