1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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
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
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
178
179
180
181
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
194
195
196
197
198
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
206 DialogContext dcontext = create(context, dialogName, parentId);
207 if (dcontext == null) {
208 return;
209 }
210
211
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 }