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.SortedMap;
021import java.util.TreeMap;
022
023import net.sf.hajdbc.Database;
024import net.sf.hajdbc.DatabaseCluster;
025import net.sf.hajdbc.ExceptionFactory;
026import net.sf.hajdbc.Messages;
027import net.sf.hajdbc.balancer.Balancer;
028import net.sf.hajdbc.dialect.Dialect;
029import net.sf.hajdbc.logging.Level;
030import net.sf.hajdbc.logging.Logger;
031import net.sf.hajdbc.logging.LoggerFactory;
032import net.sf.hajdbc.sql.ProxyFactory;
033import net.sf.hajdbc.state.StateManager;
034
035/**
036 * @author paul
037 *
038 */
039public class InvokeOnOneInvocationStrategy implements InvocationStrategy
040{
041        private static Logger logger = LoggerFactory.getLogger(InvokeOnOneInvocationStrategy.class);
042        
043        public static interface DatabaseSelector
044        {
045                <Z, D extends Database<Z>> D selectDatabase(Balancer<Z, D> balancer);
046        }
047
048        private final DatabaseSelector selector;
049        
050        public InvokeOnOneInvocationStrategy(DatabaseSelector selector)
051        {
052                this.selector = selector;
053        }
054        
055        /**
056         * {@inheritDoc}
057         */
058        @Override
059        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
060        {
061                DatabaseCluster<Z, D> cluster = factory.getDatabaseCluster();
062                ExceptionFactory<E> exceptionFactory = factory.getExceptionFactory();
063                Balancer<Z, D> balancer = cluster.getBalancer();
064                Dialect dialect = cluster.getDialect();
065                StateManager stateManager = cluster.getStateManager();
066                
067                while (true)
068                {
069                        D database = this.selector.selectDatabase(balancer);
070                        
071                        if (database == null)
072                        {
073                                throw exceptionFactory.createException(Messages.NO_ACTIVE_DATABASES.getMessage(cluster));
074                        }
075                        
076                        T object = factory.get(database);
077                        
078                        try
079                        {
080                                R result = balancer.invoke(invoker, database, object);
081                                
082                                SortedMap<D, R> resultMap = new TreeMap<D, R>();
083                                resultMap.put(database, result);
084                                return resultMap;
085                        }
086                        catch (Exception e)
087                        {
088                                // If this database was concurrently deactivated, just ignore the failure
089                                if (balancer.contains(database))
090                                {
091                                        E exception = exceptionFactory.createException(e);
092                                        
093                                        if (exceptionFactory.indicatesFailure(exception, dialect))
094                                        {
095                                                if (cluster.deactivate(database, stateManager))
096                                                {
097                                                        logger.log(Level.ERROR, exception, Messages.DATABASE_DEACTIVATED.getMessage(), database, cluster);
098                                                }
099                                        }
100                                        else
101                                        {
102                                                throw exception;
103                                        }
104                                }
105                        }
106                }
107        }
108}