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: */
021: package net.sf.hajdbc.balancer;
022:
023: import java.util.Collections;
024: import java.util.Comparator;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.TreeMap;
028: import java.util.concurrent.atomic.AtomicInteger;
029: import java.util.concurrent.locks.Lock;
030: import java.util.concurrent.locks.ReentrantLock;
031:
032: import net.sf.hajdbc.Balancer;
033: import net.sf.hajdbc.Database;
034:
035: /**
036: * Balancer implementation whose {@link #next()} implementation returns the database with the least load.
037: *
038: * @author Paul Ferraro
039: * @param <D> either java.sql.Driver or javax.sql.DataSource
040: */
041: public class LoadBalancer<D> implements Balancer<D> {
042: private volatile Map<Database<D>, AtomicInteger> databaseMap = Collections
043: .emptyMap();
044:
045: private Lock lock = new ReentrantLock();
046:
047: private Comparator<Map.Entry<Database<D>, AtomicInteger>> comparator = new Comparator<Map.Entry<Database<D>, AtomicInteger>>() {
048: public int compare(
049: Map.Entry<Database<D>, AtomicInteger> mapEntry1,
050: Map.Entry<Database<D>, AtomicInteger> mapEntry2) {
051: Database<D> database1 = mapEntry1.getKey();
052: Database<D> database2 = mapEntry2.getKey();
053:
054: float load1 = mapEntry1.getValue().get();
055: float load2 = mapEntry2.getValue().get();
056:
057: int weight1 = database1.getWeight();
058: int weight2 = database2.getWeight();
059:
060: // If weights are the same, we can simply compare the loads
061: if (weight1 == weight2) {
062: return Float.compare(load1, load2);
063: }
064:
065: float weightedLoad1 = (weight1 != 0) ? (load1 / weight1)
066: : Float.POSITIVE_INFINITY;
067: float weightedLoad2 = (weight2 != 0) ? (load2 / weight2)
068: : Float.POSITIVE_INFINITY;
069:
070: return Float.compare(weightedLoad1, weightedLoad2);
071: }
072: };
073:
074: /**
075: * @see net.sf.hajdbc.Balancer#all()
076: */
077: @Override
078: public Set<Database<D>> all() {
079: return Collections.unmodifiableSet(this .databaseMap.keySet());
080: }
081:
082: /**
083: * @see net.sf.hajdbc.Balancer#clear()
084: */
085: @Override
086: public void clear() {
087: this .lock.lock();
088:
089: try {
090: this .databaseMap = Collections.emptyMap();
091: } finally {
092: this .lock.unlock();
093: }
094: }
095:
096: /**
097: * @see net.sf.hajdbc.Balancer#remove(net.sf.hajdbc.Database)
098: */
099: @Override
100: public boolean remove(Database<D> database) {
101: this .lock.lock();
102:
103: try {
104: boolean exists = this .databaseMap.containsKey(database);
105:
106: if (exists) {
107: Map<Database<D>, AtomicInteger> map = new TreeMap<Database<D>, AtomicInteger>(
108: this .databaseMap);
109:
110: map.remove(database);
111:
112: this .databaseMap = map;
113: }
114:
115: return exists;
116: } finally {
117: this .lock.unlock();
118: }
119: }
120:
121: /**
122: * @see net.sf.hajdbc.Balancer#next()
123: */
124: @Override
125: public Database<D> next() {
126: return Collections.min(this .databaseMap.entrySet(),
127: this .comparator).getKey();
128: }
129:
130: /**
131: * @see net.sf.hajdbc.Balancer#add(net.sf.hajdbc.Database)
132: */
133: @Override
134: public boolean add(Database<D> database) {
135: this .lock.lock();
136:
137: try {
138: boolean exists = this .databaseMap.containsKey(database);
139:
140: if (!exists) {
141: Map<Database<D>, AtomicInteger> map = new TreeMap<Database<D>, AtomicInteger>(
142: this .databaseMap);
143:
144: map.put(database, new AtomicInteger(1));
145:
146: this .databaseMap = map;
147: }
148:
149: return !exists;
150: } finally {
151: this .lock.unlock();
152: }
153: }
154:
155: /**
156: * @see net.sf.hajdbc.Balancer#beforeInvocation(net.sf.hajdbc.Database)
157: */
158: @Override
159: public void beforeInvocation(Database<D> database) {
160: AtomicInteger load = this .databaseMap.get(database);
161:
162: if (load != null) {
163: load.incrementAndGet();
164: }
165: }
166:
167: /**
168: * @see net.sf.hajdbc.Balancer#afterInvocation(net.sf.hajdbc.Database)
169: */
170: @Override
171: public void afterInvocation(Database<D> database) {
172: AtomicInteger load = this.databaseMap.get(database);
173:
174: if (load != null) {
175: load.decrementAndGet();
176: }
177: }
178: }
|