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.portlet;
017:
018: import java.io.IOException;
019:
020: import javax.faces.FactoryFinder;
021: import javax.faces.application.Application;
022: import javax.faces.application.ApplicationFactory;
023: import javax.faces.application.ViewHandler;
024: import javax.faces.component.UIViewRoot;
025: import javax.faces.context.ExternalContext;
026: import javax.faces.context.FacesContext;
027: import javax.faces.context.FacesContextFactory;
028: import javax.faces.lifecycle.Lifecycle;
029: import javax.faces.lifecycle.LifecycleFactory;
030: import javax.faces.webapp.FacesServlet;
031: import javax.portlet.ActionRequest;
032: import javax.portlet.ActionResponse;
033: import javax.portlet.GenericPortlet;
034: import javax.portlet.PortletContext;
035: import javax.portlet.PortletException;
036: import javax.portlet.PortletRequest;
037: import javax.portlet.PortletResponse;
038: import javax.portlet.RenderRequest;
039: import javax.portlet.RenderResponse;
040: import javax.portlet.UnavailableException;
041:
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044: import org.apache.myfaces.context.ReleaseableExternalContext;
045: import org.apache.myfaces.context.portlet.PortletExternalContextImpl;
046: import org.apache.myfaces.context.servlet.FacesContextImpl;
047: import org.apache.myfaces.shared_impl.webapp.webxml.WebXml;
048:
049: /**
050: * This portlet initializes MyFaces and converts portlet requests into
051: * JSF requests.
052: *
053: * @author Stan Silvert (latest modification by $Author: dennisbyrne $)
054: * @version $Revision: 511715 $ $Date: 2007-02-26 05:05:36 +0100 (Mo, 26 Feb 2007) $
055: */
056: public class MyFacesGenericPortlet extends GenericPortlet {
057: private static final Log log = LogFactory
058: .getLog(MyFacesGenericPortlet.class);
059:
060: // PortletRequest parameter
061: public static final String VIEW_ID = MyFacesGenericPortlet.class
062: .getName()
063: + ".VIEW_ID";
064:
065: // PortletSession attribute
066: protected static final String CURRENT_FACES_CONTEXT = MyFacesGenericPortlet.class
067: .getName()
068: + ".CURRENT_FACES_CONTEXT";
069:
070: // portlet config parameter from portlet.xml
071: protected static final String DEFAULT_VIEW = "default-view";
072:
073: // portlet config parameter from portlet.xml
074: protected static final String DEFAULT_VIEW_SELECTOR = "default-view-selector";
075:
076: protected static final String FACES_INIT_DONE = MyFacesGenericPortlet.class
077: .getName()
078: + ".FACES_INIT_DONE";
079:
080: protected PortletContext portletContext;
081:
082: protected FacesContextFactory facesContextFactory;
083: protected Lifecycle lifecycle;
084:
085: protected String defaultView;
086: protected DefaultViewSelector defaultViewSelector;
087:
088: /**
089: * Creates a new instance of MyFacesPortlet
090: */
091: public MyFacesGenericPortlet() {
092: }
093:
094: /**
095: * Portlet lifecycle.
096: */
097: public void destroy() {
098: super .destroy();
099: FactoryFinder.releaseFactories();
100: }
101:
102: /**
103: * Portlet lifecycle.
104: */
105: public void init() throws PortletException, UnavailableException {
106: this .portletContext = getPortletContext();
107: setDefaultView();
108: setDefaultViewSelector();
109: initMyFaces();
110:
111: facesContextFactory = (FacesContextFactory) FactoryFinder
112: .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
113:
114: // Javadoc says: Lifecycle instance is shared across multiple simultaneous requests, it must be
115: // implemented in a thread-safe manner. So we can acquire it here once:
116: LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
117: .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
118: lifecycle = lifecycleFactory.getLifecycle(getLifecycleId());
119: }
120:
121: protected void setDefaultView() throws UnavailableException {
122: this .defaultView = getPortletConfig().getInitParameter(
123: DEFAULT_VIEW);
124: if (defaultView == null) {
125: String msg = "Fatal: must specify a JSF view id as the default view in portlet.xml";
126: throw new UnavailableException(msg);
127: }
128: }
129:
130: protected void setDefaultViewSelector() throws UnavailableException {
131: String selectorClass = getPortletConfig().getInitParameter(
132: DEFAULT_VIEW_SELECTOR);
133: if (selectorClass == null)
134: return;
135:
136: try {
137: this .defaultViewSelector = (DefaultViewSelector) Class
138: .forName(selectorClass).newInstance();
139: this .defaultViewSelector
140: .setPortletContext(getPortletContext());
141: } catch (Exception e) {
142: log.error("Failed to load " + DEFAULT_VIEW_SELECTOR, e);
143: throw new UnavailableException(e.getMessage());
144: }
145: }
146:
147: protected void setContentType(RenderRequest request,
148: RenderResponse response) {
149:
150: if (response.getContentType() == null) {
151: String portalPreferredContentType = request
152: .getResponseContentType();
153: if (portalPreferredContentType != null) {
154: response.setContentType(portalPreferredContentType);
155: } else {
156: response.setContentType("text/html");
157: }
158: }
159: }
160:
161: protected String getLifecycleId() {
162: String lifecycleId = getPortletConfig().getInitParameter(
163: FacesServlet.LIFECYCLE_ID_ATTR);
164: return lifecycleId != null ? lifecycleId
165: : LifecycleFactory.DEFAULT_LIFECYCLE;
166: }
167:
168: protected void initMyFaces() {
169: try {
170: Boolean b = (Boolean) portletContext
171: .getAttribute(FACES_INIT_DONE);
172:
173: if (b == null || b.booleanValue() == false) {
174: log.trace("Initializing MyFaces");
175:
176: //Load the configuration
177: ExternalContext externalContext = new PortletExternalContextImpl(
178: portletContext, null, null);
179:
180: //And configure everything
181:
182: // TODO: FIX PORTLET STUFF FOR JSF 1.2
183: // new FacesConfigurator(externalContext).configure();
184:
185: // parse web.xml - not sure if this is needed for portlet
186: WebXml.init(externalContext);
187:
188: portletContext.setAttribute(FACES_INIT_DONE,
189: Boolean.TRUE);
190: } else {
191: log.info("MyFaces already initialized");
192: }
193: } catch (Exception ex) {
194: log.error("Error initializing MyFacesGenericPortlet", ex);
195: }
196:
197: log.info("PortletContext '" + portletContext.getRealPath("/")
198: + "' initialized.");
199: }
200:
201: /**
202: * Called by the portlet container to allow the portlet to process an action request.
203: */
204: public void processAction(ActionRequest request,
205: ActionResponse response) throws PortletException,
206: IOException {
207: if (log.isTraceEnabled())
208: log.trace("called processAction");
209:
210: if (sessionTimedOut(request))
211: return;
212:
213: setPortletRequestFlag(request);
214:
215: FacesContext facesContext = facesContext(request, response);
216:
217: try {
218: lifecycle.execute(facesContext);
219:
220: if (!facesContext.getResponseComplete()) {
221: response.setRenderParameter(VIEW_ID, facesContext
222: .getViewRoot().getViewId());
223: }
224:
225: request.getPortletSession().setAttribute(
226: CURRENT_FACES_CONTEXT, facesContext);
227: } catch (Throwable e) {
228: facesContext.release();
229: handleExceptionFromLifecycle(e);
230: }
231: }
232:
233: protected void handleExceptionFromLifecycle(Throwable e)
234: throws PortletException, IOException {
235: logException(e, null);
236:
237: if (e instanceof IOException) {
238: throw (IOException) e;
239: }
240:
241: if (e instanceof PortletException) {
242: throw (PortletException) e;
243: }
244:
245: if (e.getMessage() != null) {
246: throw new PortletException(e.getMessage(), e);
247: }
248:
249: throw new PortletException(e);
250: }
251:
252: /**
253: * Helper method to serve up the view mode.
254: */
255: protected void doView(RenderRequest request, RenderResponse response)
256: throws PortletException, IOException {
257: facesRender(request, response);
258: }
259:
260: /**
261: * Helper method to serve up the edit mode. Can be overridden to add
262: * the edit mode concept to a JSF application.
263: */
264: protected void doEdit(RenderRequest request, RenderResponse response)
265: throws PortletException, IOException {
266: facesRender(request, response);
267: }
268:
269: /**
270: * Helper method to serve up the edit mode. Can be overridden to add
271: * the help mode concept to a JSF application.
272: */
273: protected void doHelp(RenderRequest request, RenderResponse response)
274: throws PortletException, IOException {
275: facesRender(request, response);
276: }
277:
278: /**
279: * This method follows JSF Spec section 2.1.1. It renders the default view from a non-faces
280: * request.
281: *
282: * @param request The portlet render request.
283: * @param response The portlet render response.
284: */
285: protected void nonFacesRequest(RenderRequest request,
286: RenderResponse response) throws PortletException {
287: nonFacesRequest(request, response, selectDefaultView(request,
288: response));
289: }
290:
291: /**
292: * This method follows JSF Spec section 2.1.1. It renders a view from a non-faces
293: * request. This is useful for a default view as well as for views that need to
294: * be rendered from the portlet's edit and help buttons.
295: *
296: * @param request The portlet render request.
297: * @param response The portlet render response.
298: * @param view The name of the view that needs to be rendered.
299: */
300: protected void nonFacesRequest(RenderRequest request,
301: RenderResponse response, String view)
302: throws PortletException {
303: if (log.isTraceEnabled())
304: log.trace("Non-faces request: contextPath = "
305: + request.getContextPath());
306: setContentType(request, response); // do this in case nonFacesRequest is called by a subclass
307: ApplicationFactory appFactory = (ApplicationFactory) FactoryFinder
308: .getFactory(FactoryFinder.APPLICATION_FACTORY);
309: Application application = appFactory.getApplication();
310: ViewHandler viewHandler = application.getViewHandler();
311: FacesContext facesContext = facesContext(request, response);
312: UIViewRoot viewRoot = viewHandler
313: .createView(facesContext, view);
314: viewRoot.setViewId(view);
315: facesContext.setViewRoot(viewRoot);
316: lifecycle.render(facesContext);
317: }
318:
319: protected String selectDefaultView(RenderRequest request,
320: RenderResponse response) throws PortletException {
321: String view = this .defaultView;
322: if (this .defaultViewSelector != null) {
323: String selectedView = this .defaultViewSelector
324: .selectViewId(request, response);
325: if (selectedView != null) {
326: view = selectedView;
327: }
328: }
329:
330: return view;
331: }
332:
333: protected FacesContext facesContext(PortletRequest request,
334: PortletResponse response) {
335: return facesContextFactory.getFacesContext(portletContext,
336: request, response, lifecycle);
337: }
338:
339: protected ReleaseableExternalContext makeExternalContext(
340: PortletRequest request, PortletResponse response) {
341: return new PortletExternalContextImpl(portletContext, request,
342: response);
343: }
344:
345: protected boolean sessionTimedOut(PortletRequest request) {
346: return request.getPortletSession(false) == null;
347: }
348:
349: protected void setPortletRequestFlag(PortletRequest request) {
350: request.getPortletSession().setAttribute(
351: PortletUtil.PORTLET_REQUEST_FLAG, "true");
352: }
353:
354: /**
355: * Render a JSF view.
356: */
357: protected void facesRender(RenderRequest request,
358: RenderResponse response) throws PortletException,
359: java.io.IOException {
360: if (log.isTraceEnabled())
361: log.trace("called facesRender");
362:
363: setContentType(request, response);
364:
365: String viewId = request.getParameter(VIEW_ID);
366: if ((viewId == null) || sessionTimedOut(request)) {
367: setPortletRequestFlag(request);
368: nonFacesRequest(request, response);
369: return;
370: }
371:
372: setPortletRequestFlag(request);
373:
374: try {
375: FacesContextImpl facesContext = (FacesContextImpl) request
376: .getPortletSession().getAttribute(
377: CURRENT_FACES_CONTEXT);
378:
379: // TODO: not sure if this can happen. Also double check this against spec section 2.1.3
380: if (facesContext.getResponseComplete())
381: return;
382:
383: facesContext.setExternalContext(makeExternalContext(
384: request, response));
385: lifecycle.render(facesContext);
386: } catch (Throwable e) {
387: handleExceptionFromLifecycle(e);
388: }
389: }
390:
391: protected void logException(Throwable e, String msgPrefix) {
392: String msg;
393: if (msgPrefix == null) {
394: if (e.getMessage() == null) {
395: msg = "Exception in FacesServlet";
396: } else {
397: msg = e.getMessage();
398: }
399: } else {
400: if (e.getMessage() == null) {
401: msg = msgPrefix;
402: } else {
403: msg = msgPrefix + ": " + e.getMessage();
404: }
405: }
406:
407: portletContext.log(msg, e);
408:
409: Throwable cause = e.getCause();
410: if (cause != null && cause != e) {
411: logException(cause, "Root cause");
412: }
413:
414: if (e instanceof PortletException) {
415: cause = ((PortletException) e).getCause();
416:
417: if (cause != null && cause != e) {
418: logException(cause, "Root cause of PortletException");
419: }
420: }
421: }
422:
423: }
|