001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2005 Danet GmbH (www.danet.de), BU BTS.
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: PortletEnvironment.java,v 1.16 2007/05/26 21:54:29 mlipp Exp $
021: *
022: * $Log: PortletEnvironment.java,v $
023: * Revision 1.16 2007/05/26 21:54:29 mlipp
024: * Added liferay support for timezones.
025: *
026: * Revision 1.15 2007/05/25 16:45:04 drmlipp
027: * Improved comment.
028: *
029: * Revision 1.14 2006/10/22 20:12:48 mlipp
030: * Fixed problems with empty preferences.
031: *
032: * Revision 1.13 2006/09/29 12:32:11 drmlipp
033: * Consistently using WfMOpen as projct name now.
034: *
035: * Revision 1.12 2006/09/14 07:41:23 drmlipp
036: * Removed old code.
037: *
038: * Revision 1.11 2006/09/13 21:53:31 mlipp
039: * Improved preferences handling.
040: *
041: * Revision 1.8 2006/09/13 11:01:34 drmlipp
042: * Fixed caching scope.
043: *
044: * Revision 1.7 2006/09/12 15:55:28 drmlipp
045: * Added canonical time zone retrieval.
046: *
047: * Revision 1.6 2005/10/14 08:57:08 drmlipp
048: * Fixed.
049: *
050: * Revision 1.5 2005/10/13 21:53:07 mlipp
051: * Simplified by using commons-collections.
052: *
053: * Revision 1.4 2005/09/19 21:14:41 mlipp
054: * Added boolean access to preferences.
055: *
056: * Revision 1.3 2005/09/09 13:31:03 drmlipp
057: * Added possibility to access preferences as Integer.
058: *
059: * Revision 1.2 2005/09/09 10:47:47 drmlipp
060: * Imroved.
061: *
062: * Revision 1.1 2005/09/08 15:13:00 drmlipp
063: * New approach to store() problem.
064: *
065: */
066: package de.danet.an.util.jsf;
067:
068: import java.io.IOException;
069: import java.lang.reflect.InvocationTargetException;
070: import java.lang.reflect.Method;
071: import java.util.HashMap;
072: import java.util.Iterator;
073: import java.util.Map;
074: import java.util.Set;
075: import java.util.TimeZone;
076:
077: import javax.faces.FactoryFinder;
078: import javax.faces.context.ExternalContext;
079: import javax.faces.context.FacesContext;
080: import javax.faces.event.PhaseEvent;
081: import javax.faces.event.PhaseId;
082: import javax.faces.event.PhaseListener;
083: import javax.faces.lifecycle.Lifecycle;
084: import javax.faces.lifecycle.LifecycleFactory;
085: import javax.portlet.PortletPreferences;
086: import javax.portlet.PortletRequest;
087: import javax.portlet.PortletSession;
088: import javax.portlet.ReadOnlyException;
089: import javax.portlet.ValidatorException;
090:
091: import org.apache.commons.collections.map.AbstractMapDecorator;
092:
093: import de.danet.an.util.CollectionsUtil;
094:
095: /**
096: * A bean that provides access to the portlet environment from a JSF page.
097: */
098: public class PortletEnvironment implements PhaseListener {
099:
100: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
101: .getLog(PortletEnvironment.class);
102:
103: private static final String PREFS_CACHE = PortletEnvironment.class
104: .getName()
105: + "_PREFS_CACHE";
106:
107: private Lifecycle lifecycle = null;
108:
109: /**
110: * Return the portlet preferences from the current request as map.
111: * If a preference consists of multiple values, only the first
112: * will be returned.
113: *
114: * @return the preferences.
115: */
116: public Map portletPreferences() {
117: ExternalContext extCtx = FacesContext.getCurrentInstance()
118: .getExternalContext();
119: Map prefsCache = (Map) extCtx.getSessionMap().get(PREFS_CACHE);
120: if (prefsCache == null) {
121: Object req = extCtx.getRequest();
122: if (req instanceof PortletRequest) {
123: // Get a map of preferences where
124: // the entries hold only the first string from the
125: // array of preferences values.
126: Map prefsMap = new HashMap();
127: for (Iterator i = ((PortletRequest) req)
128: .getPreferences().getMap().entrySet()
129: .iterator(); i.hasNext();) {
130: Map.Entry entry = (Map.Entry) i.next();
131: String[] entryVal = (String[]) entry.getValue();
132: if (entryVal != null && entryVal.length > 0) {
133: prefsMap.put(entry.getKey(), entryVal[0]);
134: }
135: }
136: // Track the map to allow update later.
137: prefsCache = CollectionsUtil.trackedMap(prefsMap);
138: } else {
139: prefsCache = new HashMap();
140: }
141: extCtx.getSessionMap().put(PREFS_CACHE, prefsCache);
142: }
143: return prefsCache;
144: }
145:
146: /**
147: * Return the preferences defined for the portlet.
148: * @return the preferences
149: */
150: public Map getPreferences() {
151: registerForCleanup();
152: return portletPreferences();
153: }
154:
155: /**
156: * Return the time zone for this user. The value is derived from
157: * the "user.timezone" preference. If this is not set, the system default
158: * is returned.
159: *
160: * @return result
161: */
162: public TimeZone getTimeZone() {
163: String timeZoneId = (String) getPreferences().get(
164: "user.timezone");
165: if (timeZoneId != null) {
166: TimeZone found = TimeZone.getTimeZone(timeZoneId);
167: if (found.getID().equals(timeZoneId)) {
168: return found;
169: }
170: logger.warn("Time zone with id=\"" + timeZoneId
171: + "\" not found");
172: }
173: try {
174: Class usuc = Thread
175: .currentThread()
176: .getContextClassLoader()
177: .loadClass(
178: "com.liferay.portal.service.UserServiceUtil");
179: Method gum = usuc.getMethod("getUserById",
180: new Class[] { String.class });
181: ExternalContext extCtx = FacesContext.getCurrentInstance()
182: .getExternalContext();
183: String userId = (String) ((PortletRequest) extCtx
184: .getRequest()).getPortletSession().getAttribute(
185: "USER_ID", PortletSession.APPLICATION_SCOPE);
186: if (userId != null) {
187: Object user = gum.invoke(null, new Object[] { userId });
188: Method gtz = user.getClass().getMethod("getTimeZone",
189: null);
190: TimeZone tz = (TimeZone) gtz.invoke(user, null);
191: return tz;
192: }
193: } catch (ClassNotFoundException e1) {
194: // Was just a try anyway...
195: } catch (SecurityException e) {
196: // Was just a try anyway...
197: } catch (NoSuchMethodException e) {
198: // Was just a try anyway...
199: } catch (IllegalArgumentException e) {
200: // Was just a try anyway...
201: } catch (IllegalAccessException e) {
202: // Was just a try anyway...
203: } catch (InvocationTargetException e) {
204: // Was just a try anyway...
205: }
206:
207: return TimeZone.getDefault();
208: }
209:
210: public static class IntegerWrapper extends AbstractMapDecorator {
211:
212: /**
213: * @param delegee
214: */
215: public IntegerWrapper(Map delegee) {
216: super (delegee);
217: }
218:
219: /* (non-Javadoc)
220: * @see java.util.Map#get(java.lang.Object)
221: */
222: public Object get(Object key) {
223: return Integer.decode((String) map.get(key));
224: }
225:
226: /* (non-Javadoc)
227: * @see org.apache.commons.collections.map.AbstractMapDecorator#put(java.lang.Object, java.lang.Object)
228: */
229: public Object put(Object key, Object value) {
230: // TODO Auto-generated method stub
231: return super .put(key, value.toString());
232: }
233: }
234:
235: /**
236: * Return the preferences with an Integer wrapper. Accessing
237: * a preference will return an Integer if the string is convertible.
238: * @return the wrapped preferences
239: */
240: public Map getPreferencesAsIntegers() {
241: return new IntegerWrapper(getPreferences());
242: }
243:
244: public static class BooleanWrapper extends AbstractMapDecorator {
245:
246: /**
247: * @param delegee
248: */
249: public BooleanWrapper(Map delegee) {
250: super (delegee);
251: }
252:
253: /* (non-Javadoc)
254: * @see java.util.Map#get(java.lang.Object)
255: */
256: public Object get(Object key) {
257: return Boolean.valueOf((String) map.get(key));
258: }
259:
260: /* (non-Javadoc)
261: * @see org.apache.commons.collections.map.AbstractMapDecorator#put(java.lang.Object, java.lang.Object)
262: */
263: public Object put(Object key, Object value) {
264: // TODO Auto-generated method stub
265: return super .put(key, value.toString());
266: }
267: }
268:
269: /**
270: * Return the preferences with a Boolean wrapper. Accessing
271: * a preference will return an Integer if the string is convertible.
272: * @return the wrapped preferences
273: */
274: public Map getPreferencesAsBooleans() {
275: return new BooleanWrapper(getPreferences());
276: }
277:
278: // Implementation of PhaseListener issues
279:
280: /**
281: * Register for cleanup.
282: */
283: protected void registerForCleanup() {
284: if (lifecycle != null) {
285: return;
286: }
287: LifecycleFactory factory = (LifecycleFactory) FactoryFinder
288: .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
289: lifecycle = factory
290: .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
291: lifecycle.addPhaseListener(this );
292: }
293:
294: /* (non-Javadoc)
295: * @see javax.faces.event.PhaseListener#getPhaseId()
296: */
297: public PhaseId getPhaseId() {
298: return PhaseId.UPDATE_MODEL_VALUES;
299: }
300:
301: /* (non-Javadoc)
302: * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
303: */
304: public void afterPhase(PhaseEvent event) {
305: try {
306: ExternalContext extCtx = FacesContext.getCurrentInstance()
307: .getExternalContext();
308: Map prefsCache = (Map) extCtx.getSessionMap().get(
309: PREFS_CACHE);
310: Object req = extCtx.getRequest();
311: if (prefsCache != null && req instanceof PortletRequest) {
312: PortletPreferences prefs = ((PortletRequest) req)
313: .getPreferences();
314: try {
315: Set mods = CollectionsUtil
316: .modifiedEntries(prefsCache);
317: for (Iterator i = mods.iterator(); i.hasNext();) {
318: String mod = (String) i.next();
319: if (!prefsCache.containsKey(mod)) {
320: prefs.reset(mod);
321: } else {
322: prefs.setValue(mod, (String) prefsCache
323: .get(mod));
324: }
325: }
326: } catch (ReadOnlyException e) {
327: logger.warn("Cannot set read-only preference: "
328: + e.getMessage(), e);
329: }
330: prefs.store();
331: }
332: } catch (ValidatorException e) {
333: logger.error("Cannot store preferences: " + e.getMessage(),
334: e);
335: } catch (IOException e) {
336: logger.error("Cannot store preferences: " + e.getMessage(),
337: e);
338: }
339: // De-register
340: lifecycle.removePhaseListener(this );
341: lifecycle = null;
342: }
343:
344: /* (non-Javadoc)
345: * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
346: */
347: public void beforePhase(PhaseEvent event) {
348: }
349:
350: }
|