1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.portals.bridges.jsf;
17
18 import java.io.IOException;
19
20 import javax.faces.FacesException;
21 import javax.faces.FactoryFinder;
22 import javax.faces.application.Application;
23 import javax.faces.component.UIViewRoot;
24 import javax.faces.context.FacesContext;
25 import javax.faces.context.FacesContextFactory;
26 import javax.faces.lifecycle.Lifecycle;
27 import javax.faces.lifecycle.LifecycleFactory;
28 import javax.faces.render.RenderKitFactory;
29 import javax.faces.webapp.FacesServlet;
30
31 import javax.portlet.ActionRequest;
32 import javax.portlet.ActionResponse;
33 import javax.portlet.PortletConfig;
34 import javax.portlet.PortletException;
35 import javax.portlet.PortletMode;
36 import javax.portlet.PortletRequest;
37 import javax.portlet.PortletResponse;
38 import javax.portlet.PortletSession;
39 import javax.portlet.RenderRequest;
40 import javax.portlet.RenderResponse;
41
42 import org.apache.commons.logging.Log;
43 import org.apache.commons.logging.LogFactory;
44 import org.apache.portals.bridges.common.GenericServletPortlet;
45
46 /***
47 * <p>
48 * FacesPortlet utilizes Java Server Faces to create the user interface in a
49 * portlet environment.
50 * </p>
51 *
52 * @author <a href="mailto:dlestrat@yahoo.com">David Le Strat</a>
53 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
54 */
55 public class FacesPortlet extends GenericServletPortlet
56 {
57
58 /*** The Log instance for this class. */
59 private static final Log log = LogFactory.getLog(FacesPortlet.class);
60
61 /*** The VIEW_ROOT used to keep track of action between the action request and the render request. */
62 public static final String VIEW_ROOT = "org.apache.portals.bridges.jsf.VIEW_ROOT";
63
64 /***
65 * The REQUEST_SERVLET_PATH used for externalContext.getRequestServletPath(). externalContext.getRequestServletPath()
66 * should return null but this is a work around an issue with MyFaces JspViewHandler implementation getServletMapping().
67 */
68 public static final String REQUEST_SERVLET_PATH = "org.apache.portals.bridges.jsf.REQUEST_SERVLET_PATH";
69
70 /*** The JSF_VIEW_ID used to maintain the state of the view action. */
71 public static final String JSF_VIEW_ID = "jsf_viewid";
72 public static final String JSF_EDIT_ID = "jsf_editid";
73 public static final String JSF_HELP_ID = "jsf_helpid";
74 public static final String JSF_CUSTOM_ID = "jsf_customid";
75
76 /*** Name of portlet preference for Action page. */
77 public static final String PARAM_ACTION_PAGE = "ActionPage";
78
79 /*** Name of portlet preference for Custom page. */
80 public static final String PARAM_CUSTOM_PAGE = "CustomPage";
81
82 /*** Name of portlet preference for Edit page. */
83 public static final String PARAM_EDIT_PAGE = "EditPage";
84
85 /*** Name of portlet preference for Edit page */
86 public static final String PARAM_HELP_PAGE = "HelpPage";
87
88 /*** Name of portlet preference for View page */
89 public static final String PARAM_VIEW_PAGE = "ViewPage";
90
91 /*** Action request. */
92 public static final String ACTION_REQUEST = "ACTION";
93
94 /*** View request. */
95 public static final String VIEW_REQUEST = "VIEW";
96
97 /*** Custom request. */
98 public static final String CUSTOM_REQUEST = "CUSTOM";
99
100 /*** Edit request. */
101 public static final String EDIT_REQUEST = "EDIT";
102
103 /*** Help request. */
104 public static final String HELP_REQUEST = "HELP";
105
106 /*** Default URL for the action page. */
107 private String defaultActionPage = null;
108
109 /*** Default URL for the custom page. */
110 private String defaultCustomPage = null;
111
112 /*** Default URL for the edit page. */
113 private String defaultEditPage = null;
114
115 /*** Default URL for the help page. */
116 private String defaultHelpPage = null;
117
118 /*** Default URL for the view page. */
119 private String defaultViewPage = null;
120
121 /***
122 * <p>
123 * Context initialization parameter name for the lifecycle identifier of the
124 * {@link Lifecycle}instance to be utilized.
125 * </p>
126 */
127 private static final String LIFECYCLE_ID_ATTR = FacesServlet.LIFECYCLE_ID_ATTR;
128
129 /***
130 * <p>
131 * The {@link Application}instance for this web application.
132 * </p>
133 */
134 private Application application = null;
135
136 /***
137 * <p>
138 * Factory for {@link FacesContext}instances.
139 * </p>
140 */
141 private FacesContextFactory facesContextFactory = null;
142
143 /***
144 * <p>
145 * The {@link Lifecycle}instance to use for request processing.
146 * </p>
147 */
148 private Lifecycle lifecycle = null;
149
150 /***
151 * <p>
152 * The <code>PortletConfig</code> instance for this portlet.
153 * </p>
154 */
155 private PortletConfig portletConfig = null;
156
157 /***
158 * <p>
159 * Release all resources acquired at startup time.
160 * </p>
161 */
162 public void destroy()
163 {
164 if (log.isTraceEnabled())
165 {
166 log.trace("Begin FacesPortlet.destory() ");
167 }
168 application = null;
169 facesContextFactory = null;
170 lifecycle = null;
171 portletConfig = null;
172 if (log.isTraceEnabled())
173 {
174 log.trace("End FacesPortlet.destory() ");
175 }
176
177 }
178
179 /***
180 * <p>
181 * Acquire the factory instance we will require.
182 * </p>
183 *
184 * @exception PortletException if, for any reason, the startp of this Faces
185 * application failed. This includes errors in the config
186 * file that is parsed before or during the processing of
187 * this <code>init()</code> method.
188 */
189 public void init(PortletConfig portletConfig) throws PortletException
190 {
191
192 if (log.isTraceEnabled())
193 {
194 log.trace("Begin FacesPortlet.init() ");
195 }
196
197 super.init(portletConfig);
198
199
200 this.portletConfig = portletConfig;
201 this.defaultViewPage = portletConfig.getInitParameter(PARAM_VIEW_PAGE);
202 this.defaultEditPage = portletConfig.getInitParameter(PARAM_EDIT_PAGE);
203 this.defaultHelpPage = portletConfig.getInitParameter(PARAM_HELP_PAGE);
204
205 if (null == this.defaultViewPage)
206 {
207
208
209
210 throw new PortletException("Portlet " + portletConfig.getPortletName()
211 + " is incorrectly configured. No default View page is defined.");
212 }
213 if (null == this.defaultActionPage)
214 {
215 this.defaultActionPage = this.defaultViewPage;
216 }
217 if (null == this.defaultCustomPage)
218 {
219 this.defaultCustomPage = this.defaultViewPage;
220 }
221 if (null == this.defaultHelpPage)
222 {
223 this.defaultHelpPage = this.defaultViewPage;
224 }
225 if (null == this.defaultEditPage)
226 {
227 this.defaultEditPage = this.defaultViewPage;
228 }
229 if (log.isTraceEnabled())
230 {
231 log.trace("End FacesPortlet.init() ");
232 }
233 }
234
235 /***
236 * @see javax.portlet.GenericPortlet#doEdit(javax.portlet.RenderRequest,
237 * javax.portlet.RenderResponse)
238 */
239 public void doEdit(RenderRequest request, RenderResponse response) throws PortletException, IOException
240 {
241 process(request, response, defaultEditPage, FacesPortlet.EDIT_REQUEST, JSF_EDIT_ID);
242 }
243
244 /***
245 * @see javax.portlet.GenericPortlet#doHelp(javax.portlet.RenderRequest,
246 * javax.portlet.RenderResponse)
247 */
248 public void doHelp(RenderRequest request, RenderResponse response) throws PortletException, IOException
249 {
250 if (this.defaultHelpPage != null && this.defaultHelpPage.endsWith(".html"))
251 {
252 super.doHelp(request, response);
253 }
254 else
255 {
256 process(request, response, defaultHelpPage, FacesPortlet.HELP_REQUEST, JSF_HELP_ID);
257 }
258 }
259
260 /***
261 * @param request The {@link RenderRequest}.
262 * @param response The {@link RenderResponse}.
263 * @throws PortletException Throws a {@link PortletException}.
264 * @throws IOException Throws a {@link IOException}.
265 */
266 public void doCustom(RenderRequest request, RenderResponse response) throws PortletException, IOException
267 {
268 process(request, response, defaultCustomPage, FacesPortlet.CUSTOM_REQUEST, JSF_CUSTOM_ID);
269 }
270
271 /***
272 * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest,
273 * javax.portlet.RenderResponse)
274 */
275 public void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException
276 {
277 process(request, response, defaultViewPage, FacesPortlet.VIEW_REQUEST, JSF_VIEW_ID);
278 }
279
280 /***
281 * @see javax.portlet.Portlet#processAction(javax.portlet.ActionRequest,
282 * javax.portlet.ActionResponse)
283 */
284 public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException
285 {
286 String viewId = JSF_CUSTOM_ID;
287 if (request.getPortletMode().equals(PortletMode.VIEW))
288 {
289 viewId = JSF_VIEW_ID;
290 }
291 else if (request.getPortletMode().equals(PortletMode.EDIT))
292 {
293 viewId = JSF_EDIT_ID;
294 }
295 else if (request.getPortletMode().equals(PortletMode.HELP))
296 {
297 viewId = JSF_HELP_ID;
298 }
299 process(request, response, defaultActionPage, FacesPortlet.ACTION_REQUEST, viewId);
300 }
301
302 /***
303 * <p>
304 * Gets the {@link FacesContextFactory}.
305 * </p>
306 *
307 * @return The {@link FacesContextFactory}.
308 * @throws PortletException Throws a {@link PortletException}.
309 */
310 public FacesContextFactory getFacesContextFactory() throws PortletException
311 {
312 if (facesContextFactory != null)
313 {
314 return facesContextFactory;
315 }
316 try
317 {
318 facesContextFactory = (FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
319 if (log.isTraceEnabled())
320 {
321 log.trace("Retrieved facesContextFactory " + facesContextFactory);
322 }
323 }
324 catch (FacesException e)
325 {
326 Throwable rootCause = e.getCause();
327 if (rootCause == null)
328 {
329 throw e;
330 }
331 else
332 {
333 throw new PortletException(e.getMessage(), rootCause);
334 }
335 }
336 return facesContextFactory;
337 }
338
339 /***
340 * <p>
341 * Get the faces life cycle.
342 * </p>
343 *
344 * @return The {@link Lifecycle}.
345 * @throws PortletException Throws a {@link PortletException}.
346 */
347 public Lifecycle getLifecycle() throws PortletException
348 {
349 if (lifecycle != null)
350 {
351 return lifecycle;
352 }
353 try
354 {
355 LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder
356 .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
357 if (log.isTraceEnabled())
358 {
359 log.trace("Retrieved lifecycleFactory " + lifecycleFactory);
360 }
361 String lifecycleId = portletConfig.getPortletContext().getInitParameter(LIFECYCLE_ID_ATTR);
362 if (log.isDebugEnabled())
363 {
364 log.debug("lifecycleId " + lifecycleId);
365 }
366 if (lifecycleId == null)
367 {
368 lifecycleId = LifecycleFactory.DEFAULT_LIFECYCLE;
369 }
370 lifecycle = lifecycleFactory.getLifecycle(lifecycleId);
371 if (log.isTraceEnabled())
372 {
373 log.trace("Retrieved lifecycle from lifecycleFactory " + lifecycle);
374 }
375 }
376 catch (FacesException e)
377 {
378 Throwable rootCause = e.getCause();
379 if (rootCause == null)
380 {
381 throw e;
382 }
383 else
384 {
385 throw new PortletException(e.getMessage(), rootCause);
386 }
387 }
388 return lifecycle;
389 }
390
391 /***
392 * <p>
393 * Processes the request.
394 * </p>
395 *
396 * @param request The {@link PortletRequest}.
397 * @param response The {@link PortletResponse}.
398 * @param defaultPage The default page.
399 * @param requestType The request type.
400 * @throws PortletException Throws a {@link PortletException}.
401 * @throws IOException Throws an {@link IOException}.
402 */
403 private void process(PortletRequest request, PortletResponse response, String defaultPage, String requestType, String viewId)
404 throws PortletException, IOException
405 {
406 boolean actionRequest = (request instanceof ActionRequest);
407 boolean renderRequest = (request instanceof RenderRequest);
408
409 String defaultView = defaultPage;
410
411 if (actionRequest)
412 {
413 log.trace("Begin FacesPortlet.processAction()");
414 }
415
416
417
418 FacesContext context = getFacesContextFactory().getFacesContext(
419 portletConfig,
420 request, response, getLifecycle());
421
422
423 setDefaultView(context, defaultPage, viewId);
424 if (log.isTraceEnabled())
425 {
426 log.trace("Begin Executing phases");
427 }
428
429 preProcessFaces(context);
430
431
432 try
433 {
434 if (actionRequest)
435 {
436 getLifecycle().execute(context);
437 if (log.isTraceEnabled())
438 {
439 log.trace("End Executing phases");
440 }
441
442
443
444 request.getPortletSession().setAttribute(createViewRootKey(context, defaultPage, viewId), context.getViewRoot());
445 ActionResponse actionResponse = (ActionResponse)response;
446
447
448 }
449 else if (renderRequest)
450 {
451
452 String vi = context.getViewRoot().getViewId();
453 context.getApplication().getViewHandler().restoreView(context, vi);
454
455 getLifecycle().render(context);
456 if (log.isTraceEnabled())
457 {
458 log.trace("End executing RenderResponse phase ");
459 }
460 }
461 else
462 {
463 throw new PortletException("Request must be of type ActionRequest or RenderRequest");
464 }
465 request.getPortletSession().setAttribute(viewId, context.getViewRoot().getViewId(), PortletSession.PORTLET_SCOPE);
466
467 }
468 catch (FacesException e)
469 {
470 Throwable t = ((FacesException) e).getCause();
471 if (t == null)
472 {
473 throw new PortletException(e.getMessage(), e);
474 }
475 else
476 {
477 if (t instanceof PortletException)
478 {
479 throw ((PortletException) t);
480 }
481 else if (t instanceof IOException)
482 {
483 throw ((IOException) t);
484 }
485 else
486 {
487 throw new PortletException(t.getMessage(), t);
488 }
489 }
490 }
491 finally
492 {
493
494 context.release();
495 }
496 if (log.isTraceEnabled())
497 {
498 log.trace("End FacesPortlet.process()");
499 }
500 }
501
502 protected void preProcessFaces(FacesContext context)
503 {
504 }
505
506
507 private String createViewRootKey(FacesContext context, String defaultView, String viewId)
508 {
509 PortletRequest portletRequest = (PortletRequest) context.getExternalContext().getRequest();
510
511 String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
512
513 if (view == null)
514 {
515 view = defaultView;
516 }
517 String key = VIEW_ROOT + ":" + getPortletName();
518 UIViewRoot root = context.getViewRoot();
519 if (root != null)
520 {
521 key = key + ":" + root.getViewId();
522 }
523 else
524 {
525 key = key + ":" + view;
526 }
527 return key;
528 }
529
530 /***
531 * <p>
532 * Set the view identifier to the view for the page to be rendered.
533 * </p>
534 *
535 * @param context The {@link FacesContext}for the current request.
536 * @param defaultView The default view identifier.
537 * @return The default view.
538 */
539 private void setDefaultView(FacesContext facesContext, String defaultView, String viewId)
540 {
541
542
543 PortletRequest portletRequest = (PortletRequest) facesContext.getExternalContext().getRequest();
544 if (portletRequest instanceof ActionRequest)
545 {
546 String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
547
548 if ((null != facesContext.getViewRoot()) && (null != facesContext.getViewRoot().getViewId()))
549 {
550 defaultView = facesContext.getViewRoot().getViewId();
551 }
552
553 else if (null != view)
554 {
555
556 defaultView = view;
557 }
558
559 UIViewRoot viewRoot = (UIViewRoot)portletRequest.
560 getPortletSession().
561 getAttribute(createViewRootKey(facesContext, defaultView, viewId));
562 if (viewRoot != null)
563 {
564 facesContext.setViewRoot(viewRoot);
565 defaultView = facesContext.getViewRoot().getViewId();
566 }
567
568 portletRequest.setAttribute(REQUEST_SERVLET_PATH, defaultView.replaceAll("[.]jsp", ".jsf"));
569 }
570 else if (portletRequest instanceof RenderRequest)
571 {
572
573 String view = (String)portletRequest.getPortletSession().getAttribute(viewId, PortletSession.PORTLET_SCOPE);
574
575 if (null == facesContext.getViewRoot())
576 {
577 if (view == null)
578 {
579 view = defaultView;
580 }
581 UIViewRoot viewRoot = (UIViewRoot)portletRequest.
582 getPortletSession().
583 getAttribute(createViewRootKey(facesContext, view, viewId));
584 if (null != viewRoot)
585 {
586 facesContext.setViewRoot(viewRoot);
587 defaultView = facesContext.getViewRoot().getViewId();
588 }
589 else
590 {
591 facesContext.setViewRoot(new UIViewRoot());
592 facesContext.getViewRoot().setViewId(view);
593 facesContext.getViewRoot().setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
594 portletRequest.getPortletSession().setAttribute(createViewRootKey(facesContext, view, viewId), viewRoot);
595 }
596 }
597 portletRequest.setAttribute(REQUEST_SERVLET_PATH, view.replaceAll(".jsp", ".jsf"));
598 }
599
600 }
601 }