Source code: com/opencms/flex/cache/CmsFlexRequest.java
1 /*
2 * File : $Source: /usr/local/cvs/opencms/src/com/opencms/flex/cache/Attic/CmsFlexRequest.java,v $
3 * Date : $Date: 2003/05/13 13:18:20 $
4 * Version: $Revision: 1.12.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.core.A_OpenCms;
35 import com.opencms.file.CmsObject;
36 import com.opencms.flex.CmsEvent;
37 import com.opencms.flex.I_CmsEventListener;
38
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.Enumeration;
42 import java.util.HashMap;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Set;
47
48 import javax.servlet.http.HttpServletRequest;
49 import javax.servlet.http.HttpServletRequestWrapper;
50
51 /**
52 * Wrapper class for a HttpServletRequest.<p>
53 *
54 * This class wrapps the standard HttpServletRequest so that it's output can be delivered to
55 * the CmsFlexCache.
56 *
57 * @author Alexander Kandzior (a.kandzior@alkacon.com)
58 * @version $Revision: 1.12.2.1 $
59 */
60 public class CmsFlexRequest extends HttpServletRequestWrapper {
61
62 /** The wrapped HttpServletRequest */
63 private HttpServletRequest m_req = null;
64
65 /** The requested resource (target resource) */
66 private String m_resource = null;
67
68 /** The CmsFlexCacheKey for this request */
69 private CmsFlexCacheKey m_key = null;
70
71 /** The CmsFlexController for this request */
72 private CmsFlexController m_controller = null;
73
74 /** Flag to decide if this request can be cached or not */
75 private boolean m_canCache = false;
76
77 /** Flag to check if this request is in the online project or not */
78 private boolean m_isOnline = false;
79
80 /** Flag to force a JSP recompile */
81 private boolean m_doRecompile = false;
82
83 /** Stores the request URI after it was once calculated */
84 private String m_requestUri = null;
85
86 /** Stores the request URI after it was once calculated */
87 private StringBuffer m_requestUrl = null;
88
89 /** Set of all include calls (to prevent an endless inclusion loop) */
90 private Set m_includeCalls;
91
92 /** Map of parameters from the original request */
93 private Map m_parameters = null;
94
95 /** Attribute name used for checking if _flex request parameters have already been processed */
96 public static String ATTRIBUTE_PROCESSED = "__com.opencms.flex.cache.CmsFlexRequest";
97
98 /** Debug flag */
99 private static final boolean DEBUG = false;
100
101 /**
102 * Creates a new CmsFlexRequest wrapper which is most likley the "Top"
103 * request wrapper, i.e. the wrapper that is constructed around the
104 * first "real" (not wrapped) request.<p>
105 *
106 * @param req the request to wrap
107 * @param controller the controller to use
108 */
109 public CmsFlexRequest(HttpServletRequest req, CmsFlexController controller) {
110 super(req);
111 m_req = req;
112 m_controller = controller;
113 m_resource = m_controller.getCmsFile().getAbsolutePath();
114 CmsObject cms = m_controller.getCmsObject();
115 m_includeCalls = Collections.synchronizedSet(new java.util.HashSet(23));
116 m_parameters = req.getParameterMap();
117 try {
118 m_isOnline = cms.getRequestContext().currentProject().isOnlineProject();
119 } catch (Exception e) {}
120 String[] paras = req.getParameterValues("_flex");
121 boolean nocachepara = false;
122 boolean dorecompile = false;
123 boolean isAdmin = false;
124 if (paras != null) {
125 try {
126 isAdmin = cms.getRequestContext().isAdmin();
127 } catch (Exception e) {}
128 if (isAdmin) {
129 List l = Arrays.asList(paras);
130 String context = (String)req.getAttribute(ATTRIBUTE_PROCESSED);
131 boolean firstCall = (context == null);
132 if (firstCall) req.setAttribute(ATTRIBUTE_PROCESSED, "true");
133 nocachepara = l.contains("nocache");
134 dorecompile = l.contains("recompile");
135 boolean p_on = l.contains("online");
136 boolean p_off = l.contains("offline");
137 if (l.contains("purge") && firstCall) {
138 A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_PURGE_JSP_REPOSITORY, new java.util.HashMap(0)));
139 A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_ENTRIES))));
140 dorecompile = false;
141 } else if ((l.contains("clearcache") || dorecompile) && firstCall) {
142 if (! (p_on || p_off)) {
143 A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_ALL))));
144 } else {
145 if (p_on) A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_ONLINE_ALL))));
146 if (p_off) A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_OFFLINE_ALL))));
147 }
148 } else if (l.contains("clearvariations") && firstCall) {
149 if (! (p_on || p_off)) {
150 A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_ENTRIES))));
151 } else {
152 if (p_on) A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_ONLINE_ENTRIES))));
153 if (p_off) A_OpenCms.fireCmsEvent(new CmsEvent(cms, I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR, Collections.singletonMap("action", new Integer(CmsFlexCache.C_CLEAR_OFFLINE_ENTRIES))));
154 }
155 }
156 }
157 }
158 m_canCache = (((m_isOnline || m_controller.getCmsCache().cacheOffline()) && ! nocachepara) || dorecompile);
159 m_doRecompile = dorecompile;
160 if (DEBUG) System.err.println("[FlexRequest] Constructing new Flex request for resource: " + m_resource);
161 }
162
163 /**
164 * Constructs a new wrapper layer around a (already wrapped) CmsFlexRequest.<p>
165 *
166 * @param req the request to be wrapped
167 * @param controller the controller to use
168 * @param resource the target resource that has been requested
169 */
170 CmsFlexRequest(HttpServletRequest req, CmsFlexController controller, String resource) {
171 super(req);
172 m_req = req;
173 m_controller = controller;
174 m_resource = toAbsolute(resource);
175 // must reset request URI/URL buffer here because m_resource has changed
176 m_requestUri = null;
177 m_requestUrl = null;
178 m_isOnline = m_controller.getCurrentRequest().isOnline();
179 m_canCache = m_controller.getCurrentRequest().isCacheable();
180 m_doRecompile = m_controller.getCurrentRequest().isDoRecompile();
181 m_includeCalls = m_controller.getCurrentRequest().getCmsIncludeCalls();
182 m_parameters = req.getParameterMap();
183 if (DEBUG) System.err.println("[FlexRequest] Re-using Flex request for resource: " + m_resource);
184 }
185
186 /**
187 * Returns the name of the resource currently processed.<p>
188 *
189 * This might be the name of an included resource,
190 * not neccesarily the name the resource requested by the user.
191 *
192 * @return the name of the resource currently processed
193 * @see #getCmsRequestedResource()
194 */
195 public String getElementUri() {
196 return m_resource;
197 }
198
199 /**
200 * Replacement for the standard servlet API getRequestDispatcher() method.<p>
201 *
202 * This variation is used if an external file (probably JSP) is dispached to.
203 * This external file must have a "mirror" version, i.e. a file in the OpenCms VFS
204 * that represents the external file.<p>
205 *
206 * @param cms_target the OpenCms file that is a "mirror" version of the external file
207 * @param ext_target the external file (outside the OpenCms VFS)
208 * @return the constructed CmsFlexRequestDispatcher
209 */
210 public CmsFlexRequestDispatcher getRequestDispatcherToExternal(String vfs_target, String ext_target) {
211 return new CmsFlexRequestDispatcher(m_controller.getTopRequest().getRequestDispatcher(ext_target), toAbsolute(vfs_target), ext_target);
212 }
213
214 /**
215 * Overloads the standard servlet API getRequestDispatcher() method,
216 * which is the main purpose of this wrapper implementation.<p>
217 *
218 * @param target the target for the request dispatcher
219 * @return the constructed RequestDispatcher
220 */
221 public javax.servlet.RequestDispatcher getRequestDispatcher(String target) {
222 return (javax.servlet.RequestDispatcher) new CmsFlexRequestDispatcher (m_controller.getTopRequest().getRequestDispatcher(toAbsolute(target)), toAbsolute(target), null);
223 }
224
225 /**
226 * Wraps the request URI, overloading the standard API.<p>
227 *
228 * This ensures that any wrapped request will use the "faked"
229 * target parameters. Remember that for the real request,
230 * a mixture of PathInfo and other request information is used to
231 * idenify the target.<p>
232 *
233 * @return a faked URI that will point to the wrapped target in the VFS
234 * @see javax.servlet.http.HttpServletRequest#getRequestURI()
235 */
236 public String getRequestURI() {
237 if (m_requestUri != null) return m_requestUri;
238 StringBuffer buf = new StringBuffer(128);
239 buf.append(getContextPath());
240 buf.append(getServletPath());
241 buf.append(getElementUri());
242 m_requestUri = buf.toString();
243 return m_requestUri;
244 }
245
246 /**
247 * Wraps the request URL, overloading the standard API,
248 * the wrapped URL will always point to the currently included VFS resource.<p>
249 *
250 * @return a faked URL that will point to the included target in the VFS
251 * @see javax.servlet.http.HttpServletRequest#getRequestURL()
252 */
253 public StringBuffer getRequestURL() {
254 if (m_requestUrl != null) return m_requestUrl;
255 StringBuffer buf = new StringBuffer(128);
256 buf.append(getScheme());
257 buf.append("://");
258 buf.append(getServerName());
259 buf.append(":");
260 buf.append(getServerPort());
261 buf.append(getRequestURI());
262 m_requestUrl = buf;
263 return m_requestUrl;
264 }
265
266 /**
267 * Convert (if necessary) and return the absolute URI that represents the
268 * resource referenced by this possibly relative URI for this request.<p>
269 *
270 * Adjust for resources in the OpenCms VFS by cutting of servlet context
271 * and servlet name.
272 * If this URI is already absolute, return it unchanged.
273 * Return URI also unchanged if it is not well-formed.<p>
274 *
275 * @param location URI to be (possibly) converted and then returned
276 * @return the location converted to an absolut location
277 */
278 public String toAbsolute(String location) {
279
280 if (DEBUG) System.err.println(getClass().getName() + " location=" + location);
281 if (location == null) return null;
282 if (location.startsWith("/")) return location;
283
284 // Construct a new absolute URL if possible (cribbed from Tomcat)
285 java.net.URL url = null;
286 try {
287 url = new java.net.URL(location);
288 } catch (java.net.MalformedURLException e1) {
289 String requrl = getRequestURL().toString();
290 try {
291 url = new java.net.URL(new java.net.URL(requrl), location);
292 } catch (java.net.MalformedURLException e2) {
293 // Some other method will deal with that sooner or later
294 return location;
295 }
296 }
297
298 // Now check if this is a opencms resource and if so remove the context / servlet path
299 String uri = url.getPath();
300 String context = getContextPath() + getServletPath();
301 if (uri.startsWith(context)) {
302 uri = uri.substring(context.length());
303 }
304 if (url.getQuery() != null) uri += "?" + url.getQuery();
305
306 if (DEBUG) System.err.println(getClass().getName() + " result=" + uri);
307 return uri;
308 }
309
310 /**
311 * Return the value of the specified request parameter, if any; otherwise,
312 * return <code>null</code>.<p>
313 *
314 * If there is more than one value defined,
315 * return only the first one.<p>
316 *
317 * @param name the name of the desired request parameter
318 * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
319 */
320 public String getParameter(String name) {
321
322 String values[] = (String[]) m_parameters.get(name);
323 if (values != null)
324 return (values[0]);
325 else
326 return (null);
327 }
328
329
330 /**
331 * Returns a <code>Map</code> of the parameters of this request.<p>
332 *
333 * Request parameters are extra information sent with the request.
334 * For HTTP servlets, parameters are contained in the query string
335 * or posted form data.<p>
336 *
337 * @return a <code>Map</code> containing parameter names as keys
338 * and parameter values as map values
339 * @see javax.servlet.ServletRequest#getParameterMap()
340 */
341 public Map getParameterMap() {
342 return (this.m_parameters);
343 }
344
345 /**
346 * Return the names of all defined request parameters for this request.<p>
347 *
348 * @return the names of all defined request parameters for this request
349 * @see javax.servlet.ServletRequest#getParameterNames()
350 */
351 public Enumeration getParameterNames() {
352 java.util.Vector v = new java.util.Vector();
353 v.addAll(m_parameters.keySet());
354 return (v.elements());
355 }
356
357 /**
358 * Returns the defined values for the specified request parameter, if any;
359 * otherwise, return <code>null</code>.
360 *
361 * @param name Name of the desired request parameter
362 * @return the defined values for the specified request parameter, if any;
363 * <code>null</code> otherwise
364 * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
365 */
366 public String[] getParameterValues(String name) {
367
368 String values[] = (String[]) m_parameters.get(name);
369 if (values != null)
370 return (values);
371 else
372 return (null);
373 }
374
375 /**
376 * Adds the specified Map to the paramters of the request.<p>
377 *
378 * Added parametes will not overwrite existing parameters in the
379 * request. Remember that the value for a parameter name in
380 * a HttpRequest is a String array. If a parameter name already
381 * exists in the HttpRequest, the values will be added to the existing
382 * value array. Multiple occurences of the same value for one
383 * paramter are also possible.<p>
384 *
385 * @param map the map to add
386 * @return the merged map of parameters
387 */
388 public Map addParameterMap(Map map) {
389 if (map == null)
390 return m_parameters;
391 if ((m_parameters == null) || (m_parameters.size() == 0)) {
392 m_parameters = Collections.unmodifiableMap(map);
393 } else {
394 HashMap parameters = new HashMap();
395 parameters.putAll(m_parameters);
396
397 Iterator it = map.keySet().iterator();
398 while (it.hasNext()) {
399 String key = (String) it.next();
400 // Check if the parameter name (key) exists
401 if (parameters.containsKey(key)) {
402
403 String[] oldValues = (String[]) parameters.get(key);
404 String[] newValues = (String[]) map.get(key);
405
406 String[] mergeValues = new String[oldValues.length + newValues.length];
407 System.arraycopy(newValues, 0, mergeValues, 0, newValues.length);
408 System.arraycopy(oldValues, 0, mergeValues, newValues.length, oldValues.length);
409
410 parameters.put(key, mergeValues);
411 } else {
412 // No: Add new value array
413 parameters.put(key, map.get(key));
414 }
415 }
416 m_parameters = Collections.unmodifiableMap(parameters);
417 }
418
419 return m_parameters;
420 }
421
422 /**
423 * Sets the specified Map as paramter map of the request.<p>
424 *
425 * The map set should be immutable.
426 * This will completly replace the parameter map.
427 * Use this in combination with getParameterMap() and
428 * addParameterMap() in case you want to set the old status
429 * of the parameter map after you have modified it for
430 * a specific operation.<p>
431 *
432 * @param map the map to set
433 */
434 public void setParameterMap(Map map) {
435 m_parameters = map;
436 }
437
438 /**
439 * Returns the CmsFlexCacheKey for this request,
440 * the key will be calculated if neccessary.<p>
441 *
442 * @return the CmsFlexCacheKey for this request
443 */
444 CmsFlexCacheKey getCmsCacheKey() {
445 // The key for this request is only calculated if actually requested
446 if (m_key == null) {
447 m_key = new CmsFlexCacheKey(this, m_resource, m_isOnline);
448 }
449 return m_key;
450 }
451
452 /**
453 * Indicates that this request belongs to an online project.<p>
454 *
455 * This is required to distinguish between online and offline
456 * resources in the cache. Since the resources have the same name,
457 * a suffix [online] or [offline] is added to distinguish the strings
458 * when building cache keys.
459 * Any resource from a request that isOnline() will be saved with
460 * the [online] suffix and vice versa.<p>
461 *
462 * The suffixes are used so that we have a simple String name
463 * for the resources in the cache. This makes it easy to
464 * use a standard HashMap for storage of the resources.<p>
465 *
466 * @return true if an online resource was requested, false otherwise
467 */
468 public boolean isOnline() {
469 return m_isOnline;
470 }
471
472 /**
473 * This is needed to decide if this request can be cached or not.<p>
474 *
475 * Using the request to decide if caching is used or not
476 * makes it possible to set caching to false e.g. on a per-user
477 * or per-project basis.<p>
478 *
479 * @return true if the request is cacheable, false otherwise
480 */
481 boolean isCacheable() {
482 return m_canCache;
483 }
484
485 /**
486 * Checks if JSPs should always be recompiled.<p>
487 *
488 * This is useful in case directive based includes are used
489 * with <%@ include file="..." %> on a JSP.
490 * Note that this also forces the request not to be cached.<p>
491 *
492 * @return true if JSPs should be recompiled, false otherwise
493 */
494 public boolean isDoRecompile() {
495 return m_doRecompile;
496 }
497
498 /**
499 * Adds another include call to this wrapper.<p>
500 *
501 * The set of include calls is maintained to dectect
502 * an endless inclusion loop.<p>
503 *
504 * @param target the target name (absolute OpenCms URI) to add
505 */
506 void addInlucdeCall(String target) {
507 m_includeCalls.add(target);
508 }
509
510 /**
511 * Removes an include call from this wrapper.<p>
512 *
513 * The set of include calls is maintained to dectect
514 * an endless inclusion loop.<p>
515 *
516 * @param target the target name (absolute OpenCms URI) to remove
517 */
518 void removeIncludeCall(String target) {
519 m_includeCalls.remove(target);
520 }
521
522 /**
523 * Checks if a given target is already included in a top-layer of this
524 * wrapped request.<p>
525 *
526 * The set of include calls is maintained to dectect
527 * an endless inclusion loop.<p>
528 *
529 * @param target the target name (absolute OpenCms URI) to check for
530 * @return true if the target is already included, false otherwise
531 */
532 boolean containsIncludeCall(String target) {
533 return m_includeCalls.contains(target);
534 }
535
536 /**
537 * Returns the Set of include calls which will be passed to the next wrapping layer.<p>
538 *
539 * The set of include calls is maintained to dectect
540 * an endless inclusion loop.<p>
541 *
542 * @return the Set of include calls
543 */
544 protected Set getCmsIncludeCalls() {
545 return m_includeCalls;
546 }
547 }