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