Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
DialogPhaseListener |
|
| 4.125;4.125 |
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 | 0 | public DialogPhaseListener() { |
58 | 0 | if (log.isInfoEnabled()) { |
59 | 0 | log.info("Instantiating DialogPhaseListener()"); |
60 | } | |
61 | 0 | } |
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 | 0 | 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 | 0 | 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 | 0 | if (log.isTraceEnabled()) { |
121 | 0 | log.trace("afterPhase(phaseId='" + event.getPhaseId() |
122 | + "',facesContext='" + event.getFacesContext() + "')"); | |
123 | } | |
124 | ||
125 | 0 | if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) { |
126 | 0 | afterRestoreView(event.getFacesContext()); |
127 | } | |
128 | ||
129 | 0 | } |
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 | 0 | if (log.isTraceEnabled()) { |
141 | 0 | log.trace("beforePhase(phaseId='" + event.getPhaseId() |
142 | + "',facesContext='" + event.getFacesContext() + "')"); | |
143 | } | |
144 | ||
145 | 0 | if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) { |
146 | 0 | beforeRenderResponse(event.getFacesContext()); |
147 | } | |
148 | ||
149 | 0 | } |
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 | 0 | String id = (String) |
183 | context.getViewRoot().getAttributes().get(CONTEXT_ID_ATTR); | |
184 | 0 | if (id == null) { |
185 | 0 | id = (String) context.getExternalContext().getRequestParameterMap(). |
186 | get(Constants.DIALOG_ID); | |
187 | } | |
188 | 0 | if (id != null) { |
189 | 0 | restore(context, id); |
190 | 0 | 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 | 0 | String dialogName = (String) context.getExternalContext(). |
200 | getRequestParameterMap().get(Constants.DIALOG_NAME); | |
201 | 0 | String parentId = (String) context.getExternalContext(). |
202 | getRequestParameterMap().get(Constants.PARENT_ID); | |
203 | 0 | if (dialogName != null) { |
204 | ||
205 | // Create a new DialogContext instance | |
206 | 0 | DialogContext dcontext = create(context, dialogName, parentId); |
207 | 0 | if (dcontext == null) { |
208 | 0 | return; |
209 | } | |
210 | ||
211 | // Start the new DialogContext instance | |
212 | 0 | dcontext.start(context); |
213 | 0 | if (log.isDebugEnabled()) { |
214 | 0 | 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 | 0 | } |
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 | 0 | DialogContext dcontext = (DialogContext) |
236 | context.getExternalContext().getRequestMap().get(Constants.CONTEXT_BEAN); | |
237 | 0 | Map map = context.getViewRoot().getAttributes(); |
238 | 0 | if ((dcontext != null) && dcontext.isActive()) { |
239 | 0 | if (log.isDebugEnabled()) { |
240 | 0 | log.debug("beforeRenderResponse() saving dialog context id '" |
241 | + dcontext.getId() | |
242 | + "' for FacesContext instance '" | |
243 | + context + "'"); | |
244 | } | |
245 | 0 | map.put(CONTEXT_ID_ATTR, dcontext.getId()); |
246 | 0 | Object opaqueState = dcontext.getOpaqueState(); |
247 | 0 | if (opaqueState != null) { |
248 | 0 | map.put(CONTEXT_OPAQUE_ATTR, opaqueState); |
249 | } | |
250 | 0 | } else { |
251 | 0 | if (log.isTraceEnabled()) { |
252 | 0 | log.trace("beforeRenderResponse() erasing dialog context id " |
253 | + " for FacesContext instance '" | |
254 | + context + "'"); | |
255 | } | |
256 | 0 | map.remove(CONTEXT_ID_ATTR); |
257 | 0 | map.remove(CONTEXT_OPAQUE_ATTR); |
258 | } | |
259 | ||
260 | 0 | } |
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 | 0 | DialogContextManager manager = (DialogContextManager) |
278 | context.getApplication().getVariableResolver(). | |
279 | resolveVariable(context, Constants.MANAGER_BEAN); | |
280 | 0 | if (manager == null) { |
281 | 0 | return null; |
282 | } | |
283 | 0 | DialogContext parent = null; |
284 | 0 | if (parentId != null) { |
285 | 0 | parent = manager.get(parentId); |
286 | } | |
287 | 0 | DialogContext dcontext = manager.create(context, dialogName, parent); |
288 | 0 | 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 | 0 | DialogContextManager manager = (DialogContextManager) |
305 | context.getApplication().getVariableResolver(). | |
306 | resolveVariable(context, Constants.MANAGER_BEAN); | |
307 | 0 | if (manager == null) { |
308 | 0 | return; |
309 | } | |
310 | 0 | DialogContext dcontext = manager.get(dialogId); |
311 | 0 | if (dcontext == null) { |
312 | 0 | return; |
313 | } | |
314 | 0 | if (log.isDebugEnabled()) { |
315 | 0 | log.debug("afterPhase() restoring dialog context with id '" |
316 | + dialogId + "' for FacesContext instance '" | |
317 | + context + "'"); | |
318 | } | |
319 | 0 | context.getExternalContext().getRequestMap().put(Constants.CONTEXT_BEAN, dcontext); |
320 | 0 | Object opaqueState = context.getViewRoot().getAttributes().get(CONTEXT_OPAQUE_ATTR); |
321 | 0 | if (opaqueState != null) { |
322 | 0 | dcontext.setOpaqueState(opaqueState); |
323 | } | |
324 | ||
325 | 0 | } |
326 | ||
327 | ||
328 | } |