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.invocation;
019
020import java.util.Map;
021import java.util.SortedMap;
022import java.util.TreeMap;
023
024import net.sf.hajdbc.Database;
025import net.sf.hajdbc.DatabaseCluster;
026import net.sf.hajdbc.ExceptionFactory;
027import net.sf.hajdbc.Messages;
028import net.sf.hajdbc.balancer.Balancer;
029import net.sf.hajdbc.dialect.Dialect;
030import net.sf.hajdbc.logging.Level;
031import net.sf.hajdbc.logging.Logger;
032import net.sf.hajdbc.logging.LoggerFactory;
033import net.sf.hajdbc.sql.ProxyFactory;
034import net.sf.hajdbc.state.StateManager;
035
036/**
037 * @author Paul Ferraro
038 */
039public class InvokeOnAnyInvocationStrategy implements InvocationStrategy
040{
041        private static Logger logger = LoggerFactory.getLogger(ExistingResultsCollector.class);
042
043        private final InvocationStrategy strategy;
044        
045        public InvokeOnAnyInvocationStrategy(InvocationStrategy strategy)
046        {
047                this.strategy = strategy;
048        }
049
050        /**
051         * {@inheritDoc}
052         */
053        @Override
054        public <Z, D extends Database<Z>, T, R, E extends Exception> SortedMap<D, R> invoke(ProxyFactory<Z, D, T, E> factory, Invoker<Z, D, T, R, E> invoker) throws E
055        {
056                DatabaseCluster<Z, D> cluster = factory.getDatabaseCluster();
057                Balancer<Z, D> balancer = cluster.getBalancer();
058                Dialect dialect = cluster.getDialect();
059                StateManager stateManager = cluster.getStateManager();
060
061                for (Map.Entry<D, T> entry: factory.entries())
062                {
063                        D database = entry.getKey();
064                        
065                        // If this database is no longer active, just skip it.
066                        if (balancer.contains(database))
067                        {
068                                try
069                                {
070                                        R result = invoker.invoke(database, entry.getValue());
071                                        
072                                        SortedMap<D, R> resultMap = new TreeMap<D, R>();
073                                        resultMap.put(database, result);
074                                        return resultMap;
075                                }
076                                catch (Exception e)
077                                {
078                                        // If this database was concurrently deactivated, just ignore the failure
079                                        if (cluster.getBalancer().contains(database))
080                                        {
081                                                ExceptionFactory<E> exceptionFactory = factory.getExceptionFactory();
082                                                E exception = exceptionFactory.createException(e);
083                                                
084                                                if (exceptionFactory.indicatesFailure(exception, dialect) && (cluster.getBalancer().size() > 1))
085                                                {
086                                                        if (cluster.deactivate(database, stateManager))
087                                                        {
088                                                                logger.log(Level.ERROR, exception, Messages.DATABASE_DEACTIVATED.getMessage(), database, cluster);
089                                                        }
090                                                }
091                                                else
092                                                {
093                                                        throw exception;
094                                                }
095                                        }
096                                }
097                        }
098                }
099                
100                // If no existing databases could handle the request, delegate to another invocation strategy
101                return this.strategy.invoke(factory, invoker);
102        }
103}