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; 019 020import java.util.Collection; 021import java.util.NoSuchElementException; 022import java.util.Set; 023import java.util.SortedSet; 024import java.util.TreeSet; 025import java.util.concurrent.locks.Lock; 026import java.util.concurrent.locks.ReentrantLock; 027 028import net.sf.hajdbc.Database; 029import net.sf.hajdbc.invocation.Invoker; 030import net.sf.hajdbc.util.Collections; 031 032/** 033 * Abstract set-based {@link Balancer} implementation. 034 * @author Paul Ferraro 035 */ 036public abstract class AbstractSetBalancer<Z, D extends Database<Z>> extends AbstractBalancer<Z, D> 037{ 038 private final Lock lock = new ReentrantLock(); 039 040 private volatile SortedSet<D> databaseSet; 041 042 protected AbstractSetBalancer(Set<D> databases) 043 { 044 if (databases.isEmpty()) 045 { 046 this.databaseSet = Collections.emptySortedSet(); 047 } 048 else if (databases.size() == 1) 049 { 050 this.databaseSet = Collections.singletonSortedSet(databases.iterator().next()); 051 } 052 else 053 { 054 SortedSet<D> set = new TreeSet<D>(); 055 056 for (D database: databases) 057 { 058 set.add(database); 059 } 060 061 this.databaseSet = set; 062 } 063 } 064 065 protected Lock getLock() 066 { 067 return this.lock; 068 } 069 070 /** 071 * {@inheritDoc} 072 * @see net.sf.hajdbc.balancer.Balancer#invoke(net.sf.hajdbc.invocation.Invoker, net.sf.hajdbc.Database, java.lang.Object) 073 */ 074 @Override 075 public <T, R, E extends Exception> R invoke(Invoker<Z, D, T, R, E> invoker, D database, T object) throws E 076 { 077 return invoker.invoke(database, object); 078 } 079 080 /** 081 * {@inheritDoc} 082 * @see net.sf.hajdbc.balancer.Balancer#primary() 083 */ 084 @Override 085 public D primary() 086 { 087 try 088 { 089 return this.databaseSet.first(); 090 } 091 catch (NoSuchElementException e) 092 { 093 return null; 094 } 095 } 096 097 /** 098 * {@inheritDoc} 099 * @see net.sf.hajdbc.balancer.AbstractBalancer#getDatabases() 100 */ 101 @Override 102 protected Set<D> getDatabases() 103 { 104 return this.databaseSet; 105 } 106 107 /** 108 * {@inheritDoc} 109 * @see java.util.Set#remove(java.lang.Object) 110 */ 111 @SuppressWarnings("unchecked") 112 @Override 113 public boolean remove(Object database) 114 { 115 this.lock.lock(); 116 117 try 118 { 119 boolean remove = this.databaseSet.contains(database); 120 121 if (remove) 122 { 123 if (this.databaseSet.size() == 1) 124 { 125 this.databaseSet = Collections.emptySortedSet(); 126 } 127 else 128 { 129 SortedSet<D> set = new TreeSet<D>(this.databaseSet); 130 131 set.remove(database); 132 133 this.databaseSet = set; 134 } 135 136 this.removed((D) database); 137 } 138 139 return remove; 140 } 141 finally 142 { 143 this.lock.unlock(); 144 } 145 } 146 147 /** 148 * Called when a database was removed from the set. 149 * @param database a database descriptor 150 */ 151 protected abstract void removed(D database); 152 153 /** 154 * {@inheritDoc} 155 * @see java.util.Set#add(java.lang.Object) 156 */ 157 @Override 158 public boolean add(D database) 159 { 160 this.lock.lock(); 161 162 try 163 { 164 boolean add = !this.databaseSet.contains(database); 165 166 if (add) 167 { 168 if (this.databaseSet.isEmpty()) 169 { 170 this.databaseSet = Collections.singletonSortedSet(database); 171 } 172 else 173 { 174 SortedSet<D> set = new TreeSet<D>(this.databaseSet); 175 176 set.add(database); 177 178 this.databaseSet = set; 179 } 180 181 this.added(database); 182 } 183 184 return add; 185 } 186 finally 187 { 188 this.lock.unlock(); 189 } 190 } 191 192 /** 193 * Called when a database was added to the set. 194 * @param database a database descriptor 195 */ 196 protected abstract void added(D database); 197 198 /** 199 * {@inheritDoc} 200 * @see java.util.Set#addAll(java.util.Collection) 201 */ 202 @Override 203 public boolean addAll(Collection<? extends D> databases) 204 { 205 this.lock.lock(); 206 207 try 208 { 209 SortedSet<D> addSet = new TreeSet<D>(this.databaseSet); 210 211 boolean added = addSet.addAll(databases); 212 213 if (added) 214 { 215 Set<D> removeSet = new TreeSet<D>(addSet); 216 217 removeSet.removeAll(this.databaseSet); 218 219 this.databaseSet = addSet; 220 221 for (D database: removeSet) 222 { 223 this.added(database); 224 } 225 } 226 227 return added; 228 } 229 finally 230 { 231 this.lock.unlock(); 232 } 233 } 234 235 /** 236 * {@inheritDoc} 237 * @see java.util.Set#removeAll(java.util.Collection) 238 */ 239 @Override 240 public boolean removeAll(Collection<?> databases) 241 { 242 this.lock.lock(); 243 244 try 245 { 246 SortedSet<D> removeSet = new TreeSet<D>(this.databaseSet); 247 248 boolean removed = removeSet.removeAll(databases); 249 250 if (removed) 251 { 252 Set<D> retainSet = new TreeSet<D>(this.databaseSet); 253 254 retainSet.retainAll(databases); 255 256 this.databaseSet = removeSet; 257 258 for (D database: removeSet) 259 { 260 this.removed(database); 261 } 262 } 263 264 return removed; 265 } 266 finally 267 { 268 this.lock.unlock(); 269 } 270 } 271 272 /** 273 * {@inheritDoc} 274 * @see java.util.Set#retainAll(java.util.Collection) 275 */ 276 @Override 277 public boolean retainAll(Collection<?> databases) 278 { 279 this.lock.lock(); 280 281 try 282 { 283 SortedSet<D> retainSet = new TreeSet<D>(this.databaseSet); 284 285 boolean retained = retainSet.retainAll(databases); 286 287 if (retained) 288 { 289 Set<D> removeSet = new TreeSet<D>(this.databaseSet); 290 291 removeSet.removeAll(databases); 292 293 this.databaseSet = retainSet; 294 295 for (D database: removeSet) 296 { 297 this.removed(database); 298 } 299 } 300 301 return retained; 302 } 303 finally 304 { 305 this.lock.unlock(); 306 } 307 } 308 309 /** 310 * {@inheritDoc} 311 * @see java.util.Set#clear() 312 */ 313 @Override 314 public void clear() 315 { 316 this.lock.lock(); 317 318 try 319 { 320 if (!this.databaseSet.isEmpty()) 321 { 322 this.databaseSet = Collections.emptySortedSet(); 323 324 this.cleared(); 325 } 326 } 327 finally 328 { 329 this.lock.unlock(); 330 } 331 } 332 333 /** 334 * Called when the set was cleared. 335 */ 336 protected abstract void cleared(); 337}