001    /*
002     * 
003     * $Revision: 14909 $ $Date: 2009-03-16 18:06:26 +0100 (Mon, 16 Mar 2009) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.common;
025    
026    import static org.mycore.common.events.MCRSessionEvent.Type.created;
027    import static org.mycore.common.events.MCRSessionEvent.Type.destroyed;
028    
029    import java.util.ArrayList;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.concurrent.locks.ReentrantReadWriteLock;
034    
035    import org.mycore.common.events.MCRSessionListener;
036    
037    /**
038     * Manages sessions for a MyCoRe system. This class is backed by a ThreadLocal
039     * variable, so every Thread is guaranteed to get a unique instance of
040     * MCRSession. Care must be taken when using an environment utilizing a Thread
041     * pool, such as many Servlet engines. In this case it is possible for the
042     * session object to stay attached to a thread where it should not be. Use the
043     * {@link #releaseCurrentSession()}method to reset the session object for a
044     * Thread to its default values.
045     * 
046     * The basic idea for the implementation of this class is taken from an apache
047     * project, namely the class org.apache.common.latka.LatkaProperties.java
048     * written by Morgan Delagrange. Please see <http://www.apache.org/>.
049     * 
050     * @author Detlev Degenhardt
051     * @author Thomas Scheffler (yagee)
052     * @version $Revision: 14909 $ $Date: 2009-03-16 18:06:26 +0100 (Mon, 16 Mar 2009) $
053     */
054    public class MCRSessionMgr {
055    
056        private static Map<String, MCRSession> sessions = new HashMap<String, MCRSession>();
057    
058        private static List<MCRSessionListener> listeners = new ArrayList<MCRSessionListener>();
059    
060        private static ReentrantReadWriteLock listenersLock = new ReentrantReadWriteLock();
061    
062        /**
063         * This ThreadLocal is automatically instantiated per thread with a MyCoRe
064         * session object containing the default session parameters which are set in
065         * the constructor of MCRSession.
066         * 
067         * @see ThreadLocal
068         */
069        private static ThreadLocal<MCRSession> theThreadLocalSession = new ThreadLocal<MCRSession>() {
070            public MCRSession initialValue() {
071                return new MCRSession();
072            }
073        };
074    
075        /**
076         * This method returns the unique MyCoRe session object for the current
077         * Thread. The session object is initialized with the default MyCoRe session
078         * data.
079         * 
080         * @return MyCoRe MCRSession object
081         */
082        public static MCRSession getCurrentSession() {
083            return theThreadLocalSession.get();
084        }
085    
086        /**
087         * This method sets a MyCoRe session object for the current Thread. This
088         * method fires a "activated" event, when called the first time for this
089         * session and thread.
090         * 
091         * @see org.mycore.common.events.MCRSessionEvent.Type#activated
092         */
093        public static void setCurrentSession(MCRSession theSession) {
094            theSession.activate();
095            theThreadLocalSession.set(theSession);
096        }
097    
098        /**
099         * Releases the MyCoRe session from its current thread. Subsequent calls of
100         * getCurrentSession() will return a different MCRSession object than before
101         * for the current Thread. One use for this method is to reset the session
102         * inside a Thread-pooling environment like Servlet engines. This method
103         * fires a "passivated" event, when called the last time for this session
104         * and thread.
105         * 
106         * @see org.mycore.common.events.MCRSessionEvent.Type#passivated
107         */
108        public static void releaseCurrentSession() {
109            MCRSession session = theThreadLocalSession.get();
110            session.passivate();
111            MCRSession.LOGGER.debug("MCRSession released " + session.getID());
112            theThreadLocalSession.remove();
113        }
114    
115        /**
116         * Returns the MCRSession for the given sessionID.
117         */
118        public static MCRSession getSession(String sessionID) {
119            MCRSession s = sessions.get(sessionID);
120            if (s == null) {
121                MCRSession.LOGGER.warn("MCRSession with ID " + sessionID + " not cached any more");
122            }
123            return s;
124        }
125    
126        /**
127         * Add MCRSession to a static Map that manages all sessions. This method
128         * fires a "created" event and is invoked by MCRSession constructor.
129         * 
130         * @see MCRSession#MCRSession()
131         * @see org.mycore.common.events.MCRSessionEvent.Type#created
132         */
133        static void addSession(MCRSession session) {
134            sessions.put(session.getID(), session);
135            session.fireSessionEvent(created, session.concurrentAccess.get());
136        }
137    
138        /**
139         * Remove MCRSession from a static Map that manages all sessions. This
140         * method fires a "destroyed" event and is invoked by MCRSession.close().
141         * 
142         * @see MCRSession#close()
143         * @see org.mycore.common.events.MCRSessionEvent.Type#destroyed
144         */
145        static void removeSession(MCRSession session) {
146            sessions.remove(session.getID());
147            session.fireSessionEvent(destroyed, session.concurrentAccess.get());
148        }
149    
150        /**
151         * Add a MCRSessionListener, that gets infomed about MCRSessionEvents.
152         * 
153         * @see #removeSessionListener(MCRSessionListener)
154         */
155        public static void addSessionListener(MCRSessionListener listener) {
156            listenersLock.writeLock().lock();
157            listeners.add(listener);
158            listenersLock.writeLock().unlock();
159        }
160    
161        /**
162         * Removes a MCRSessionListener from the list.
163         * 
164         * @see #addSessionListener(MCRSessionListener)
165         */
166        public static void removeSessionListener(MCRSessionListener listener) {
167            listenersLock.writeLock().lock();
168            listeners.remove(listener);
169            listenersLock.writeLock().unlock();
170        }
171    
172        /**
173         * Allows access to all MCRSessionListener instances.
174         * 
175         * Mainly for internal use of MCRSession.
176         */
177        static List<MCRSessionListener> getListeners() {
178            return listeners;
179        }
180    
181        /**
182         * Allows to lock out access to list of MCESessionListener instances.
183         * 
184         * When you want to read on the list, use the readLock() and use the
185         * writeLock() if you want to modify the list. Using locks will allow a high
186         * degree of concurrent access.
187         * 
188         * Mainly for internal use of MCRSession.
189         * 
190         * @see ReentrantReadWriteLock#readLock();
191         * @see ReentrantReadWriteLock#writeLock();
192         */
193        static ReentrantReadWriteLock getListenersLock() {
194            return listenersLock;
195        }
196    
197        public static void close() {
198            listenersLock.writeLock().lock();
199            for (MCRSession session : sessions.values().toArray(new MCRSession[0])) {
200                session.close();
201            }
202            listenersLock.writeLock().unlock();
203        }
204    
205        public static Map<String, MCRSession> getAllSessions() {
206            return sessions;
207        }
208    
209    }