001: /*
002: * Copyright 2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.myfaces.application.jsp;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.apache.myfaces.application.DefaultViewHandlerSupport;
021: import org.apache.myfaces.application.InvalidViewIdException;
022: import org.apache.myfaces.application.ViewHandlerSupport;
023:
024: import javax.faces.FacesException;
025: import javax.faces.FactoryFinder;
026: import javax.faces.application.Application;
027: import javax.faces.application.StateManager;
028: import javax.faces.application.ViewHandler;
029: import javax.faces.component.UIViewRoot;
030: import javax.faces.context.ExternalContext;
031: import javax.faces.context.FacesContext;
032: import javax.faces.context.ResponseWriter;
033: import javax.faces.render.RenderKit;
034: import javax.faces.render.RenderKitFactory;
035: import javax.faces.render.ResponseStateManager;
036: import javax.servlet.ServletRequest;
037: import javax.servlet.ServletResponse;
038: import javax.servlet.http.HttpServletRequest;
039: import javax.servlet.http.HttpServletResponse;
040: import javax.servlet.http.HttpSession;
041: import javax.servlet.jsp.jstl.core.Config;
042: import java.io.IOException;
043: import java.io.StringWriter;
044: import java.io.Writer;
045: import java.util.Iterator;
046: import java.util.Locale;
047:
048: /**
049: * @author Thomas Spiegl (latest modification by $Author: baranda $)
050: * @author Bruno Aranda
051: * @version $Revision: 542008 $ $Date: 2007-05-27 19:45:19 +0200 (So, 27 Mai 2007) $
052: */
053: public class JspViewHandlerImpl extends ViewHandler {
054: private static final Log log = LogFactory
055: .getLog(JspViewHandlerImpl.class);
056: public static final String FORM_STATE_MARKER = "<!--@@JSF_FORM_STATE_MARKER@@-->";
057: public static final int FORM_STATE_MARKER_LEN = FORM_STATE_MARKER
058: .length();
059:
060: private static final String AFTER_VIEW_TAG_CONTENT_PARAM = JspViewHandlerImpl.class
061: + ".AFTER_VIEW_TAG_CONTENT";
062:
063: private ViewHandlerSupport _viewHandlerSupport;
064:
065: public JspViewHandlerImpl() {
066: if (log.isTraceEnabled())
067: log.trace("New ViewHandler instance created");
068: }
069:
070: /**
071: * @param viewHandlerSupport
072: * the viewHandlerSupport to set
073: */
074: public void setViewHandlerSupport(
075: ViewHandlerSupport viewHandlerSupport) {
076: _viewHandlerSupport = viewHandlerSupport;
077: }
078:
079: /**
080: * @return the viewHandlerSupport
081: */
082: protected ViewHandlerSupport getViewHandlerSupport() {
083: if (_viewHandlerSupport == null) {
084: _viewHandlerSupport = new DefaultViewHandlerSupport();
085: }
086: return _viewHandlerSupport;
087: }
088:
089: public Locale calculateLocale(FacesContext facesContext) {
090: Application application = facesContext.getApplication();
091: for (Iterator<Locale> requestLocales = facesContext
092: .getExternalContext().getRequestLocales(); requestLocales
093: .hasNext();) {
094: Locale requestLocale = requestLocales.next();
095: for (Iterator<Locale> supportedLocales = application
096: .getSupportedLocales(); supportedLocales.hasNext();) {
097: Locale supportedLocale = supportedLocales.next();
098: // higher priority to a language match over an exact match
099: // that occures further down (see Jstl Reference 1.0 8.3.1)
100: if (requestLocale.getLanguage().equals(
101: supportedLocale.getLanguage())
102: && (supportedLocale.getCountry() == null || supportedLocale
103: .getCountry().length() == 0)) {
104: return supportedLocale;
105: } else if (supportedLocale.equals(requestLocale)) {
106: return supportedLocale;
107: }
108: }
109: }
110:
111: Locale defaultLocale = application.getDefaultLocale();
112: return defaultLocale != null ? defaultLocale : Locale
113: .getDefault();
114: }
115:
116: public String calculateRenderKitId(FacesContext facesContext) {
117: Object renderKitId = facesContext.getExternalContext()
118: .getRequestMap().get(
119: ResponseStateManager.RENDER_KIT_ID_PARAM);
120: if (renderKitId == null) {
121: renderKitId = facesContext.getApplication()
122: .getDefaultRenderKitId();
123: }
124: if (renderKitId == null) {
125: renderKitId = RenderKitFactory.HTML_BASIC_RENDER_KIT;
126: }
127: return renderKitId.toString();
128: }
129:
130: /**
131: */
132: public UIViewRoot createView(FacesContext facesContext,
133: String viewId) {
134: String calculatedViewId = viewId;
135: try {
136: calculatedViewId = getViewHandlerSupport().calculateViewId(
137: facesContext, viewId);
138: } catch (InvalidViewIdException e) {
139: sendSourceNotFound(facesContext, e.getMessage());
140: }
141:
142: Application application = facesContext.getApplication();
143: ViewHandler applicationViewHandler = application
144: .getViewHandler();
145:
146: Locale currentLocale = null;
147: String currentRenderKitId = null;
148: UIViewRoot uiViewRoot = facesContext.getViewRoot();
149: if (uiViewRoot != null) {
150: // Remember current locale and renderKitId
151: currentLocale = uiViewRoot.getLocale();
152: currentRenderKitId = uiViewRoot.getRenderKitId();
153: }
154:
155: uiViewRoot = (UIViewRoot) application
156: .createComponent(UIViewRoot.COMPONENT_TYPE);
157:
158: uiViewRoot.setViewId(calculatedViewId);
159:
160: if (currentLocale != null) {
161: // set old locale
162: uiViewRoot.setLocale(currentLocale);
163: } else {
164: // calculate locale
165: uiViewRoot.setLocale(applicationViewHandler
166: .calculateLocale(facesContext));
167: }
168:
169: if (currentRenderKitId != null) {
170: // set old renderKit
171: uiViewRoot.setRenderKitId(currentRenderKitId);
172: } else {
173: // calculate renderKit
174: uiViewRoot.setRenderKitId(applicationViewHandler
175: .calculateRenderKitId(facesContext));
176: }
177:
178: if (log.isTraceEnabled())
179: log.trace("Created view " + viewId);
180: return uiViewRoot;
181: }
182:
183: private void sendSourceNotFound(FacesContext context, String message) {
184: HttpServletResponse response = (HttpServletResponse) context
185: .getExternalContext().getResponse();
186: try {
187: context.responseComplete();
188: response.sendError(HttpServletResponse.SC_NOT_FOUND,
189: message);
190: } catch (IOException ioe) {
191: throw new FacesException(ioe);
192: }
193: }
194:
195: public String getActionURL(FacesContext facesContext, String viewId) {
196: return getViewHandlerSupport().calculateActionURL(facesContext,
197: viewId);
198: }
199:
200: public String getResourceURL(FacesContext facesContext, String path) {
201: if (path.length() > 0 && path.charAt(0) == '/') {
202: return facesContext.getExternalContext()
203: .getRequestContextPath()
204: + path;
205: }
206:
207: return path;
208:
209: }
210:
211: public void renderView(FacesContext facesContext,
212: UIViewRoot viewToRender) throws IOException, FacesException {
213: if (viewToRender == null) {
214: log.fatal("viewToRender must not be null");
215: throw new NullPointerException(
216: "viewToRender must not be null");
217: }
218:
219: // do not render the view if the rendered attribute for the view is false
220: if (!viewToRender.isRendered()) {
221: if (log.isTraceEnabled())
222: log.trace("View is not rendered");
223: return;
224: }
225:
226: ExternalContext externalContext = facesContext
227: .getExternalContext();
228:
229: String viewId = facesContext.getViewRoot().getViewId();
230:
231: if (log.isTraceEnabled())
232: log.trace("Rendering JSP view: " + viewId);
233:
234: ServletResponse response = (ServletResponse) externalContext
235: .getResponse();
236: ServletRequest request = (ServletRequest) externalContext
237: .getRequest();
238:
239: Locale locale = viewToRender.getLocale();
240: response.setLocale(locale);
241: Config.set(request, Config.FMT_LOCALE, facesContext
242: .getViewRoot().getLocale());
243:
244: ViewResponseWrapper wrappedResponse = new ViewResponseWrapper(
245: (HttpServletResponse) response);
246:
247: externalContext.setResponse(wrappedResponse);
248: externalContext.dispatch(viewId);
249: externalContext.setResponse(response);
250:
251: boolean errorResponse = wrappedResponse.getStatus() < 200
252: || wrappedResponse.getStatus() > 299;
253: if (errorResponse) {
254: wrappedResponse.flushToWrappedResponse();
255: }
256:
257: // store the wrapped response in the request, so it is thread-safe
258: externalContext.getRequestMap().put(
259: AFTER_VIEW_TAG_CONTENT_PARAM, wrappedResponse);
260:
261: // handle character encoding as of section 2.5.2.2 of JSF 1.1
262: if (externalContext.getRequest() instanceof HttpServletRequest) {
263: HttpServletRequest httpServletRequest = (HttpServletRequest) externalContext
264: .getRequest();
265: HttpSession session = httpServletRequest.getSession(false);
266:
267: if (session != null) {
268: session.setAttribute(
269: ViewHandler.CHARACTER_ENCODING_KEY, response
270: .getCharacterEncoding());
271: }
272: }
273:
274: // render the view in this method (since JSF 1.2)
275: RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder
276: .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
277: RenderKit renderKit = renderFactory.getRenderKit(facesContext,
278: viewToRender.getRenderKitId());
279:
280: ResponseWriter newResponseWriter;
281: StateMarkerAwareWriter stateAwareWriter = new StateMarkerAwareWriter();
282:
283: // If the FacesContext has a non-null ResponseWriter create a new writer using its
284: // cloneWithWriter() method, passing the response's Writer as the argument.
285: // Otherwise, use the current RenderKit to create a new ResponseWriter.
286: ResponseWriter oldResponseWriter = facesContext
287: .getResponseWriter();
288: if (oldResponseWriter != null) {
289: newResponseWriter = oldResponseWriter
290: .cloneWithWriter(stateAwareWriter);
291: } else {
292: if (log.isTraceEnabled())
293: log.trace("Creating new ResponseWriter");
294: newResponseWriter = renderKit.createResponseWriter(
295: stateAwareWriter, null,
296: ((HttpServletRequest) externalContext.getRequest())
297: .getCharacterEncoding());
298: }
299:
300: // Set the new ResponseWriter into the FacesContext, saving the old one aside.
301: facesContext.setResponseWriter(newResponseWriter);
302:
303: // Call startDocument() on the ResponseWriter.
304: newResponseWriter.startDocument();
305:
306: // Call encodeAll() on the UIViewRoot
307: viewToRender.encodeAll(facesContext);
308:
309: ResponseWriter responseWriter;
310: if (oldResponseWriter != null) {
311: responseWriter = oldResponseWriter.cloneWithWriter(response
312: .getWriter());
313: } else {
314: responseWriter = newResponseWriter.cloneWithWriter(response
315: .getWriter());
316: }
317: facesContext.setResponseWriter(responseWriter);
318:
319: // response.getWriter().write(stateAwareWriter.parseResponse());
320: stateAwareWriter.flushToWriter(response.getWriter());
321:
322: // Output any content in the wrappedResponse response from above to the response, removing the
323: // wrappedResponse response from the thread-safe storage.
324: ViewResponseWrapper afterViewTagResponse = (ViewResponseWrapper) externalContext
325: .getRequestMap().get(AFTER_VIEW_TAG_CONTENT_PARAM);
326: externalContext.getRequestMap().remove(
327: AFTER_VIEW_TAG_CONTENT_PARAM);
328:
329: if (afterViewTagResponse != null) {
330: afterViewTagResponse.flushToWriter(response.getWriter());
331: }
332:
333: // Call endDocument() on the ResponseWriter
334: newResponseWriter.endDocument();
335:
336: // If the old ResponseWriter was not null, place the old ResponseWriter back
337: // into the FacesContext.
338: if (oldResponseWriter != null) {
339: facesContext.setResponseWriter(oldResponseWriter);
340: }
341:
342: response.flushBuffer();
343: }
344:
345: public UIViewRoot restoreView(FacesContext facesContext,
346: String viewId) {
347: Application application = facesContext.getApplication();
348: ViewHandler applicationViewHandler = application
349: .getViewHandler();
350: String renderKitId = applicationViewHandler
351: .calculateRenderKitId(facesContext);
352: String calculatedViewId = getViewHandlerSupport()
353: .calculateViewId(facesContext, viewId);
354: UIViewRoot viewRoot = application.getStateManager()
355: .restoreView(facesContext, calculatedViewId,
356: renderKitId);
357: return viewRoot;
358: }
359:
360: /**
361: * Writes a state marker that is replaced later by one or more hidden form inputs.
362: *
363: * @param facesContext
364: * @throws IOException
365: */
366: public void writeState(FacesContext facesContext)
367: throws IOException {
368: facesContext.getResponseWriter().write(FORM_STATE_MARKER);
369: }
370:
371: /**
372: * Writes the response and replaces the state marker tags with the state information for the current context
373: */
374: private static class StateMarkerAwareWriter extends StringWriter {
375: public StateMarkerAwareWriter() {
376: }
377:
378: public void flushToWriter(Writer writer) throws IOException {
379: FacesContext facesContext = FacesContext
380: .getCurrentInstance();
381: StateManager stateManager = facesContext.getApplication()
382: .getStateManager();
383:
384: StringWriter stateWriter = new StringWriter();
385: ResponseWriter realWriter = facesContext
386: .getResponseWriter();
387: facesContext.setResponseWriter(realWriter
388: .cloneWithWriter(stateWriter));
389:
390: Object serializedView = stateManager.saveView(facesContext);
391:
392: stateManager.writeState(facesContext, serializedView);
393: facesContext.setResponseWriter(realWriter);
394:
395: StringBuffer contentBuffer = getBuffer();
396: StringBuffer state = stateWriter.getBuffer();
397:
398: int form_marker;
399: while ((form_marker = contentBuffer
400: .indexOf(JspViewHandlerImpl.FORM_STATE_MARKER)) > -1) {
401: //FORM_STATE_MARKER found, replace it
402: contentBuffer.replace(form_marker, form_marker
403: + FORM_STATE_MARKER_LEN, state.toString());
404: }
405:
406: int bufferLength = contentBuffer.length();
407: int index = 0;
408: int bufferSize = 512;
409:
410: while (index < bufferLength) {
411: int maxSize = Math
412: .min(bufferSize, bufferLength - index);
413: char[] bufToWrite = new char[maxSize];
414:
415: contentBuffer.getChars(index, index + maxSize,
416: bufToWrite, 0);
417: writer.write(bufToWrite);
418:
419: index += bufferSize;
420: }
421:
422: }
423: }
424:
425: }
|