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.sql;
022:
023: import java.sql.SQLException;
024: import java.util.HashMap;
025: import java.util.Map;
026: import java.util.Set;
027: import java.util.SortedMap;
028: import java.util.TreeMap;
029: import java.util.concurrent.Callable;
030: import java.util.concurrent.ExecutionException;
031: import java.util.concurrent.ExecutorService;
032: import java.util.concurrent.Future;
033:
034: import net.sf.hajdbc.Database;
035: import net.sf.hajdbc.DatabaseCluster;
036: import net.sf.hajdbc.Messages;
037:
038: /**
039: * @author Paul Ferraro
040: * @param <D>
041: * @param <T>
042: * @param <R>
043: */
044: public class DatabaseWriteInvocationStrategy<D, T, R> implements
045: InvocationStrategy<D, T, R> {
046: private ExecutorService executor;
047:
048: /**
049: * @param executor
050: */
051: public DatabaseWriteInvocationStrategy(ExecutorService executor) {
052: this .executor = executor;
053: }
054:
055: /**
056: * @see net.sf.hajdbc.sql.InvocationStrategy#invoke(net.sf.hajdbc.sql.SQLProxy, net.sf.hajdbc.sql.Invoker)
057: */
058: @Override
059: public R invoke(SQLProxy<D, T> proxy, Invoker<D, T, R> invoker)
060: throws Exception {
061: SortedMap<Database<D>, R> map = this .invokeAll(proxy, invoker);
062:
063: return map.get(map.firstKey());
064: }
065:
066: protected SortedMap<Database<D>, R> invokeAll(SQLProxy<D, T> proxy,
067: final Invoker<D, T, R> invoker) throws Exception {
068: SortedMap<Database<D>, R> resultMap = new TreeMap<Database<D>, R>();
069: SortedMap<Database<D>, Exception> exceptionMap = new TreeMap<Database<D>, Exception>();
070: Map<Database<D>, Future<R>> futureMap = new HashMap<Database<D>, Future<R>>();
071:
072: DatabaseCluster<D> cluster = proxy.getDatabaseCluster();
073:
074: Set<Database<D>> databaseSet = cluster.getBalancer().all();
075:
076: proxy.getRoot().retain(databaseSet);
077:
078: if (databaseSet.isEmpty()) {
079: throw new SQLException(Messages.getMessage(
080: Messages.NO_ACTIVE_DATABASES, cluster));
081: }
082:
083: for (final Database<D> database : databaseSet) {
084: final T object = proxy.getObject(database);
085:
086: Callable<R> task = new Callable<R>() {
087: public R call() throws Exception {
088: return invoker.invoke(database, object);
089: }
090: };
091:
092: futureMap.put(database, this .executor.submit(task));
093: }
094:
095: for (Map.Entry<Database<D>, Future<R>> futureMapEntry : futureMap
096: .entrySet()) {
097: Database<D> database = futureMapEntry.getKey();
098:
099: try {
100: resultMap
101: .put(database, futureMapEntry.getValue().get());
102: } catch (ExecutionException e) {
103: Throwable cause = e.getCause();
104:
105: Exception exception = (cause instanceof Exception) ? (Exception) cause
106: : e;
107:
108: exceptionMap.put(database, exception);
109: } catch (InterruptedException e) {
110: Thread.currentThread().interrupt();
111:
112: exceptionMap.put(database, e);
113: }
114: }
115:
116: // If no databases returned successfully, return an exception back to the caller
117: if (resultMap.isEmpty()) {
118: proxy.handleFailures(exceptionMap);
119: }
120:
121: // If any databases failed, while others succeeded, handle the failures
122: return exceptionMap.isEmpty() ? resultMap : proxy
123: .handlePartialFailure(resultMap, exceptionMap);
124: }
125: }
|