001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2005 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: JSFUtil.java,v 1.13 2007/03/27 21:59:44 mlipp Exp $
021: *
022: * $Log: JSFUtil.java,v $
023: * Revision 1.13 2007/03/27 21:59:44 mlipp
024: * Fixed lots of checkstyle warnings.
025: *
026: * Revision 1.12 2006/12/12 13:19:08 drmlipp
027: * Fixed class casting problem in Ajax calls.
028: *
029: * Revision 1.11 2006/12/04 14:16:35 drmlipp
030: * Fixed handler registration.
031: *
032: * Revision 1.10 2006/11/24 08:36:54 drmlipp
033: * Improved listener maintenance.
034: *
035: * Revision 1.9 2006/11/23 14:54:31 drmlipp
036: * Added advanced listener handling.
037: *
038: * Revision 1.8 2006/09/29 12:32:11 drmlipp
039: * Consistently using WfMOpen as projct name now.
040: *
041: * Revision 1.7 2006/09/08 11:20:40 drmlipp
042: * Improved error messages.
043: *
044: * Revision 1.6 2005/11/10 20:52:55 mlipp
045: * Fixed problem with classloader dependend Lifecycle access.
046: *
047: * Revision 1.5 2005/10/21 15:05:51 drmlipp
048: * Continued audit event display and cleaned up some things.
049: *
050: * Revision 1.4 2005/10/20 09:54:34 drmlipp
051: * Improved process selection
052: *
053: * Revision 1.3 2005/10/18 15:28:42 drmlipp
054: * Added method.
055: *
056: * Revision 1.2 2005/09/16 13:50:03 drmlipp
057: * Improved.
058: *
059: * Revision 1.1 2005/06/20 15:08:26 drmlipp
060: * Started new JSF utility library.
061: *
062: */
063: package de.danet.an.util.jsf;
064:
065: import java.text.MessageFormat;
066: import java.util.HashMap;
067: import java.util.Iterator;
068: import java.util.Locale;
069: import java.util.Map;
070: import java.util.MissingResourceException;
071: import java.util.ResourceBundle;
072: import java.util.Set;
073: import java.util.WeakHashMap;
074:
075: import javax.faces.FactoryFinder;
076: import javax.faces.application.FacesMessage;
077: import javax.faces.context.ExternalContext;
078: import javax.faces.context.FacesContext;
079: import javax.faces.event.PhaseEvent;
080: import javax.faces.event.PhaseId;
081: import javax.faces.event.PhaseListener;
082: import javax.faces.lifecycle.Lifecycle;
083: import javax.faces.lifecycle.LifecycleFactory;
084: import javax.portlet.PortletSession;
085:
086: import org.apache.commons.collections.map.ReferenceMap;
087: import org.apache.commons.collections.set.MapBackedSet;
088:
089: /**
090: * This class provides some utility method for use with Java Server
091: * Faces.
092: *
093: * @author <a href="mailto:lipp@danet.de">Michael Lipp</a>
094: * @version $Revision: 1.13 $
095: */
096:
097: public class JSFUtil {
098:
099: /** Attribute name for accessing lifecycle as request attribue. */
100: public static final String LIFECYCLE = JSFUtil.class.getName()
101: + ".LIFECYCLE";
102:
103: /**
104: * Return the active locale.
105: * @return the active locale
106: */
107: public static Locale activeLocale() {
108: return FacesContext.getCurrentInstance().getViewRoot()
109: .getLocale();
110: }
111:
112: /**
113: * Return a resource bundle for the given base and the given locale.
114: * @param baseName the base name of the resource bundle
115: * @param locale the locale
116: * @return the resource bundle
117: * @see #activeLocale
118: */
119: public static ResourceBundle resourceBundle(String baseName,
120: Locale locale) {
121: return ResourceBundle.getBundle(baseName, locale, Thread
122: .currentThread().getContextClassLoader());
123: }
124:
125: /**
126: * Return the resource from the given resource bundle with the given key
127: * using the given locale.
128: * @param baseName the base name of the resource bundle
129: * @param locale the locale
130: * @param key the key of the resource
131: * @return the resource
132: * @see #resourceBundle
133: */
134: public static String stringResource(String baseName, Locale locale,
135: String key) {
136: return resourceBundle(baseName, locale).getString(key);
137: }
138:
139: /**
140: * Return the resource from the given resource bundle with the given key
141: * using the active locale.
142: * @param baseName the base name of the resource bundle
143: * @param key the key of the resource
144: * @return the resource
145: * @see #resourceBundle
146: */
147: public static String stringResource(String baseName, String key) {
148: return resourceBundle(baseName, activeLocale()).getString(key);
149: }
150:
151: /**
152: * Add a message to the current faces context.
153: * @param severity the message severity
154: * @param baseName the base name of the resource bundle with messages
155: * @param key the key of the message
156: * @param args arguments for message formatting
157: */
158: public static void addMessage(FacesMessage.Severity severity,
159: String baseName, String key, Object[] args) {
160: String res = MessageFormat.format(stringResource(baseName,
161: activeLocale(), key), args);
162: FacesMessage msg = new FacesMessage(severity, res, res);
163: FacesContext.getCurrentInstance().addMessage(null, msg);
164: }
165:
166: /**
167: * Add a message to the current faces context.
168: * @param severity the message severity
169: * @param baseName the base name of the resource bundle with messages
170: * @param key the key of the message
171: * @param args arguments for message formatting
172: * @param detail detailed information. This string is not localized.
173: */
174: public static void addMessage(FacesMessage.Severity severity,
175: String baseName, String key, Object[] args, String detail) {
176: String res = MessageFormat.format(stringResource(baseName,
177: activeLocale(), key), args);
178: FacesMessage msg = new FacesMessage(severity, res, detail);
179: FacesContext.getCurrentInstance().addMessage(null, msg);
180: }
181:
182: /**
183: * Add a message to the current faces context.
184: * @param severity the message severity
185: * @param the locale to use
186: * @param baseName the base name of the resource bundle with messages
187: * @param key the key of the message
188: * @param args arguments for message formatting
189: * @param thrown a throwable providing the message details
190: */
191: public static void addMessage(FacesMessage.Severity severity,
192: Locale locale, String baseName, String key, Object[] args,
193: Throwable thrown) {
194: String res = MessageFormat.format(stringResource(baseName,
195: locale, key), args);
196: String tm = null;
197: synchronized (Locale.class) {
198: Locale cur = Locale.getDefault();
199: Locale.setDefault(locale);
200: tm = thrown.getLocalizedMessage();
201: Locale.setDefault(cur);
202: }
203: tm = tm.trim();
204: tm = tm.substring(0, 1).toUpperCase(locale) + tm.substring(1);
205: FacesMessage msg = new FacesMessage(severity, res, res + " ("
206: + tm + ")");
207: FacesContext.getCurrentInstance().addMessage(null, msg);
208: }
209:
210: /**
211: * Add a message to the current faces context.
212: * @param severity the message severity
213: * @param baseName the base name of the resource bundle with messages
214: * @param key the key of the message
215: * @param args arguments for message formatting
216: * @param thrown a throwable providing the message details
217: */
218: public static void addMessage(FacesMessage.Severity severity,
219: String baseName, String key, Object[] args, Throwable thrown) {
220: addMessage(severity, activeLocale(), baseName, key, args,
221: thrown);
222: }
223:
224: /**
225: * Get the application message with the given key for the active locale.
226: * @return the message or <code>null</code> if not found
227: */
228: public static String getApplicationMessage(String key) {
229: String applMsgClass = FacesContext.getCurrentInstance()
230: .getApplication().getMessageBundle();
231: if (applMsgClass != null) {
232: try {
233: ResourceBundle bundle = loadResourceBundle(applMsgClass);
234: return bundle.getString(key);
235: } catch (MissingResourceException e) {
236: // fall though
237: }
238: }
239: try {
240: ResourceBundle bundle = loadResourceBundle("javax.faces.Messages");
241: return bundle.getString(key);
242: } catch (MissingResourceException e) {
243: // fall though
244: }
245: return null;
246: }
247:
248: private static ResourceBundle loadResourceBundle(String baseName) {
249: Locale locale = activeLocale();
250: try {
251: //First we try the JSF implementation class loader
252: return ResourceBundle.getBundle(baseName, locale,
253: FacesContext.getCurrentInstance().getClass()
254: .getClassLoader());
255: } catch (MissingResourceException ignore1) {
256: // fall through
257: }
258: try {
259: //Next we try the JSF API class loader
260: return ResourceBundle.getBundle(baseName, locale,
261: FacesContext.class.getClassLoader());
262: } catch (MissingResourceException ignore2) {
263: // fall through
264: }
265: //Last resort is the context class loader
266: return ResourceBundle.getBundle(baseName, locale, Thread
267: .currentThread().getContextClassLoader());
268: }
269:
270: /**
271: * Return the <code>Lifecycle</code>.
272: * @return the result
273: */
274: public static Lifecycle lifecycle() {
275: Lifecycle res = (Lifecycle) FacesContext.getCurrentInstance()
276: .getExternalContext().getRequestMap().get(LIFECYCLE);
277: if (res != null) {
278: return res;
279: }
280: LifecycleFactory factory = (LifecycleFactory) FactoryFinder
281: .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
282: return factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
283: }
284:
285: private static final String LISTENER_REGISTERED = JSFUtil.class
286: .getName()
287: + "_LISTENER_REGISTERED";
288: private static final String LISTENER_REGISTRY = JSFUtil.class
289: .getName()
290: + "_LISTENER_REGISTRY";
291:
292: /**
293: * Adds a phase listener that is associated with the invoking
294: * portlet instance. Normally, listeners are associated with a lifecycle
295: * with one instance for the complete application. If the application
296: * consists of several JSF based portlets, listeners are therefore invoked
297: * every time one of the portlets goes through the lifecycle.
298: * In contrary, listeners registered with the method provided here are
299: * only invoked when the registering portlet goes though its lifecycle.
300: *
301: * In addition, the listeners are maintained using weak references.
302: * Omitting the explicit remove therefore does not prevent the listeners
303: * from being garbage collected.
304: *
305: * @param pl the phase listener
306: */
307: public static void addPhaseListenerForPortlet(PhaseListener pl) {
308: PortletSession session = (PortletSession) FacesContext
309: .getCurrentInstance().getExternalContext().getSession(
310: true);
311: if (session.getAttribute(LISTENER_REGISTERED,
312: PortletSession.APPLICATION_SCOPE) == null) {
313: Lifecycle lifecycle = lifecycle();
314: lifecycle.addPhaseListener(new PhaseListener() {
315: public PhaseId getPhaseId() {
316: return PhaseId.ANY_PHASE;
317: }
318:
319: public void beforePhase(PhaseEvent e) {
320: handleEvent(false, e);
321: }
322:
323: public void afterPhase(PhaseEvent e) {
324: handleEvent(true, e);
325: }
326: });
327: session.setAttribute(LISTENER_REGISTERED, Boolean.TRUE);
328: }
329: Set listeners = (Set) session.getAttribute(LISTENER_REGISTRY);
330: if (listeners == null) {
331: listeners = MapBackedSet.decorate(new ReferenceMap(
332: ReferenceMap.WEAK, ReferenceMap.HARD, true),
333: new Object());
334: session.setAttribute(LISTENER_REGISTRY, listeners);
335: }
336: listeners.add(pl);
337: }
338:
339: private static void handleEvent(boolean after, PhaseEvent e) {
340: Object session = FacesContext.getCurrentInstance()
341: .getExternalContext().getSession(true);
342: if (!(session instanceof PortletSession)) {
343: // Calling this with ServletSession, must be Ajax call
344: return;
345: }
346: Set listeners = (Set) ((PortletSession) session)
347: .getAttribute(LISTENER_REGISTRY);
348: if (listeners == null) {
349: return;
350: }
351: for (Iterator i = listeners.iterator(); i.hasNext();) {
352: PhaseListener pl = (PhaseListener) i.next();
353: if (pl.getPhaseId() == PhaseId.ANY_PHASE
354: || pl.getPhaseId().equals(e.getPhaseId())) {
355: if (!after) {
356: pl.beforePhase(e);
357: } else {
358: pl.afterPhase(e);
359: }
360: }
361: }
362: }
363:
364: /**
365: * Removes a listener associated with the invoking portlet instance.
366: *
367: * @see #addPhaseListenerForPortlet(PhaseListener)
368: * @param pl
369: */
370: public void removePhaseListenerForPortlet(PhaseListener pl) {
371: PortletSession session = (PortletSession) FacesContext
372: .getCurrentInstance().getExternalContext().getSession(
373: true);
374: Set listeners = (Set) session.getAttribute(LISTENER_REGISTRY);
375: if (listeners == null) {
376: return;
377: }
378: listeners.remove(pl);
379: }
380: }
|