1 21 package net.sf.hajdbc.sql; 22 23 import java.util.Collection ; 24 import java.util.HashMap ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.NoSuchElementException ; 29 import java.util.SortedMap ; 30 import java.util.TreeMap ; 31 import java.util.concurrent.Callable ; 32 import java.util.concurrent.ExecutionException ; 33 import java.util.concurrent.ExecutorService ; 34 import java.util.concurrent.Future ; 35 import java.util.concurrent.locks.Lock ; 36 37 import net.sf.hajdbc.Balancer; 38 import net.sf.hajdbc.Database; 39 import net.sf.hajdbc.DatabaseCluster; 40 import net.sf.hajdbc.Messages; 41 import net.sf.hajdbc.Operation; 42 import net.sf.hajdbc.SQLException; 43 44 import org.slf4j.Logger; 45 import org.slf4j.LoggerFactory; 46 47 54 public abstract class SQLObject<E, P> 55 { 56 private static Logger logger = LoggerFactory.getLogger(SQLObject.class); 57 58 private DatabaseCluster databaseCluster; 59 protected SQLObject<P, ?> parent; 60 private Operation<P, E> parentOperation; 61 private Map <Database, E> objectMap; 62 private Map <String , Operation<E, ?>> operationMap = new HashMap <String , Operation<E, ?>>(); 63 64 protected SQLObject(SQLObject<P, ?> parent, Operation<P, E> operation, ExecutorService executor, Lock lock) throws java.sql.SQLException 65 { 66 this(parent.getDatabaseCluster(), execute(parent, operation, executor, lock)); 67 68 this.parent = parent; 69 this.parentOperation = operation; 70 } 71 72 81 private static <T, S> Map <Database, T> execute(SQLObject<S, ?> parent, Operation<S, T> operation, ExecutorService executor, Lock lock) throws java.sql.SQLException 82 { 83 return parent.executeWriteToDatabase(operation, executor, lock); 84 } 85 86 protected SQLObject(DatabaseCluster databaseCluster, Map <Database, E> objectMap) 87 { 88 this.databaseCluster = databaseCluster; 89 this.objectMap = objectMap; 90 } 91 92 99 public synchronized final E getObject(Database database) 100 { 101 E object = this.objectMap.get(database); 102 103 if (object == null) 104 { 105 try 106 { 107 if (this.parent == null) 108 { 109 throw new java.sql.SQLException (); 110 } 111 112 P parentObject = this.parent.getObject(database); 113 114 if (parentObject == null) 115 { 116 throw new java.sql.SQLException (); 117 } 118 119 object = this.parentOperation.execute(database, parentObject); 120 121 for (Operation<E, ?> operation: this.operationMap.values()) 122 { 123 operation.execute(database, object); 124 } 125 126 this.objectMap.put(database, object); 127 } 128 catch (java.sql.SQLException e) 129 { 130 if (this.databaseCluster.deactivate(database)) 131 { 132 logger.warn(Messages.getMessage(Messages.SQL_OBJECT_INIT_FAILED, this.getClass().getName(), database), e); 133 } 134 } 135 } 136 137 return object; 138 } 139 140 144 protected synchronized final void record(Operation<E, ?> operation) 145 { 146 this.operationMap.put(operation.getClass().toString(), operation); 147 } 148 149 155 protected final <T> T firstValue(Map <Database, T> valueMap) 156 { 157 return valueMap.values().iterator().next(); 158 } 159 160 private List <Database> getActiveDatabaseList() 161 { 162 List <Database> databaseList = this.databaseCluster.getBalancer().list(); 163 164 this.retain(databaseList); 165 166 return databaseList; 167 } 168 169 protected synchronized void retain(Collection <Database> activeDatabases) 170 { 171 if (this.parent == null) return; 172 173 Iterator <Map.Entry <Database, E>> mapEntries = this.objectMap.entrySet().iterator(); 174 175 while (mapEntries.hasNext()) 176 { 177 Map.Entry <Database, E> mapEntry = mapEntries.next(); 178 179 Database database = mapEntry.getKey(); 180 181 if (!activeDatabases.contains(database)) 182 { 183 E object = mapEntry.getValue(); 184 185 if (object != null) 186 { 187 try 188 { 189 this.close(object); 190 } 191 catch (java.sql.SQLException e) 192 { 193 } 195 } 196 197 mapEntries.remove(); 198 } 199 } 200 201 this.parent.retain(activeDatabases); 202 } 203 204 protected abstract void close(E object) throws java.sql.SQLException ; 205 206 214 public final <T> T executeReadFromDriver(Operation<E, T> operation) throws java.sql.SQLException 215 { 216 try 217 { 218 Database database = this.databaseCluster.getBalancer().first(); 219 E object = this.getObject(database); 220 221 return operation.execute(database, object); 222 } 223 catch (NoSuchElementException e) 224 { 225 throw new SQLException(Messages.getMessage(Messages.NO_ACTIVE_DATABASES, this.databaseCluster)); 226 } 227 } 228 229 237 public final <T> T executeReadFromDatabase(Operation<E, T> operation) throws java.sql.SQLException 238 { 239 Balancer balancer = this.databaseCluster.getBalancer(); 240 241 try 242 { 243 while (true) 244 { 245 Database database = balancer.next(); 246 E object = this.getObject(database); 247 248 try 249 { 250 balancer.beforeOperation(database); 251 252 return operation.execute(database, object); 253 } 254 catch (java.sql.SQLException e) 255 { 256 this.databaseCluster.handleFailure(database, e); 257 } 258 finally 259 { 260 balancer.afterOperation(database); 261 } 262 } 263 } 264 catch (NoSuchElementException e) 265 { 266 throw new SQLException(Messages.getMessage(Messages.NO_ACTIVE_DATABASES, this.databaseCluster)); 267 } 268 } 269 270 278 public final <T> Map <Database, T> executeTransactionalWriteToDatabase(final Operation<E, T> operation) throws java.sql.SQLException 279 { 280 return this.executeWriteToDatabase(operation, this.databaseCluster.getTransactionalExecutor(), this.databaseCluster.readLock()); 281 } 282 283 291 public final <T> Map <Database, T> executeNonTransactionalWriteToDatabase(final Operation<E, T> operation) throws java.sql.SQLException 292 { 293 return this.executeWriteToDatabase(operation, this.databaseCluster.getNonTransactionalExecutor(), null); 294 } 295 296 private <T> Map <Database, T> executeWriteToDatabase(final Operation<E, T> operation, ExecutorService executor, Lock lock) throws java.sql.SQLException 297 { 298 Map <Database, T> resultMap = new TreeMap <Database, T>(); 299 SortedMap <Database, java.sql.SQLException > exceptionMap = new TreeMap <Database, java.sql.SQLException >(); 300 301 if (lock != null) 302 { 303 lock.lock(); 304 } 305 306 try 307 { 308 List <Database> databaseList = this.getActiveDatabaseList(); 309 310 if (databaseList.isEmpty()) 311 { 312 throw new SQLException(Messages.getMessage(Messages.NO_ACTIVE_DATABASES, this.databaseCluster)); 313 } 314 315 Map <Database, Future <T>> futureMap = new HashMap <Database, Future <T>>(); 316 317 for (final Database database: databaseList) 318 { 319 final E object = this.getObject(database); 320 321 Callable <T> task = new Callable <T>() 322 { 323 public T call() throws java.sql.SQLException 324 { 325 return operation.execute(database, object); 326 } 327 }; 328 329 futureMap.put(database, executor.submit(task)); 330 } 331 332 for (Database database: databaseList) 333 { 334 Future <T> future = futureMap.get(database); 335 336 try 337 { 338 resultMap.put(database, future.get()); 339 } 340 catch (ExecutionException e) 341 { 342 SQLException cause = new SQLException(e.getCause()); 343 344 try 345 { 346 this.databaseCluster.handleFailure(database, cause); 347 } 348 catch (java.sql.SQLException sqle) 349 { 350 exceptionMap.put(database, sqle); 351 } 352 } 353 } 354 } 355 catch (InterruptedException e) 356 { 357 throw new SQLException(e); 358 } 359 finally 360 { 361 if (lock != null) 362 { 363 lock.unlock(); 364 } 365 } 366 367 if (resultMap.isEmpty()) 369 { 370 if (exceptionMap.isEmpty()) 371 { 372 throw new SQLException(Messages.getMessage(Messages.NO_ACTIVE_DATABASES, this.databaseCluster)); 373 } 374 375 throw exceptionMap.get(exceptionMap.firstKey()); 376 } 377 378 if (!exceptionMap.isEmpty()) 380 { 381 this.handleExceptions(exceptionMap); 382 } 383 384 return resultMap; 386 } 387 388 396 public final <T> Map <Database, T> executeWriteToDriver(Operation<E, T> operation) throws java.sql.SQLException 397 { 398 Map <Database, T> resultMap = new TreeMap <Database, T>(); 399 400 Lock lock = this.databaseCluster.readLock(); 401 402 lock.lock(); 403 404 try 405 { 406 List <Database> databaseList = this.getActiveDatabaseList(); 407 408 if (databaseList.isEmpty()) 409 { 410 throw new SQLException(Messages.getMessage(Messages.NO_ACTIVE_DATABASES, this.databaseCluster)); 411 } 412 413 for (Database database: databaseList) 414 { 415 E object = this.getObject(database); 416 417 resultMap.put(database, operation.execute(database, object)); 418 } 419 } 420 finally 421 { 422 lock.unlock(); 423 } 424 425 this.record(operation); 426 427 return resultMap; 428 } 429 430 434 public DatabaseCluster getDatabaseCluster() 435 { 436 return this.databaseCluster; 437 } 438 439 443 @SuppressWarnings ("unused") 444 public void handleExceptions(Map <Database, java.sql.SQLException > exceptionMap) throws java.sql.SQLException 445 { 446 for (Map.Entry <Database, java.sql.SQLException > exceptionMapEntry: exceptionMap.entrySet()) 447 { 448 Database database = exceptionMapEntry.getKey(); 449 java.sql.SQLException exception = exceptionMapEntry.getValue(); 450 451 if (this.databaseCluster.deactivate(database)) 452 { 453 logger.error(Messages.getMessage(Messages.DATABASE_DEACTIVATED, database, this.databaseCluster), exception); 454 } 455 } 456 } 457 } 458 | Popular Tags |