001    package org.apache.ojb.broker.core;
002    
003    /* Copyright 2003-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.Collection;
019    import java.util.HashMap;
020    import java.util.HashSet;
021    import java.util.Iterator;
022    import java.util.WeakHashMap;
023    
024    import org.apache.ojb.broker.PBFactoryException;
025    import org.apache.ojb.broker.PBKey;
026    import org.apache.ojb.broker.PersistenceBrokerException;
027    import org.apache.ojb.broker.PersistenceBrokerInternal;
028    
029    /**
030     * Helper class that tracks correspondence between PersistenceBroker instances
031     * and threads. The main task that this class solves is: to get current
032     * PersistenceBroker for the given thread. For internal use only.
033     *
034     * @author Oleg Nitz
035     * @version $Id: PersistenceBrokerThreadMapping.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
036     */
037    public class PersistenceBrokerThreadMapping
038    {
039        /**
040         * A Collection of all HashMaps added to the <CODE>ThreadLocal currentBrokerMap</CODE> that are still -alive-.
041         * If all the PersistenceBrokers are always correctly closed, the Collection should remain empty.
042         * The Collection is iterated trough when calling the <CODE>shutdown()</CODE> method and all the maps that are
043         * still alive will be cleared.
044         */
045        private static Collection loadedHMs = new HashSet();
046    
047        /**
048         * The hashmap that maps PBKeys to current brokers for the thread
049         */
050        private static ThreadLocal currentBrokerMap = new ThreadLocal();
051    
052        /**
053         * Mark a PersistenceBroker as preferred choice for current Thread
054         *
055         * @param key    The PBKey the broker is associated to
056         * @param broker The PersistenceBroker to mark as current
057         */
058        public static void setCurrentPersistenceBroker(PBKey key, PersistenceBrokerInternal broker)
059                throws PBFactoryException
060        {
061            HashMap map = (HashMap) currentBrokerMap.get();
062            WeakHashMap set = null;
063            if(map == null)
064            {
065                map = new HashMap();
066                currentBrokerMap.set(map);
067    
068                loadedHMs.add(map);
069            }
070            else
071            {
072                set = (WeakHashMap) map.get(key);
073            }
074    
075            if(set == null)
076            {
077                // We emulate weak HashSet using WeakHashMap
078                set = new WeakHashMap();
079                map.put(key, set);
080            }
081            set.put(broker, null);
082        }
083    
084        /**
085         * Unmark a PersistenceBroker as preferred choice for current Thread
086         *
087         * @param key    The PBKey the broker is associated to
088         * @param broker The PersistenceBroker to unmark
089         */
090        public static void unsetCurrentPersistenceBroker(PBKey key, PersistenceBrokerInternal broker)
091                throws PBFactoryException
092        {
093            HashMap map = (HashMap) currentBrokerMap.get();
094            WeakHashMap set = null;
095            if(map != null)
096            {
097                set = (WeakHashMap) map.get(key);
098                if(set != null)
099                {
100                    set.remove(broker);
101                    if(set.isEmpty())
102                    {
103                        map.remove(key);
104                    }
105                }
106                if(map.isEmpty())
107                {
108                    currentBrokerMap.set(null);
109                    loadedHMs.remove(map);
110                }
111            }
112        }
113    
114        /**
115         * Return the current open {@link org.apache.ojb.broker.PersistenceBroker}
116         * instance for the given {@link org.apache.ojb.broker.PBKey}, if any.
117         *
118         * @param key
119         * @return null if no open {@link org.apache.ojb.broker.PersistenceBroker} found.
120         */
121        public static PersistenceBrokerInternal currentPersistenceBroker(PBKey key)
122                throws PBFactoryException, PersistenceBrokerException
123        {
124            HashMap map = (HashMap) currentBrokerMap.get();
125            WeakHashMap set;
126            PersistenceBrokerInternal broker = null;
127    
128            if(map == null)
129            {
130                return null;
131            }
132    
133            set = (WeakHashMap) map.get(key);
134            if(set == null)
135            {
136                return null;
137            }
138    
139            // seek for an open broker, preferably in transaction
140            for(Iterator it = set.keySet().iterator(); it.hasNext();)
141            {
142                PersistenceBrokerInternal tmp = (PersistenceBrokerInternal) it.next();
143                if(tmp == null || tmp.isClosed())
144                {
145                    it.remove();
146                    continue;
147                }
148                broker = tmp;
149                if(tmp.isInTransaction())
150                {
151                    break; // the best choice found
152                }
153            }
154            return broker;
155        }
156    
157        /**
158         * Clean up static fields and any registered ThreadLocal contents to grant a clean
159         * shutdown/reload of OJB within re/hot-deployable applications.
160         */
161        public static void shutdown()
162        {
163            for(Iterator it = loadedHMs.iterator(); it.hasNext();)
164            {
165                ((HashMap) it.next()).clear();
166            }
167            loadedHMs.clear();
168            loadedHMs = null;
169            currentBrokerMap = null;
170        }
171    }