2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shale.dialog.faces;
19  
20  import java.util.Map;
21  
22  import javax.faces.context.FacesContext;
23  import javax.faces.event.PhaseEvent;
24  import javax.faces.event.PhaseId;
25  import javax.faces.event.PhaseListener;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.shale.dialog.Constants;
30  import org.apache.shale.dialog.DialogContext;
31  import org.apache.shale.dialog.DialogContextListener;
32  import org.apache.shale.dialog.DialogContextManager;
33  import org.apache.shale.dialog.DialogHelper;
34  
35  /***
36   * <p>Phase listener that saves and restores the dialog identifier for the
37   * currently active dialog, if any.  Based on the presence of certain
38   * request parameters, it can also cause a new {@link DialogContext}
39   * instance to be created and started, optionally associated with a parent
40   * {@link DialogContext} instance also belonging to the same user.</p>
41   *
42   * @since 1.0.4
43   */
44  public final class DialogPhaseListener implements PhaseListener {
45  
46  
47      // ------------------------------------------------------------ Constructors
48  
49  
50      /***
51       * Serial version UID.
52       */
53      private static final long serialVersionUID = 5219990658057949928L;
54  
55  
56      /***
57       * <p>Creates a new instance of DialogPhaseListener.</p>
58       */
59      public DialogPhaseListener() {
60          if (log.isInfoEnabled()) {
61              log.info("Instantiating DialogPhaseListener()");
62          }
63      }
64  
65  
66      // ------------------------------------------------------ Manifest Constants
67  
68  
69      /***
70       * <p>Generic attribute name (on the view root component of the current
71       * JSF component tree) under which the context identifier for the
72       * dialog instance that is current for this view (if any) should be
73       * stored and retrieved.</p>
74       */
75      private static final String CONTEXT_ID_ATTR =
76              "org.apache.shale.dialog.CONTEXT_ID";
77  
78  
79      /***
80       * <p>Generic attribute name (on the view root component of the current
81       * JSF component tree) under which the opaque state information for the
82       * current {@link DialogContext} instance (if any) should be stored and
83       * retrieved.</p>
84       */
85      private static final String CONTEXT_OPAQUE_ATTR =
86              "org.apache.shale.dialog.OPAQUE_STATE";
87  
88  
89      // ------------------------------------------------------ Instance Variables
90  
91  
92      /***
93       * <p>The <code>Log</code> instance for this class.</p>
94       */
95      private Log log = LogFactory.getLog(DialogPhaseListener.class);
96  
97  
98      // --------------------------------------------------- PhaseListener Methods
99  
100 
101     /***
102      * <p>Return the phase identifier we are interested in.</p>
103      *
104      * @return The phase identifier of interest
105      */
106     public PhaseId getPhaseId() {
107         return PhaseId.ANY_PHASE;
108     }
109 
110 
111     /***
112      * <p>After the <em>Restore View</em> phase, retrieve the current
113      * dialog identifier (if any), and restore the corresponding
114      * {@link DialogContext}.  If this view is not currently executing
115      * a {@link DialogContext} instance, optionally create a new
116      * instance based o the presence of request parameters.</p>
117      *
118      * @param event The phase event to be processed
119      */
120     public void afterPhase(PhaseEvent event) {
121 
122         if (log.isTraceEnabled()) {
123             log.trace("afterPhase(phaseId='" + event.getPhaseId()
124                       + "',facesContext='" + event.getFacesContext() + "')");
125         }
126 
127         if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) {
128             afterRestoreView(event.getFacesContext());
129         } else if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) {
130             afterRenderResponse(event.getFacesContext());
131         }
132 
133     }
134 
135 
136     /***
137      * <p>Before the <em>Render Response</em> phase, acquire the current
138      * dialog identifier (if any), and store it in the view.</p>
139      *
140      * @param event The phase event to be processed
141      */
142     public void beforePhase(PhaseEvent event) {
143 
144         if (log.isTraceEnabled()) {
145             log.trace("beforePhase(phaseId='" + event.getPhaseId()
146                       + "',facesContext='" + event.getFacesContext() + "')");
147         }
148 
149         if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) {
150             beforeRenderResponse(event.getFacesContext());
151         }
152 
153     }
154 
155 
156     // --------------------------------------------------------- Private Methods
157 
158 
159     /***
160      * <p>Fire an <code>onActivate</code> event to all registered listeners
161      * of the specified {@link DialogContext} instance.</p>
162      *
163      * @param dcontext {@link DialogContext} instance being activated
164      */
165     private void activate(DialogContext dcontext) {
166 
167         DialogContextListener[] listeners = dcontext.getDialogContextListeners();
168         for (int i = 0; i < listeners.length; i++) {
169             listeners[i].onActivate();
170         }
171 
172     }
173 
174 
175     /***
176      * <p>Perform the required processing after the <em>Render Response</em>
177      * phase of request processing lifecycle has been completed for the
178      * current request:</p>
179      * <ul>
180      * <li>If there is a currently active {@link DialogContext} instance
181      *     for the current request, fire an <code>onPassivate()</code> event
182      *     to all of its registered {@link DialogContextListener}s.</li>
183      * </ul>
184      *
185      * @param context <code>FacesContext</code> for the current request
186      *
187      * @since 1.1.0
188      */
189     private void afterRenderResponse(FacesContext context) {
190 
191         DialogContext dcontext = DialogHelper.getDialogContext(context);
192         if (dcontext != null) {
193             passivate(dcontext);
194         }
195 
196     }
197 
198 
199     /***
200      * <p>Perform the required processing after the <em>Restore View Phase</em>
201      * of the request processing lifecycle has been completed for the current
202      * request:</p>
203      * <ul>
204      * <li>If the restored view contains an appropriate attribute containing
205      *     the <code>id</code> of an existing {@link DialogContext} instance
206      *     for the current user, this instance is restored.<li>
207      * <li>If there is no such <code>id</code> of an existing {@link DialogContext}
208      *     instance, AND if the request includes a parameter specifying a
209      *     dialog name, a new instance of the specified dialog will be created
210      *     and associated with the current view.</li>
211      * <li>In the latter case, if the request also includes a parameter specifying
212      *     the <code>id</code> of an active {@link DialogContext} instance for
213      *     the current user, that existing instance will be configured as the
214      *     parent {@link DialogContext} instance for the newly created instance.</li>
215      * <li>(Since 1.1.0) If a {@link DialogContext} instance was restored
216      *     for the current request, fire an <code>onActivate()</code>
217      *     event to all of its registered {@link DialogContextListener}s.</li>
218      * </ul>
219      *
220      * @param context <code>FacesContext</code> for the current request
221      */
222     private void afterRestoreView(FacesContext context) {
223 
224         // If this view has a currently active dialog context instance,
225         // make it visible in request scope and return.  Normally, the
226         // instance identifier is passed as part of the JSF view state,
227         // but it might also have been passed as a request parameter in
228         // the case of a redirect
229         String id = (String)
230           context.getViewRoot().getAttributes().get(CONTEXT_ID_ATTR);
231         if (id == null) {
232             id = (String) context.getExternalContext().getRequestParameterMap().
233                     get(Constants.DIALOG_ID);
234         }
235         if (id != null) {
236             DialogContext dcontext = restore(context, id);
237             if (dcontext != null) {
238                 activate(dcontext);
239             }
240             return;
241         }
242 
243         // If this request includes a request parameter naming a dialog to be
244         // created, create a corresponding {@link DialogContext} instance and
245         // associate it with the current request.  If the request also specifies
246         // the identifier of an existing {@link DialogContext} instance for the
247         // current user, make that instance the parent of the newly created
248         // instance
249         String dialogName = (String) context.getExternalContext().
250                 getRequestParameterMap().get(Constants.DIALOG_NAME);
251         String parentId = (String) context.getExternalContext().
252                 getRequestParameterMap().get(Constants.PARENT_ID);
253         if (dialogName != null) {
254 
255             // Create a new DialogContext instance
256             DialogContext dcontext = create(context, dialogName, parentId);
257             if (dcontext == null) {
258                 return;
259             }
260 
261             // Start the new DialogContext instance
262             dcontext.start(context);
263             if (log.isDebugEnabled()) {
264                 log.debug("afterRestoreView() creating dialog context with id '"
265                           + id + "' for FacesContext instance '"
266                           + context + "' associated with parent dialog context id '"
267                           + parentId + "' and advancing to viewId '"
268                           + context.getViewRoot().getViewId() + "'");
269             }
270 
271         }
272 
273     }
274 
275 
276     /***
277      * <p>Before the <em>Render Response</em> phase, acquire the current
278      * dialog identifier (if any), along with any corresponding opaque
279      * state information, and store it in the view.</p>
280      *
281      * @param context <code>FacesContext</code> for the current request
282      */
283     private void beforeRenderResponse(FacesContext context) {
284 
285         DialogContext dcontext = (DialogContext)
286           context.getExternalContext().getRequestMap().get(Constants.CONTEXT_BEAN);
287         Map map = context.getViewRoot().getAttributes();
288         if ((dcontext != null) && dcontext.isActive()) {
289             if (log.isDebugEnabled()) {
290                 log.debug("beforeRenderResponse() saving dialog context id '"
291                           + dcontext.getId()
292                           + "' for FacesContext instance '"
293                           + context + "'");
294             }
295             map.put(CONTEXT_ID_ATTR, dcontext.getId());
296             Object opaqueState = dcontext.getOpaqueState();
297             if (opaqueState != null) {
298                 map.put(CONTEXT_OPAQUE_ATTR, opaqueState);
299             }
300         } else {
301             if (log.isTraceEnabled()) {
302                 log.trace("beforeRenderResponse() erasing dialog context id "
303                           + " for FacesContext instance '"
304                           + context + "'");
305             }
306             map.remove(CONTEXT_ID_ATTR);
307             map.remove(CONTEXT_OPAQUE_ATTR);
308         }
309 
310     }
311 
312 
313 
314     /***
315      * <p>Create and return a new {@link DialogContext} for the specified
316      * dialog name and optional parent id.  If no such {@link DialogContext}
317      * can be created, return <code>null</code> instead.</p>
318      *
319      * @param context FacesContext for the current request
320      * @param dialogName Logical name of the dialog to be created
321      * @param parentId Parent dialog context instance (if any)
322      * @return The newly created {@link DialogContext}, may be null
323      */
324     private DialogContext create(FacesContext context, String dialogName,
325                                  String parentId) {
326 
327         DialogContextManager manager =
328                 DialogHelper.getDialogContextManager(context);
329         if (manager == null) {
330             return null;
331         }
332         DialogContext parent = null;
333         if (parentId != null) {
334             parent = manager.get(parentId);
335         }
336         DialogContext dcontext = manager.create(context, dialogName, parent);
337         return dcontext;
338 
339     }
340 
341 
342     /***
343      * <p>Fire an <code>onPassivate</code> event to all registered listeners
344      * of the specified {@link DialogContext} instance.</p>
345      *
346      * @param dcontext {@link DialogContext} instance being activated
347      */
348     private void passivate(DialogContext dcontext) {
349 
350         DialogContextListener[] listeners = dcontext.getDialogContextListeners();
351         for (int i = 0; i < listeners.length; i++) {
352             listeners[i].onPassivate();
353         }
354 
355     }
356 
357 
358     /***
359      * <p>Restore access to the {@link DialogContext} with the specified id,
360      * if possible.  If there was any opaque state information stored, update
361      * the corresponding {@link DialogContext} instance as well.</p>
362      *
363      * @param context FacesContext for the current request
364      * @param dialogId Dialog identifier of the {@link DialogContext}
365      *  to be restored
366      *
367      * @return The restored {@link DialogContext} instance (if any)
368      */
369     private DialogContext restore(FacesContext context, String dialogId) {
370 
371         DialogContextManager manager =
372                 DialogHelper.getDialogContextManager(context);
373         if (manager == null) {
374             return null;
375         }
376         DialogContext dcontext = manager.get(dialogId);
377         if (dcontext == null) {
378             return null;
379         }
380         if (log.isDebugEnabled()) {
381             log.debug("afterPhase() restoring dialog context with id '"
382                       + dialogId + "' for FacesContext instance '"
383                       + context + "'");
384         }
385         context.getExternalContext().getRequestMap().put(Constants.CONTEXT_BEAN, dcontext);
386         Object opaqueState = context.getViewRoot().getAttributes().get(CONTEXT_OPAQUE_ATTR);
387         if (opaqueState != null) {
388             dcontext.setOpaqueState(opaqueState);
389         }
390         return dcontext;
391 
392     }
393 
394 
395 }