/* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.arsdigita.london.userprefs; import com.arsdigita.caching.CacheTable; import com.arsdigita.domain.DataObjectNotFoundException; import com.arsdigita.domain.DomainCollection; import com.arsdigita.kernel.Kernel; import com.arsdigita.kernel.User; import com.arsdigita.persistence.OID; import com.arsdigita.util.UncheckedWrapperException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.HashMap; import java.util.Iterator; import java.util.Random; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.log4j.Logger; /** *

User Preferences. An object for storing user preferences in the current * session.

* *

This object is an abstraction of PersistentUserPreferences, which is a * DomainObject stored in the database. UserPrefs is * cached in the java Session and will not touch the database on every * request.

* *

If the current user is not logged in, UserPrefs will use its * own cookie. This means that preferences can be saved even when a user doesn't * log in.

* * @author Matthew Booth */ public class UserPrefs { private static final Logger s_log = Logger.getLogger( UserPrefs.class ); private static final String COOKIE = "WAF_USER_PREFS"; static final String SESSION_ATTRIBUTE = UserPrefs.class.getName(); private OID m_user = null; private OID m_persistentPrefs = null; private Long m_cookie = null; private HashMap m_prefs = new HashMap(); private static final CacheTable s_prefsCache = new CacheTable( "user_preferences" ); private UserPrefs() {}; private UserPrefs( PersistentUserPrefs persistentPrefs ) { init( persistentPrefs ); } private void init( PersistentUserPrefs persistentPrefs ) { m_persistentPrefs = persistentPrefs.getOID(); m_cookie = persistentPrefs.getCookie(); m_user = persistentPrefs.getUser() == null ? null : persistentPrefs.getUser().getOID(); DomainCollection prefs = persistentPrefs.getAllValues(); while( prefs.next() ) { Pref pref = (Pref) prefs.getDomainObject(); m_prefs.put( pref.getKey(), pref.getValue() ); } } /** *

Retrieve a UserPrefs object for the current request.

* *

In order, this will: * *

*

*/ public static UserPrefs retrieve( HttpServletRequest req, HttpServletResponse res ) { HttpSession session = req.getSession(); // Yes, this seems like a silly use of an HttpSession, but they really // are broken in almost every useful way. UserPrefs prefs = (UserPrefs) s_prefsCache.get( session.getId() ); if( null != prefs ) { if( s_log.isDebugEnabled() ) { s_log.debug( "Retrieved user prefs for session " + session.getId() ); } return prefs; } PersistentUserPrefs persistentPrefs = retrievePersistent( req, res ); if( null != persistentPrefs ) { prefs = new UserPrefs( persistentPrefs ); } else { prefs = new UserPrefs(); User user = (User) Kernel.getContext().getParty(); if( null != user ) { prefs.m_user = user.getOID(); } else { Long cookie; try { SecureRandom random = SecureRandom.getInstance( "SHA1PRNG" ); cookie = new Long( random.nextLong() ); } catch( NoSuchAlgorithmException ex ) { s_log.warn( "Unable to get SecureRandom for SHA1PRNG. " + "Falling back to insecure random generator." ); cookie = new Long( new Random().nextLong() ); } prefs.m_cookie = cookie; setCookie( res, cookie.toString() ); } s_log.debug( "Created new prefs" ); } s_prefsCache.put( session.getId(), prefs ); return prefs; } private static PersistentUserPrefs retrievePersistent ( HttpServletRequest req, HttpServletResponse res ) { PersistentUserPrefs persistentPrefs = null; User user = (User) Kernel.getContext().getParty(); if( null != user ) { persistentPrefs = PersistentUserPrefs.retrieveForUser( user ); } if( null != persistentPrefs ) { s_log.debug( "Got prefs for user" ); return persistentPrefs; } Long cookie = getCookie( req ); if( null != cookie ) { persistentPrefs = PersistentUserPrefs.retrieveForCookie( cookie ); // Remove a bogus cookie if( null == persistentPrefs ) { removeCookie( res ); } } if( null != persistentPrefs ) { s_log.debug( "Got prefs for cookie" ); return persistentPrefs; } s_log.debug( "No existing prefs" ); return null; } /** * Retrieve the value of a user preference entry. * * @param key The identifier of the preference to be retrieved * @return The value of the requsted preference, or null if it is not set */ public String get( String key ) { return (String) m_prefs.get( key ); } /** * Retrieve all stored user preferences. * * @return An Iterator of Map.Entry objects containing key/value pairs for * the user's current preferences */ public Iterator getAll() { return m_prefs.entrySet().iterator(); } /** * Set a preference. * * @param key The identifier of the preference to be stored * @param value The value of the preference to be stored */ public void set( String key, String value, HttpServletRequest req, HttpServletResponse res ) { m_prefs.put( key, value ); PersistentUserPrefs prefs = getPersistent(); if( null == prefs ) prefs = createPersistent( req, res ); prefs.setValue( key, value ); } /** * Remove a user preference. * * @param key The identifier of the preference to be removed */ public void remove( String key, HttpServletRequest req ) { m_prefs.remove( key ); PersistentUserPrefs persistent = getPersistent(); if( !m_prefs.isEmpty() ) { if( null != persistent ) persistent.removeValue( key ); } else { if( null != persistent ) persistent.delete(); req.getSession().setAttribute( SESSION_ATTRIBUTE, null ); m_persistentPrefs = null; } } /** * Save user preferences to the database */ public void persist( HttpServletRequest req, HttpServletResponse res ) { s_log.info( "Persisting session" ); PersistentUserPrefs prefs = getPersistent(); if( null == prefs ) prefs = createPersistent( req, res ); prefs.setAllValues( m_prefs ); prefs.save(); s_log.debug( "Session persisted" ); } /** * Get a PersistentUserPrefs object. Create one if necessary. */ private PersistentUserPrefs getPersistent() { PersistentUserPrefs prefs = null; if( null != m_persistentPrefs ) { try { prefs = new PersistentUserPrefs( m_persistentPrefs ); } catch( DataObjectNotFoundException ex ) { s_log.warn( "User preferences object contained bogus " + "persistent preferences OID" ); } } // If we're saving something and we have a user object now, use that // in preference if( null == m_user ) { User user = (User) Kernel.getContext().getParty(); if( null != user ) { prefs.setUser( user ); prefs.setCookie( null ); } } return prefs; } private PersistentUserPrefs createPersistent( HttpServletRequest req, HttpServletResponse res ) { PersistentUserPrefs prefs = retrievePersistent( req, res ); if( null == prefs ) { prefs = new PersistentUserPrefs(); if( s_log.isDebugEnabled() ) { s_log.debug( "Initializing new user preferences: " + prefs.getOID() ); } if( null != m_user ) { prefs.setUser( new User( m_user ) ); } else if( null != m_cookie ) { prefs.setCookie( m_cookie ); } else { throw new UncheckedWrapperException ( "User preferences object doesn't contain either a user or a cookie object" ); } m_persistentPrefs = prefs.getOID(); } else { if( s_log.isDebugEnabled() ) { s_log.debug( "Reusing existing persistent preferences " + prefs.getOID() ); } init( prefs ); } return prefs; } private static Long getCookie( HttpServletRequest req ) { Cookie[] cookieJar = req.getCookies(); if( null == cookieJar ) return null; for( int i = 0; i < cookieJar.length; i++ ) { if( COOKIE.equals( cookieJar[i].getName() ) ) { try { return Long.valueOf( cookieJar[i].getValue() ); } catch( NumberFormatException ex ) { s_log.warn( "Bogus cookie value: " + cookieJar[i].getValue() ); // Might as well keep looking } } } return null; } private static void setCookie( HttpServletResponse res, String value ) { Cookie cookie = new Cookie( COOKIE, value ); cookie.setMaxAge( Integer.MAX_VALUE ); res.addCookie( cookie ); } private static void removeCookie( HttpServletResponse res ) { Cookie cookie = new Cookie( COOKIE, "" ); cookie.setMaxAge( 0 ); res.addCookie( cookie ); } }