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.DialogContextManager;
32  
33  /***
34   * <p>Phase listener that saves and restores the dialog identifier for the
35   * currently active dialog, if any.  Based on the presence of certain
36   * request parameters, it can also cause a new {@link DialogContext}
37   * instance to be created and started, optionally associated with a parent
38   * {@link DialogContext} instance also belonging to the same user.</p>
39   *
40   * @since 1.0.4
41   */
42  public final class DialogPhaseListener implements PhaseListener {
43  
44  
45      // ------------------------------------------------------------ Constructors
46  
47  
48      /***
49       * Serial version UID.
50       */
51      private static final long serialVersionUID = 5219990658057949928L;
52  
53  
54      /***
55       * <p>Creates a new instance of DialogPhaseListener.</p>
56       */
57      public DialogPhaseListener() {
58          if (log.isInfoEnabled()) {
59              log.info("Instantiating DialogPhaseListener()");
60          }
61      }
62  
63  
64      // ------------------------------------------------------ Manifest Constants
65  
66  
67      /***
68       * <p>Generic attribute name (on the view root component of the current
69       * JSF component tree) under which the context identifier for the
70       * dialog instance that is current for this view (if any) should be
71       * stored and retrieved.</p>
72       */
73      private static final String CONTEXT_ID_ATTR =
74              "org.apache.shale.dialog.CONTEXT_ID";
75  
76  
77      /***
78       * <p>Generic attribute name (on the view root component of the current
79       * JSF component tree) under which the opaque state information for the
80       * current {@link DialogContext} instance (if any) should be stored and
81       * retrieved.</p>
82       */
83      private static final String CONTEXT_OPAQUE_ATTR =
84              "org.apache.shale.dialog.OPAQUE_STATE";
85  
86  
87      // ------------------------------------------------------ Instance Variables
88  
89  
90      /***
91       * <p>The <code>Log</code> instance for this class.</p>
92       */
93      private Log log = LogFactory.getLog(DialogPhaseListener.class);
94  
95  
96      // --------------------------------------------------- PhaseListener Methods
97  
98  
99      /***
100      * <p>Return the phase identifier we are interested in.</p>
101      *
102      * @return The phase identifier of interest
103      */
104     public PhaseId getPhaseId() {
105         return PhaseId.ANY_PHASE;
106     }
107 
108 
109     /***
110      * <p>After the <em>Restore View</em> phase, retrieve the current
111      * dialog identifier (if any), and restore the corresponding
112      * {@link DialogContext}.  If this view is not currently executing
113      * a {@link DialogContext} instance, optionally create a new
114      * instance based o the presence of request parameters.</p>
115      *
116      * @param event The phase event to be processed
117      */
118     public void afterPhase(PhaseEvent event) {
119 
120         if (log.isTraceEnabled()) {
121             log.trace("afterPhase(phaseId='" + event.getPhaseId()
122                       + "',facesContext='" + event.getFacesContext() + "')");
123         }
124 
125         if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) {
126             afterRestoreView(event.getFacesContext());
127         }
128 
129     }
130 
131 
132     /***
133      * <p>Before the <em>Render Response</em> phase, acquire the current
134      * dialog identifier (if any), and store it in the view.</p>
135      *
136      * @param event The phase event to be processed
137      */
138     public void beforePhase(PhaseEvent event) {
139 
140         if (log.isTraceEnabled()) {
141             log.trace("beforePhase(phaseId='" + event.getPhaseId()
142                       + "',facesContext='" + event.getFacesContext() + "')");
143         }
144 
145         if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) {
146             beforeRenderResponse(event.getFacesContext());
147         }
148 
149     }
150 
151 
152     // --------------------------------------------------------- Private Methods
153 
154 
155     /***
156      * <p>Perform the required processing after the <em>Restore View Phase</em>
157      * of the request processing lifecycle has been completed for the current
158      * request:</p>
159      * <ul>
160      * <li>If the restored view contains an appropriate attribute containing
161      *     the <code>id</code> of an existing {@link DialogContext} instance
162      *     for the current user, this instance is restored.<li>
163      * <li>If there is no such <code>id</code> of an existing {@link DialogContext}
164      *     instance, AND if the request includes a parameter specifying a
165      *     dialog name, a new instance of the specified dialog will be created
166      *     and associated with the current view.</li>
167      * <li>In the latter case, if the request also includes a parameter specifying
168      *     the <code>id</code> of an active {@link DialogContext} instance for
169      *     the current user, that existing instance will be configured as the
170      *     parent {@link DialogContext} instance for the newly created instance.</li>
171      * </ul>
172      *
173      * @param context <code>FacesContext</code> for the current request
174      */
175     private void afterRestoreView(FacesContext context) {
176 
177         // If this view has a currently active dialog context instance,
178         // make it visible in request scope and return.  Normally, the
179         // instance identifier is passed as part of the JSF view state,
180         // but it might also have been passed as a request parameter in
181         // the case of a redirect
182         String id = (String)
183           context.getViewRoot().getAttributes().get(CONTEXT_ID_ATTR);
184         if (id == null) {
185             id = (String) context.getExternalContext().getRequestParameterMap().
186                     get(Constants.DIALOG_ID);
187         }
188         if (id != null) {
189             restore(context, id);
190             return;
191         }
192 
193         // If this request includes a request parameter naming a dialog to be
194         // created, create a corresponding {@link DialogContext} instance and
195         // associate it with the current request.  If the request also specifies
196         // the identifier of an existing {@link DialogContext} instance for the
197         // current user, make that instance the parent of the newly created
198         // instance
199         String dialogName = (String) context.getExternalContext().
200                 getRequestParameterMap().get(Constants.DIALOG_NAME);
201         String parentId = (String) context.getExternalContext().
202                 getRequestParameterMap().get(Constants.PARENT_ID);
203         if (dialogName != null) {
204 
205             // Create a new DialogContext instance
206             DialogContext dcontext = create(context, dialogName, parentId);
207             if (dcontext == null) {
208                 return;
209             }
210 
211             // Start the new DialogContext instance
212             dcontext.start(context);
213             if (log.isDebugEnabled()) {
214                 log.debug("afterRestoreView() creating dialog context with id '"
215                           + id + "' for FacesContext instance '"
216                           + context + "' associated with parent dialog context id '"
217                           + parentId + "' and advancing to viewId '"
218                           + context.getViewRoot().getViewId() + "'");
219             }
220 
221         }
222 
223     }
224 
225 
226     /***
227      * <p>Before the <em>Render Response</em> phase, acquire the current
228      * dialog identifier (if any), along with any corresponding opaque
229      * state information, and store it in the view.</p>
230      *
231      * @param context <code>FacesContext</code> for the current request
232      */
233     private void beforeRenderResponse(FacesContext context) {
234 
235         DialogContext dcontext = (DialogContext)
236           context.getExternalContext().getRequestMap().get(Constants.CONTEXT_BEAN);
237         Map map = context.getViewRoot().getAttributes();
238         if ((dcontext != null) && dcontext.isActive()) {
239             if (log.isDebugEnabled()) {
240                 log.debug("beforeRenderResponse() saving dialog context id '"
241                           + dcontext.getId()
242                           + "' for FacesContext instance '"
243                           + context + "'");
244             }
245             map.put(CONTEXT_ID_ATTR, dcontext.getId());
246             Object opaqueState = dcontext.getOpaqueState();
247             if (opaqueState != null) {
248                 map.put(CONTEXT_OPAQUE_ATTR, opaqueState);
249             }
250         } else {
251             if (log.isTraceEnabled()) {
252                 log.trace("beforeRenderResponse() erasing dialog context id "
253                           + " for FacesContext instance '"
254                           + context + "'");
255             }
256             map.remove(CONTEXT_ID_ATTR);
257             map.remove(CONTEXT_OPAQUE_ATTR);
258         }
259 
260     }
261 
262 
263 
264     /***
265      * <p>Create and return a new {@link DialogContext} for the specified
266      * dialog name and optional parent id.  If no such {@link DialogContext}
267      * can be created, return <code>null</code> instead.</p>
268      *
269      * @param context FacesContext for the current request
270      * @param dialogName Logical name of the dialog to be created
271      * @param parentId Parent dialog context instance (if any)
272      * @return The newly created {@link DialogContext}, may be null
273      */
274     private DialogContext create(FacesContext context, String dialogName,
275                                  String parentId) {
276 
277         DialogContextManager manager = (DialogContextManager)
278           context.getApplication().getVariableResolver().
279                 resolveVariable(context, Constants.MANAGER_BEAN);
280         if (manager == null) {
281             return null;
282         }
283         DialogContext parent = null;
284         if (parentId != null) {
285             parent = manager.get(parentId);
286         }
287         DialogContext dcontext = manager.create(context, dialogName, parent);
288         return dcontext;
289 
290     }
291 
292 
293     /***
294      * <p>Restore access to the {@link DialogContext} with the specified id,
295      * if possible.  If there was any opaque state information stored, update
296      * the corresponding {@link DialogContext} instance as well.</p>
297      *
298      * @param context FacesContext for the current request
299      * @param dialogId Dialog identifier of the {@link DialogContext}
300      *  to be restored
301      */
302     private void restore(FacesContext context, String dialogId) {
303 
304         DialogContextManager manager = (DialogContextManager)
305           context.getApplication().getVariableResolver().
306                 resolveVariable(context, Constants.MANAGER_BEAN);
307         if (manager == null) {
308             return;
309         }
310         DialogContext dcontext = manager.get(dialogId);
311         if (dcontext == null) {
312             return;
313         }
314         if (log.isDebugEnabled()) {
315             log.debug("afterPhase() restoring dialog context with id '"
316                       + dialogId + "' for FacesContext instance '"
317                       + context + "'");
318         }
319         context.getExternalContext().getRequestMap().put(Constants.CONTEXT_BEAN, dcontext);
320         Object opaqueState = context.getViewRoot().getAttributes().get(CONTEXT_OPAQUE_ATTR);
321         if (opaqueState != null) {
322             dcontext.setOpaqueState(opaqueState);
323         }
324 
325     }
326 
327 
328 }