001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2012  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.balancer.random;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.Random;
024import java.util.Set;
025
026import net.sf.hajdbc.Database;
027import net.sf.hajdbc.balancer.AbstractSetBalancer;
028
029/**
030 * Balancer implementation whose {@link #next()} implementation returns a random database.
031 * The probability that a given database will be returned is: <em>weight / total-weight</em>.
032 * 
033 * @author  Paul Ferraro
034 * @param <D> either java.sql.Driver or javax.sql.DataSource
035 */
036public class RandomBalancer<P, D extends Database<P>> extends AbstractSetBalancer<P, D>
037{
038        private volatile List<D> databaseList;
039
040        private Random random = new Random();
041
042        /**
043         * Constructs a new RandomBalancer
044         * @param databases
045         */
046        public RandomBalancer(Set<D> databases)
047        {
048                super(databases);
049                
050                int total = 0;
051                
052                for (D database: databases)
053                {
054                        total += database.getWeight();
055                }
056                
057                List<D> list = new ArrayList<D>(total);
058                
059                for (D database: databases)
060                {
061                        int weight = database.getWeight();
062                        for (int i = 0; i < weight; ++i)
063                        {
064                                list.add(database);
065                        }
066                }
067                
068                this.databaseList = list;
069        }
070        
071        /**
072         * {@inheritDoc}
073         * @see net.sf.hajdbc.balancer.Balancer#next()
074         */
075        @Override
076        public D next()
077        {
078                List<D> list = this.databaseList;
079                
080                return !list.isEmpty() ? list.get(this.random.nextInt(list.size())) : this.primary();
081        }
082        
083        /**
084         * {@inheritDoc}
085         * @see net.sf.hajdbc.balancer.AbstractSetBalancer#added(net.sf.hajdbc.Database)
086         */
087        @Override
088        protected void added(D database)
089        {
090                int weight = database.getWeight();
091                
092                if (weight > 0)
093                {
094                        List<D> list = new ArrayList<D>(this.databaseList.size() + weight);
095                        
096                        list.addAll(this.databaseList);
097                        
098                        for (int i = 0; i < weight; ++i)
099                        {
100                                list.add(database);
101                        }
102                        
103                        this.databaseList = list;
104                }
105        }
106
107        /**
108         * {@inheritDoc}
109         * @see net.sf.hajdbc.balancer.AbstractSetBalancer#removed(net.sf.hajdbc.Database)
110         */
111        @Override
112        protected void removed(D database)
113        {
114                int weight = database.getWeight();
115
116                if (weight > 0)
117                {
118                        List<D> list = new ArrayList<D>(this.databaseList.size() - weight);
119                        
120                        int index = this.databaseList.indexOf(database);
121                        
122                        list.addAll(this.databaseList.subList(0, index));
123                        list.addAll(this.databaseList.subList(index + weight, this.databaseList.size()));
124                        
125                        this.databaseList = list;
126                }
127        }
128
129        /**
130         * {@inheritDoc}
131         * @see net.sf.hajdbc.balancer.AbstractSetBalancer#cleared()
132         */
133        @Override
134        protected void cleared()
135        {
136                this.databaseList = Collections.emptyList();
137        }
138}