1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.custom.redirectTracker;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23 import org.apache.myfaces.shared_tomahawk.util.ClassUtils;
24 import org.apache.myfaces.custom.redirectTracker.policy.NoopRedirectTrackPolicy;
25
26 import javax.faces.application.FacesMessage;
27 import javax.faces.context.FacesContext;
28 import javax.faces.context.ExternalContext;
29 import javax.faces.el.ValueBinding;
30 import javax.faces.FacesException;
31 import java.io.Serializable;
32 import java.util.ArrayList;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.TreeMap;
38
39
40
41
42
43 public class RedirectTrackerManager implements Serializable
44 {
45 private final static String INIT_POLICY = "org.apache.myfaces.redirectTracker.POLICY";
46 private final static String INIT_MAX_REDIRECTS = "org.apache.myfaces.redirectTracker.MAX_REDIRECTS";
47 public static final int TRACK_REDIRECTS = 20;
48
49 private final static Log log = LogFactory.getLog(RedirectTrackerManager.class);
50
51 public static final String SESSION_KEY = RedirectTrackerManager.class.getName();
52 public static final String REDIRECT_ARG = "_rtid";
53
54 private final String redirectTrackerPolicy;
55 private final int redirects;
56 private transient Map requestBeanMap;
57
58 private Map redirectEntryMap;
59 private List redirectEntryList;
60 private long requests;
61
62 static class Entry implements Serializable
63 {
64 private final String mapKey;
65 private List messages;
66 private Map beanMap = new TreeMap();
67 private Locale locale;
68
69 private Entry(String mapKey)
70 {
71 this.mapKey = mapKey;
72 }
73
74 void addMessage(Object clientId, Object message)
75 {
76 if (messages == null)
77 {
78 messages = new ArrayList();
79 }
80 messages.add(new MessageEntry(clientId, message));
81 }
82
83 public Iterator getMessages()
84 {
85 if (messages == null)
86 {
87 return null;
88 }
89
90 return messages.iterator();
91 }
92
93 public boolean hasCapturedData()
94 {
95 return (messages != null && messages.size() > 0)
96 || (beanMap != null && beanMap.size() > 0)
97 || locale != null;
98 }
99 }
100
101 private static class MessageEntry implements Serializable
102 {
103 private final Object clientId;
104 private final Object message;
105
106 public MessageEntry(Object clientId, Object message)
107 {
108 this.clientId = clientId;
109 this.message = message;
110 }
111 }
112
113
114
115
116
117
118 public RedirectTrackerManager(final int redirects, final String redirectTrackerPolicy)
119 {
120 this.redirects = redirects;
121 this.redirectTrackerPolicy = redirectTrackerPolicy;
122 }
123
124 protected void initRedirectEntryMap()
125 {
126 if (redirectEntryMap == null)
127 {
128 redirectEntryMap = new TreeMap();
129 redirectEntryList = new ArrayList(redirects);
130 }
131 }
132
133
134
135
136 public void processTrackedRequest(FacesContext facesContext)
137 {
138 Object rtid = facesContext.getExternalContext().getRequestParameterMap().get(REDIRECT_ARG);
139 if (!isRedirectedRequest(rtid))
140 {
141 return;
142 }
143
144 setupFaces(facesContext, rtid);
145 }
146
147
148
149
150 protected boolean isRedirectedRequest(Object rtid)
151 {
152 if (rtid == null)
153 {
154 return false;
155 }
156
157 initRedirectEntryMap();
158 synchronized (redirectEntryMap)
159 {
160 return redirectEntryMap.containsKey(rtid);
161 }
162 }
163
164
165
166
167 public static RedirectTrackerManager getInstance(FacesContext facesContext)
168 {
169 ExternalContext exCtx = facesContext.getExternalContext();
170 Map sessionMap = exCtx.getSessionMap();
171 Object session = exCtx.getSession(false);
172
173 if (session == null || sessionMap == null)
174 {
175 return null;
176 }
177 RedirectTrackerManager redirectManager = (RedirectTrackerManager) sessionMap.get(SESSION_KEY);
178 if (redirectManager == null)
179 {
180 redirectManager = createRedirectTrackerManager(facesContext);
181 sessionMap.put(SESSION_KEY, redirectManager);
182 }
183
184 return redirectManager;
185 }
186
187
188
189
190 protected static RedirectTrackerManager createRedirectTrackerManager(FacesContext facesContext)
191 {
192 String initPolicy = (String) facesContext.getExternalContext().getInitParameter(INIT_POLICY);
193 if (initPolicy == null)
194 {
195 initPolicy = NoopRedirectTrackPolicy.class.getName();
196 if (log.isInfoEnabled())
197 {
198 log.info("No context init parameter '" + INIT_POLICY + "' found, using default value " + initPolicy);
199 }
200 }
201
202 String maxRedirects = (String) facesContext.getExternalContext().getInitParameter(INIT_MAX_REDIRECTS);
203 int numMaxRedirects;
204 if (maxRedirects == null)
205 {
206 numMaxRedirects = TRACK_REDIRECTS;
207 if (log.isInfoEnabled())
208 {
209 log.info("No context init parameter '" + INIT_MAX_REDIRECTS + "' found, using default value " + numMaxRedirects);
210 }
211 }
212 else
213 {
214 numMaxRedirects = Integer.parseInt(maxRedirects, 10);
215 }
216
217 return new RedirectTrackerManager(numMaxRedirects, initPolicy);
218 }
219
220
221
222
223
224 public String trackRedirect(FacesContext facesContext, String redirectPath)
225 {
226 long rtid = getNextRequestNo();
227 String mapKey = Long.toString(rtid, Character.MAX_RADIX);
228
229 Entry entry = new Entry(mapKey);
230
231 RedirectTrackerContext context = new RedirectTrackerContext(this, entry, facesContext);
232
233 RedirectTrackerPolicy policy = getRedirectTrackerPolicy();
234 redirectPath = policy.trackRedirect(context, redirectPath);
235
236
237
238
239
240 clearSaveStateBean();
241
242 if (!entry.hasCapturedData())
243 {
244
245 return redirectPath;
246 }
247
248 initRedirectEntryMap();
249 synchronized (redirectEntryMap)
250 {
251 redirectEntryMap.put(mapKey, entry);
252 redirectEntryList.add(entry);
253
254 while (redirectEntryList.size() > redirects)
255 {
256 Entry prevEntry = (Entry) redirectEntryList.remove(0);
257 redirectEntryMap.remove(prevEntry.mapKey);
258 }
259 }
260
261 if (redirectPath.indexOf('?') == -1)
262 {
263 return redirectPath + "?" + REDIRECT_ARG + "=" + mapKey;
264 }
265 else
266 {
267 return redirectPath + "&" + REDIRECT_ARG + "=" + mapKey;
268 }
269 }
270
271 protected RedirectTrackerPolicy getRedirectTrackerPolicy()
272 {
273 try
274 {
275 return (RedirectTrackerPolicy) ClassUtils.classForName(redirectTrackerPolicy).newInstance();
276 }
277 catch (InstantiationException e)
278 {
279 throw new FacesException(e);
280 }
281 catch (IllegalAccessException e)
282 {
283 throw new FacesException(e);
284 }
285 catch (ClassNotFoundException e)
286 {
287 throw new FacesException(e);
288 }
289 }
290
291 protected void saveBeans(Entry entry)
292 {
293 if (requestBeanMap != null)
294 {
295 entry.beanMap.putAll(requestBeanMap);
296 }
297 }
298
299 protected void saveBean(Entry entry, String name, Object value)
300 {
301 entry.beanMap.put(name, value);
302 }
303
304
305
306
307 public void addSaveStateBean(String expressionString, Object value)
308 {
309 if (log.isDebugEnabled())
310 {
311 log.debug("addSaveStateBean: " + expressionString + " value=" + value);
312 }
313
314 getRequestBeanMap().put(expressionString, value);
315 }
316
317 protected Map getRequestBeanMap()
318 {
319 if (requestBeanMap == null)
320 {
321 requestBeanMap = new TreeMap();
322 }
323
324 return requestBeanMap;
325 }
326
327
328
329
330 public void clearSaveStateBean()
331 {
332 if (requestBeanMap != null)
333 {
334 requestBeanMap.clear();
335 }
336 }
337
338 protected void saveMessages(FacesContext facesContext, Entry entry)
339 {
340 Iterator iterClientIds = facesContext.getClientIdsWithMessages();
341 while (iterClientIds.hasNext())
342 {
343 String clientId = (String) iterClientIds.next();
344 Iterator iterMessages = facesContext.getMessages(clientId);
345 while (iterMessages.hasNext())
346 {
347 Object message = iterMessages.next();
348
349 if (log.isDebugEnabled())
350 {
351 log.debug("saveMessage: " + message);
352 }
353
354 entry.addMessage(clientId, message);
355 }
356 }
357 }
358
359 protected void restoreMessages(FacesContext facesContext, Entry entry)
360 {
361 Iterator iterMessages = entry.getMessages();
362 if (iterMessages == null)
363 {
364 return;
365 }
366
367 while (iterMessages.hasNext())
368 {
369 MessageEntry message = (MessageEntry) iterMessages.next();
370 facesContext.addMessage((String) message.clientId, (FacesMessage) message.message);
371 }
372 }
373
374 protected void saveLocale(FacesContext facesContext, Entry entry)
375 {
376 if (facesContext.getViewRoot() != null && facesContext.getViewRoot().getLocale() != null)
377 {
378 if (log.isDebugEnabled())
379 {
380 log.debug("saveLocale: " + facesContext.getViewRoot().getLocale());
381 }
382
383 entry.locale = facesContext.getViewRoot().getLocale();
384 }
385 }
386
387 protected void restoreLocale(FacesContext facesContext, Entry entry)
388 {
389 if (entry.locale != null && facesContext.getViewRoot() != null)
390 {
391 facesContext.getViewRoot().setLocale(entry.locale);
392 }
393 }
394
395
396
397
398 protected void setupFaces(FacesContext facesContext, Object rtid)
399 {
400 Entry entry;
401 initRedirectEntryMap();
402 synchronized (redirectEntryMap)
403 {
404 entry = (Entry) redirectEntryMap.get(rtid);
405 }
406
407 if (entry == null)
408 {
409
410 return;
411 }
412
413 restoreLocale(facesContext, entry);
414 restoreMessages(facesContext, entry);
415 restoreBeans(facesContext, entry);
416 }
417
418 protected void restoreBeans(FacesContext facesContext, Entry entry)
419 {
420 Iterator iterBeanMap = entry.beanMap.entrySet().iterator();
421 while (iterBeanMap.hasNext())
422 {
423 Map.Entry bean = (Map.Entry) iterBeanMap.next();
424
425 String beanName = bean.getKey().toString();
426
427 if (log.isDebugEnabled())
428 {
429 log.debug("restore bean: " + beanName + " value=" + bean.getValue());
430 }
431
432 if (beanName.startsWith("#{") && beanName.endsWith("}"))
433 {
434 ValueBinding vb = facesContext.getApplication().createValueBinding(bean.getKey().toString());
435 vb.setValue(facesContext, bean.getValue());
436 }
437 else
438 {
439 facesContext.getExternalContext().getRequestMap().put(beanName, bean.getValue());
440
441
442 addSaveStateBean(beanName, bean.getValue());
443 }
444 }
445 }
446
447
448
449
450 protected synchronized long getNextRequestNo()
451 {
452 requests++;
453 return requests;
454 }
455 }