001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/flex/CmsFlexRequest.java,v $
003: * Date : $Date: 2008-02-27 12:05:47 $
004: * Version: $Revision: 1.45 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.flex;
033:
034: import org.opencms.file.CmsObject;
035: import org.opencms.file.history.CmsHistoryResourceHandler;
036: import org.opencms.loader.CmsJspLoader;
037: import org.opencms.main.CmsEvent;
038: import org.opencms.main.CmsLog;
039: import org.opencms.main.I_CmsEventListener;
040: import org.opencms.main.OpenCms;
041: import org.opencms.security.CmsRole;
042: import org.opencms.staticexport.CmsLinkManager;
043:
044: import java.util.Arrays;
045: import java.util.Collections;
046: import java.util.Enumeration;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.Vector;
052:
053: import javax.servlet.ServletRequest;
054: import javax.servlet.http.HttpServletRequest;
055: import javax.servlet.http.HttpServletRequestWrapper;
056:
057: import org.apache.commons.logging.Log;
058:
059: /**
060: * Wrapper class for a HttpServletRequest.<p>
061: *
062: * This class wraps the standard HttpServletRequest so that it's output can be delivered to
063: * the CmsFlexCache.<p>
064: *
065: * @author Alexander Kandzior
066: *
067: * @version $Revision: 1.45 $
068: *
069: * @since 6.0.0
070: */
071: public class CmsFlexRequest extends HttpServletRequestWrapper {
072:
073: /** Request parameter for FlexCache commands. */
074: public static final String PARAMETER_FLEX = "_flex";
075:
076: /** The log object for this class. */
077: private static final Log LOG = CmsLog.getLog(CmsFlexRequest.class);
078:
079: /** Flag to decide if this request can be cached or not. */
080: private boolean m_canCache;
081:
082: /** The CmsFlexController for this request. */
083: private CmsFlexController m_controller;
084:
085: /** Flag to force a JSP recompile. */
086: private boolean m_doRecompile;
087:
088: /** The requested resources element URI in the OpenCms VFS. */
089: private String m_elementUri;
090:
091: /** The site root of the requested resource. */
092: private String m_elementUriSiteRoot;
093:
094: /** List of all include calls (to prevent an endless inclusion loop). */
095: private List m_includeCalls;
096:
097: /** Flag to check if this request is in the online project or not. */
098: private boolean m_isOnline;
099:
100: /** The CmsFlexRequestKey for this request. */
101: private CmsFlexRequestKey m_key;
102:
103: /** Map of parameters from the original request. */
104: private Map m_parameters;
105:
106: /** Stores the request URI after it was once calculated. */
107: private String m_requestUri;
108:
109: /** Stores the request URL after it was once calculated. */
110: private StringBuffer m_requestUrl;
111:
112: /**
113: * Creates a new CmsFlexRequest wrapper which is most likely the "Top"
114: * request wrapper, i.e. the wrapper that is constructed around the
115: * first "real" (not wrapped) request.<p>
116: *
117: * @param req the request to wrap
118: * @param controller the controller to use
119: */
120: public CmsFlexRequest(HttpServletRequest req,
121: CmsFlexController controller) {
122:
123: super (req);
124: m_controller = controller;
125: CmsObject cms = m_controller.getCmsObject();
126: m_elementUri = cms.getSitePath(m_controller.getCmsResource());
127: m_elementUriSiteRoot = cms.getRequestContext().getSiteRoot();
128: m_includeCalls = new Vector();
129: m_parameters = req.getParameterMap();
130: m_isOnline = cms.getRequestContext().currentProject()
131: .isOnlineProject();
132: String[] params = req.getParameterValues(PARAMETER_FLEX);
133: boolean nocachepara = CmsHistoryResourceHandler
134: .isHistoryRequest(req);
135: boolean dorecompile = false;
136: if (params != null) {
137: if (OpenCms.getRoleManager().hasRole(cms,
138: CmsRole.WORKPLACE_MANAGER)) {
139: List paramList = Arrays.asList(params);
140: boolean firstCall = controller.isEmptyRequestList();
141: nocachepara |= paramList.contains("nocache");
142: dorecompile = paramList.contains("recompile");
143: boolean p_on = paramList.contains("online");
144: boolean p_off = paramList.contains("offline");
145: if (paramList.contains("purge") && firstCall) {
146: OpenCms
147: .fireCmsEvent(new CmsEvent(
148: I_CmsEventListener.EVENT_FLEX_PURGE_JSP_REPOSITORY,
149: new HashMap(0)));
150: OpenCms
151: .fireCmsEvent(new CmsEvent(
152: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
153: Collections
154: .singletonMap(
155: "action",
156: new Integer(
157: CmsFlexCache.CLEAR_ENTRIES))));
158: dorecompile = false;
159: } else if ((paramList.contains("clearcache") || dorecompile)
160: && firstCall) {
161: if (!(p_on || p_off)) {
162: OpenCms
163: .fireCmsEvent(new CmsEvent(
164: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
165: Collections
166: .singletonMap(
167: "action",
168: new Integer(
169: CmsFlexCache.CLEAR_ALL))));
170: } else {
171: if (p_on) {
172: OpenCms
173: .fireCmsEvent(new CmsEvent(
174: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
175: Collections
176: .singletonMap(
177: "action",
178: new Integer(
179: CmsFlexCache.CLEAR_ONLINE_ALL))));
180: }
181: if (p_off) {
182: OpenCms
183: .fireCmsEvent(new CmsEvent(
184: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
185: Collections
186: .singletonMap(
187: "action",
188: new Integer(
189: CmsFlexCache.CLEAR_OFFLINE_ALL))));
190: }
191: }
192: } else if (paramList.contains("clearvariations")
193: && firstCall) {
194: if (!(p_on || p_off)) {
195: OpenCms
196: .fireCmsEvent(new CmsEvent(
197: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
198: Collections
199: .singletonMap(
200: "action",
201: new Integer(
202: CmsFlexCache.CLEAR_ENTRIES))));
203: } else {
204: if (p_on) {
205: OpenCms
206: .fireCmsEvent(new CmsEvent(
207: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
208: Collections
209: .singletonMap(
210: "action",
211: new Integer(
212: CmsFlexCache.CLEAR_ONLINE_ENTRIES))));
213: }
214: if (p_off) {
215: OpenCms
216: .fireCmsEvent(new CmsEvent(
217: I_CmsEventListener.EVENT_FLEX_CACHE_CLEAR,
218: Collections
219: .singletonMap(
220: "action",
221: new Integer(
222: CmsFlexCache.CLEAR_OFFLINE_ENTRIES))));
223: }
224: }
225: }
226: }
227: }
228: m_canCache = (((m_isOnline || m_controller.getCmsCache()
229: .cacheOffline()) && !nocachepara) || dorecompile);
230: m_doRecompile = dorecompile;
231: if (LOG.isDebugEnabled()) {
232: LOG.debug(Messages.get().getBundle().key(
233: Messages.LOG_FLEXREQUEST_CREATED_NEW_REQUEST_1,
234: m_elementUri));
235: }
236: }
237:
238: /**
239: * Constructs a new wrapper layer around an (already wrapped) CmsFlexRequest.<p>
240: *
241: * @param req the request to be wrapped
242: * @param controller the controller to use
243: * @param resource the target resource that has been requested
244: */
245: CmsFlexRequest(HttpServletRequest req,
246: CmsFlexController controller, String resource) {
247:
248: super (req);
249: m_controller = controller;
250: m_elementUri = CmsLinkManager.getAbsoluteUri(resource,
251: m_controller.getCurrentRequest().getElementUri());
252: m_elementUriSiteRoot = m_controller.getCurrentRequest().m_elementUriSiteRoot;
253: m_isOnline = m_controller.getCurrentRequest().isOnline();
254: m_canCache = m_controller.getCurrentRequest().isCacheable();
255: m_doRecompile = m_controller.getCurrentRequest()
256: .isDoRecompile();
257: m_includeCalls = m_controller.getCurrentRequest()
258: .getCmsIncludeCalls();
259: m_parameters = req.getParameterMap();
260: if (LOG.isDebugEnabled()) {
261: LOG.debug(Messages.get().getBundle().key(
262: Messages.LOG_FLEXREQUEST_REUSING_FLEX_REQUEST_1,
263: m_elementUri));
264: }
265: }
266:
267: /**
268: * Adds the specified Map to the parameters of the request,
269: * added parameters will not overwrite existing parameters in the
270: * request.<p>
271: *
272: * Remember that the value for a parameter name in
273: * a HttpRequest is a String array. If a parameter name already
274: * exists in the HttpRequest, the values will be added to the existing
275: * value array. Multiple occurrences of the same value for one
276: * parameter are also possible.<p>
277: *
278: * @param map the map to add
279: * @return the merged map of parameters
280: */
281: public Map addParameterMap(Map map) {
282:
283: if (map == null) {
284: return m_parameters;
285: }
286: if ((m_parameters == null) || (m_parameters.size() == 0)) {
287: m_parameters = Collections.unmodifiableMap(map);
288: } else {
289: HashMap parameters = new HashMap();
290: parameters.putAll(m_parameters);
291:
292: Iterator it = map.entrySet().iterator();
293: while (it.hasNext()) {
294: Map.Entry entry = (Map.Entry) it.next();
295: String key = (String) entry.getKey();
296: // Check if the parameter name (key) exists
297: if (parameters.containsKey(key)) {
298:
299: String[] oldValues = (String[]) parameters.get(key);
300: String[] newValues = (String[]) entry.getValue();
301:
302: String[] mergeValues = new String[oldValues.length
303: + newValues.length];
304: System.arraycopy(newValues, 0, mergeValues, 0,
305: newValues.length);
306: System.arraycopy(oldValues, 0, mergeValues,
307: newValues.length, oldValues.length);
308:
309: parameters.put(key, mergeValues);
310: } else {
311: // No: Add new value array
312: parameters.put(key, entry.getValue());
313: }
314: }
315: m_parameters = Collections.unmodifiableMap(parameters);
316: }
317:
318: return m_parameters;
319: }
320:
321: /**
322: * Returns the full element URI site root path to the resource currently processed.<p>
323: *
324: * @return the name of the resource currently processed
325: */
326: public String getElementRootPath() {
327:
328: return m_controller.getCmsObject().getRequestContext()
329: .addSiteRoot(m_elementUriSiteRoot, m_elementUri);
330: }
331:
332: /**
333: * Returns the element URI of the resource currently processed,
334: * relative to the current site root.<p>
335: *
336: * This might be the name of an included resource,
337: * not necessarily the name the resource requested by the user.<p>
338: *
339: * @return the name of the resource currently processed
340: */
341: public String getElementUri() {
342:
343: return m_elementUri;
344: }
345:
346: /**
347: * Return the value of the specified request parameter, if any; otherwise,
348: * return <code>null</code>.<p>
349: *
350: * If there is more than one value defined,
351: * return only the first one.<p>
352: *
353: * @param name the name of the desired request parameter
354: *
355: * @return the value of the specified request parameter
356: *
357: * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
358: */
359: public String getParameter(String name) {
360:
361: String[] values = (String[]) m_parameters.get(name);
362: if (values != null) {
363: return (values[0]);
364: } else {
365: return (null);
366: }
367: }
368:
369: /**
370: * Returns a <code>Map</code> of the parameters of this request.<p>
371: *
372: * Request parameters are extra information sent with the request.
373: * For HTTP servlets, parameters are contained in the query string
374: * or posted form data.<p>
375: *
376: * @return a <code>Map</code> containing parameter names as keys
377: * and parameter values as map values
378: *
379: * @see javax.servlet.ServletRequest#getParameterMap()
380: */
381: public Map getParameterMap() {
382:
383: return m_parameters;
384: }
385:
386: /**
387: * Return the names of all defined request parameters for this request.<p>
388: *
389: * @return the names of all defined request parameters for this request
390: *
391: * @see javax.servlet.ServletRequest#getParameterNames()
392: */
393: public Enumeration getParameterNames() {
394:
395: Vector v = new Vector();
396: v.addAll(m_parameters.keySet());
397: return (v.elements());
398: }
399:
400: /**
401: * Returns the defined values for the specified request parameter, if any;
402: * otherwise, return <code>null</code>.<p>
403: *
404: * @param name Name of the desired request parameter
405: *
406: * @return the defined values for the specified request parameter, if any;
407: * <code>null</code> otherwise
408: *
409: * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
410: */
411: public String[] getParameterValues(String name) {
412:
413: return (String[]) m_parameters.get(name);
414: }
415:
416: /**
417: * Allows requests to be dispatched to internal VFS resources or
418: * external JSP pages, overloads the standard servlet API <code>getRequestDispatcher()</code> method.<p>
419: *
420: * @param target the target for the request dispatcher
421: *
422: * @return a special RequestDispatcher that allows access to VFS resources
423: */
424: public javax.servlet.RequestDispatcher getRequestDispatcher(
425: String target) {
426:
427: String absolutUri = CmsLinkManager.getAbsoluteUri(target,
428: m_controller.getCurrentRequest().getElementUri());
429: return new CmsFlexRequestDispatcher(m_controller
430: .getTopRequest().getRequestDispatcher(absolutUri),
431: absolutUri, null);
432: }
433:
434: /**
435: * Replacement for the standard servlet API getRequestDispatcher() method.<p>
436: *
437: * This variation is used if an external file (probably JSP) is dispatched to.
438: * This external file must have a "mirror" version, i.e. a file in the OpenCms VFS
439: * that represents the external file.<p>
440: *
441: * @param vfs_target the OpenCms file that is a "mirror" version of the external file
442: * @param ext_target the external file (outside the OpenCms VFS)
443: *
444: * @return the constructed CmsFlexRequestDispatcher
445: */
446: public CmsFlexRequestDispatcher getRequestDispatcherToExternal(
447: String vfs_target, String ext_target) {
448:
449: return new CmsFlexRequestDispatcher(m_controller
450: .getTopRequest().getRequestDispatcher(ext_target),
451: CmsLinkManager.getAbsoluteUri(vfs_target, m_controller
452: .getCmsObject().getRequestContext().getUri()),
453: ext_target);
454: }
455:
456: /**
457: * Wraps the request URI, overloading the standard API.<p>
458: *
459: * This ensures that any wrapped request will use the "faked"
460: * target parameters. Remember that for the real request,
461: * a mixture of PathInfo and other request information is used to
462: * identify the target.<p>
463: *
464: * @return a faked URI that will point to the wrapped target in the VFS
465: *
466: * @see javax.servlet.http.HttpServletRequest#getRequestURI()
467: */
468: public String getRequestURI() {
469:
470: if (m_requestUri != null) {
471: return m_requestUri;
472: }
473: StringBuffer buf = new StringBuffer(128);
474: buf.append(OpenCms.getSystemInfo().getOpenCmsContext());
475: buf.append(getElementUri());
476: m_requestUri = buf.toString();
477: return m_requestUri;
478: }
479:
480: /**
481: * Wraps the request URL, overloading the standard API,
482: * the wrapped URL will always point to the currently included VFS resource.<p>
483: *
484: * @return a faked URL that will point to the included target in the VFS
485: *
486: * @see javax.servlet.http.HttpServletRequest#getRequestURL()
487: */
488: public StringBuffer getRequestURL() {
489:
490: if (m_requestUrl != null) {
491: return m_requestUrl;
492: }
493: StringBuffer buf = new StringBuffer(128);
494: buf.append(getScheme());
495: buf.append("://");
496: buf.append(getServerName());
497: buf.append(":");
498: buf.append(getServerPort());
499: buf.append(getRequestURI());
500: m_requestUrl = buf;
501: return m_requestUrl;
502: }
503:
504: /**
505: * This is a work around for servlet containers creating a new application dispatcher
506: * instead of using our request dispatcher, so missing RFS jsp pages are not requested to
507: * OpenCms and the dispatcher is unable to load the included/forwarded jsp file.<p>
508: *
509: * @see javax.servlet.http.HttpServletRequestWrapper#getServletPath()
510: */
511: public String getServletPath() {
512:
513: // unwrap the request to prevent multiple unneeded attempts to generate missing jsp files
514: // m_controller.getTopRequest() does not return the right request here when forwarding
515: // this method is generally called exactly once per request on different servlet containers
516: // only resin calls it twice
517: ServletRequest req = getRequest();
518: while (req instanceof CmsFlexRequest) {
519: req = ((CmsFlexRequest) req).getRequest();
520: }
521: String servletPath = null;
522: if (req instanceof HttpServletRequest) {
523: servletPath = ((HttpServletRequest) req).getServletPath();
524: } else {
525: servletPath = super .getServletPath();
526: }
527: // generate missing jsp file
528: CmsJspLoader.updateJspFromRequest(servletPath, this );
529: return servletPath;
530: }
531:
532: /**
533: * Checks if JSPs should always be recompiled.<p>
534: *
535: * This is useful in case directive based includes are used
536: * with <%@ include file="..." %> on a JSP.
537: * Note that this also forces the request not to be cached.<p>
538: *
539: * @return true if JSPs should be recompiled, false otherwise
540: */
541: public boolean isDoRecompile() {
542:
543: return m_doRecompile;
544: }
545:
546: /**
547: * Indicates that this request belongs to an online project.<p>
548: *
549: * This is required to distinguish between online and offline
550: * resources in the cache. Since the resources have the same name,
551: * a suffix [online] or [offline] is added to distinguish the strings
552: * when building cache keys.
553: * Any resource from a request that isOnline() will be saved with
554: * the [online] suffix and vice versa.<p>
555: *
556: * Resources in the OpenCms workplace are not distinguished between
557: * online and offline but have their own suffix [workplace].
558: * The assumption is that if you do change the workplace, this is
559: * only on true development machines so you can do the cache clearing
560: * manually if required.<p>
561: *
562: * The suffixes are used so that we have a simple String name
563: * for the resources in the cache. This makes it easy to
564: * use a standard HashMap for storage of the resources.<p>
565: *
566: * @return true if an online resource was requested, false otherwise
567: */
568: public boolean isOnline() {
569:
570: return m_isOnline;
571: }
572:
573: /**
574: * Sets the specified Map as parameter map of the request.<p>
575: *
576: * The map set should be immutable.
577: * This will completely replace the parameter map.
578: * Use this in combination with getParameterMap() and
579: * addParameterMap() in case you want to set the old status
580: * of the parameter map after you have modified it for
581: * a specific operation.<p>
582: *
583: * @param map the map to set
584: */
585: public void setParameterMap(Map map) {
586:
587: m_parameters = map;
588: }
589:
590: /**
591: * @see java.lang.Object#toString()
592: */
593: public String toString() {
594:
595: // return the uri of the element requested for this request, useful in debugging
596: return m_elementUri;
597: }
598:
599: /**
600: * Returns the List of include calls which will be passed to the next wrapping layer.<p>
601: *
602: * The set of include calls is maintained to detect
603: * an endless inclusion loop.<p>
604: *
605: * @return the List of include calls
606: */
607: protected List getCmsIncludeCalls() {
608:
609: return m_includeCalls;
610: }
611:
612: /**
613: * Adds another include call to this wrapper.<p>
614: *
615: * The set of include calls is maintained to detect
616: * an endless inclusion loop.<p>
617: *
618: * @param target the target name (absolute OpenCms URI) to add
619: */
620: void addInlucdeCall(String target) {
621:
622: m_includeCalls.add(target);
623: }
624:
625: /**
626: * Checks if a given target is already included in a top-layer of this
627: * wrapped request.<p>
628: *
629: * The set of include calls is maintained to detect
630: * an endless inclusion loop.<p>
631: *
632: * @param target the target name (absolute OpenCms URI) to check for
633: * @return true if the target is already included, false otherwise
634: */
635: boolean containsIncludeCall(String target) {
636:
637: return m_includeCalls.contains(target);
638: }
639:
640: /**
641: * Returns the CmsFlexCacheKey for this request,
642: * the key will be calculated if necessary.<p>
643: *
644: * @return the CmsFlexCacheKey for this request
645: */
646: CmsFlexRequestKey getCmsCacheKey() {
647:
648: // The key for this request is only calculated if actually requested
649: if (m_key == null) {
650: m_key = new CmsFlexRequestKey(this , m_elementUri,
651: m_isOnline);
652: }
653: return m_key;
654: }
655:
656: /**
657: * This is needed to decide if this request can be cached or not.<p>
658: *
659: * Using the request to decide if caching is used or not
660: * makes it possible to set caching to false e.g. on a per-user
661: * or per-project basis.<p>
662: *
663: * @return <code>true</code> if the request is cacheable, false otherwise
664: */
665: boolean isCacheable() {
666:
667: return m_canCache;
668: }
669:
670: /**
671: * Removes an include call from this wrapper.<p>
672: *
673: * The set of include calls is maintained to detect
674: * an endless inclusion loop.<p>
675: *
676: * @param target the target name (absolute OpenCms URI) to remove
677: */
678: void removeIncludeCall(String target) {
679:
680: m_includeCalls.remove(target);
681: }
682: }
|