001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1
003: * The contents of this file are subject to the Mozilla Public License Version
004: * 1.1 (the "License"); you may not use this file except in compliance with
005: * the License. You may obtain a copy of the License at
006: * http://www.mozilla.org/MPL/
007: *
008: * Software distributed under the License is distributed on an "AS IS" basis,
009: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
010: * for the specific language governing rights and limitations under the
011: * License.
012: *
013: * The Original Code is Riot.
014: *
015: * The Initial Developer of the Original Code is
016: * Neteye GmbH.
017: * Portions created by the Initial Developer are Copyright (C) 2007
018: * the Initial Developer. All Rights Reserved.
019: *
020: * Contributor(s):
021: * Felix Gnass [fgnass at neteye dot de]
022: *
023: * ***** END LICENSE BLOCK ***** */
024: package org.riotfamily.website.template;
025:
026: import java.io.IOException;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Map;
031:
032: import javax.servlet.ServletException;
033: import javax.servlet.http.HttpServletRequest;
034: import javax.servlet.http.HttpServletResponse;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038: import org.riotfamily.common.web.util.DeferredRenderingResponseWrapper;
039: import org.riotfamily.common.web.util.ServletUtils;
040: import org.springframework.util.Assert;
041: import org.springframework.web.servlet.ModelAndView;
042:
043: /**
044: * TemplateController that allows slots to be processed before others while
045: * still being rendered at the original position within the template.
046: * <p>
047: * These 'pushed-up' controllers can expose request attributes which can then
048: * be read by other controllers that would usually be processed before.
049: * A common usecase is when a controller that renders the page content also
050: * wants to control the document's title tag.
051: * </p>
052: * @author Felix Gnass [fgnass at neteye dot de]
053: * @since 6.5
054: */
055: public class PushUpTemplateController extends TemplateController {
056:
057: private static final Log log = LogFactory
058: .getLog(PushUpTemplateController.class);
059:
060: private static final String RESPONSE_WRAPPERS_ATTRIBUTE = PushUpTemplateController.class
061: .getName()
062: + ".responseWrappers";
063:
064: protected static final String SLOT_TO_RENDER_PARAMETER = "slotToRender";
065:
066: private List pushUpSlots;
067:
068: public void setPushUpSlots(List pushUpSlots) {
069: this .pushUpSlots = pushUpSlots;
070: }
071:
072: protected ModelAndView handleRequestInternal(
073: HttpServletRequest request, HttpServletResponse response)
074: throws Exception {
075:
076: String slotToRender = request
077: .getParameter(SLOT_TO_RENDER_PARAMETER);
078: if (slotToRender != null) {
079: renderCapturedSlot(request, response, slotToRender);
080: return null;
081: }
082: if (handlePushUps(request, response)) {
083: return null;
084: }
085: return super .handleRequestInternal(request, response);
086: }
087:
088: /**
089: * Handles the push-up slots. Returns <code>true</code> if one of the
090: * controllers sent an error or redirect.
091: */
092: protected boolean handlePushUps(HttpServletRequest request,
093: HttpServletResponse response) throws ServletException,
094: IOException {
095:
096: if (pushUpSlots != null && !pushUpSlots.isEmpty()) {
097: //REVISIT Perhaps we should a check whether this is a top-level request ...
098: HashMap config = new HashMap();
099: request.setAttribute(SLOTS_CONFIGURATION_ATTRIBUTE, config);
100: Iterator it = pushUpSlots.iterator();
101: while (it.hasNext()) {
102: String slot = (String) it.next();
103: config
104: .put(slot, getDeferredRenderingUrl(request,
105: slot));
106: if (handlePushUp(request, response, slot)) {
107: return true;
108: }
109: }
110: }
111: return false;
112: }
113:
114: /**
115: * Returns an URL that can be requested in order to render a captured
116: * slot. The URL is constructed from the current servletPath by appending
117: * a parameter that contains the given slot path.
118: */
119: protected String getDeferredRenderingUrl(
120: HttpServletRequest request, String slot) {
121:
122: StringBuffer url = new StringBuffer().append(
123: ServletUtils.getPathWithinApplication(request)).append(
124: '?').append(SLOT_TO_RENDER_PARAMETER).append('=')
125: .append(slot);
126:
127: return url.toString();
128: }
129:
130: protected final Map getResponseWrappers(HttpServletRequest request) {
131: Map wrappers = (Map) request
132: .getAttribute(RESPONSE_WRAPPERS_ATTRIBUTE);
133: if (wrappers == null) {
134: wrappers = new HashMap();
135: request.setAttribute(RESPONSE_WRAPPERS_ATTRIBUTE, wrappers);
136: }
137: return wrappers;
138: }
139:
140: protected boolean handlePushUp(HttpServletRequest request,
141: HttpServletResponse response, String slot)
142: throws ServletException, IOException {
143:
144: DeferredRenderingResponseWrapper responseWrapper = new DeferredRenderingResponseWrapper(
145: response);
146:
147: getResponseWrappers(request).put(slot, responseWrapper);
148: request.setAttribute(SLOT_PATH_ATTRIBUTE, slot);
149:
150: String url = (String) getMergedConfiguration().get(slot);
151:
152: log
153: .debug("Capturing pushed-up slot " + slot + " [" + url
154: + "]");
155: request.getRequestDispatcher(url).forward(request,
156: responseWrapper);
157:
158: return responseWrapper.isRedirectSent();
159: }
160:
161: /**
162: * Renders a previously captured response for the given slot.
163: */
164: protected void renderCapturedSlot(HttpServletRequest request,
165: HttpServletResponse response, String slot)
166: throws IOException {
167:
168: log.debug("Rendering captured slot: " + slot);
169: DeferredRenderingResponseWrapper responseWrapper = (DeferredRenderingResponseWrapper) getResponseWrappers(
170: request).get(slot);
171:
172: Assert.notNull(responseWrapper,
173: "No wrapped response found for slot: " + slot);
174:
175: responseWrapper.renderResponse(response);
176: }
177: }
|