001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (c) 2004-2007 Paul Ferraro
004 * 
005 * This library is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Lesser General Public License as published by the 
007 * Free Software Foundation; either version 2.1 of the License, or (at your 
008 * option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful, but WITHOUT
011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this library; if not, write to the Free Software Foundation, 
017 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018 * 
019 * Contact: ferraro@users.sourceforge.net
020 */
021package net.sf.hajdbc.balancer;
022
023import java.util.Collections;
024import java.util.Set;
025import java.util.SortedSet;
026import java.util.TreeSet;
027import java.util.concurrent.locks.Lock;
028import java.util.concurrent.locks.ReentrantLock;
029
030import net.sf.hajdbc.Balancer;
031import net.sf.hajdbc.Database;
032
033/**
034 * Thread-safe abstract balancer implementation that implements most of the Balancer interface, except {@link Balancer#next()}.
035 * Uses A copy-on-write algorithm for {@link #add(Database)}, {@link #remove(Database)}, and {@link #clear()}.
036 * Calls to {@link #all()} are non-blocking.
037 * 
038 * @author  Paul Ferraro
039 * @param <D> either java.sql.Driver or javax.sql.DataSource
040 */
041public abstract class AbstractBalancer<D> implements Balancer<D>
042{
043        protected Lock lock = new ReentrantLock();
044
045        protected volatile SortedSet<Database<D>> databaseSet = new TreeSet<Database<D>>();
046
047        /**
048         * @see net.sf.hajdbc.Balancer#beforeInvocation(net.sf.hajdbc.Database)
049         */
050        @Override
051        public void beforeInvocation(Database<D> database)
052        {
053                // Do nothing
054        }
055        
056        /**
057         * @see net.sf.hajdbc.Balancer#afterInvocation(net.sf.hajdbc.Database)
058         */
059        @Override
060        public void afterInvocation(Database<D> database)
061        {
062                // Do nothing
063        }
064        
065        /**
066         * @see net.sf.hajdbc.Balancer#remove(net.sf.hajdbc.Database)
067         */
068        @Override
069        public boolean remove(Database<D> database)
070        {
071                this.lock.lock();
072                
073                try
074                {
075                        boolean exists = this.databaseSet.contains(database);
076                        
077                        if (exists)
078                        {
079                                SortedSet<Database<D>> set = new TreeSet<Database<D>>(this.databaseSet);
080                                
081                                set.remove(database);
082                                
083                                this.databaseSet = set;
084                                
085                                this.removed(database);
086                        }
087                        
088                        return exists;
089                }
090                finally
091                {
092                        this.lock.unlock();
093                }
094        }
095        
096        /**
097         * Called when a database was removed from the set.
098         * @param database a database descriptor
099         */
100        protected abstract void removed(Database<D> database);
101        
102        /**
103         * @see net.sf.hajdbc.Balancer#add(net.sf.hajdbc.Database)
104         */
105        @Override
106        public boolean add(Database<D> database)
107        {
108                this.lock.lock();
109                
110                try
111                {
112                        boolean exists = this.databaseSet.contains(database);
113                        
114                        if (!exists)
115                        {
116                                SortedSet<Database<D>> set = new TreeSet<Database<D>>(this.databaseSet);
117                                
118                                set.add(database);
119                                
120                                this.databaseSet = set;
121                                        
122                                this.added(database);
123                        }
124                        
125                        return !exists;
126                }
127                finally
128                {
129                        this.lock.unlock();
130                }
131        }
132        
133        /**
134         * Called when a database was added to the set.
135         * @param database a database descriptor
136         */
137        protected abstract void added(Database<D> database);
138        
139        /**
140         * @see net.sf.hajdbc.Balancer#all()
141         */
142        @Override
143        public Set<Database<D>> all()
144        {
145                return Collections.unmodifiableSet(this.databaseSet);
146        }
147
148        /**
149         * @see net.sf.hajdbc.Balancer#clear()
150         */
151        @Override
152        public void clear()
153        {
154                this.lock.lock();
155                
156                try
157                {
158                        this.databaseSet = new TreeSet<Database<D>>();
159                        
160                        this.cleared();
161                }
162                finally
163                {
164                        this.lock.unlock();
165                }
166        }
167        
168        /**
169         * Called when the set was cleared.
170         */
171        protected abstract void cleared();
172}