source: Ballon/out/artifacts/geisa_artifact/WEB-INF/lib/mysql-connector-java-5.1.21/src/com/mysql/jdbc/FailoverConnectionProxy.java @ 848

Last change on this file since 848 was 766, checked in by npipsl, 11 years ago
File size: 6.9 KB
Line 
1/*
2 Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
3 
4
5  The MySQL Connector/J is licensed under the terms of the GPLv2
6  <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
7  There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
8  this software, see the FLOSS License Exception
9  <http://www.mysql.com/about/legal/licensing/foss-exception.html>.
10
11  This program is free software; you can redistribute it and/or modify it under the terms
12  of the GNU General Public License as published by the Free Software Foundation; version 2
13  of the License.
14
15  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
16  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17  See the GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License along with this
20  program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
21  Floor, Boston, MA 02110-1301  USA
22 
23 */
24
25package com.mysql.jdbc;
26
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29import java.lang.reflect.Proxy;
30import java.sql.SQLException;
31import java.util.Iterator;
32import java.util.List;
33import java.util.Map;
34import java.util.Properties;
35
36public class FailoverConnectionProxy extends LoadBalancingConnectionProxy {
37        class FailoverInvocationHandler extends
38                        ConnectionErrorFiringInvocationHandler {
39
40                public FailoverInvocationHandler(Object toInvokeOn) {
41                        super(toInvokeOn);
42                }
43
44                public Object invoke(Object proxy, Method method, Object[] args)
45                                throws Throwable {
46                        String methodName = method.getName();
47
48                        if (failedOver && methodName.indexOf("execute") != -1) {
49                                queriesIssuedFailedOver++;
50                        }
51
52                        return super.invoke(proxy, method, args);
53                }
54
55        }
56
57        boolean failedOver;
58        boolean hasTriedMaster;
59        private long masterFailTimeMillis;
60        boolean preferSlaveDuringFailover;
61        private String primaryHostPortSpec;
62        private long queriesBeforeRetryMaster;
63        long queriesIssuedFailedOver;
64        private int secondsBeforeRetryMaster;
65       
66        FailoverConnectionProxy(List<String> hosts, Properties props) throws SQLException {
67                super(hosts, props);
68                ConnectionPropertiesImpl connectionProps = new ConnectionPropertiesImpl();
69                connectionProps.initializeProperties(props);
70               
71                this.queriesBeforeRetryMaster = connectionProps.getQueriesBeforeRetryMaster();
72                this.secondsBeforeRetryMaster = connectionProps.getSecondsBeforeRetryMaster();
73                this.preferSlaveDuringFailover = false;
74        }
75       
76        protected ConnectionErrorFiringInvocationHandler createConnectionProxy(
77                        Object toProxy) {
78                return new FailoverInvocationHandler(toProxy);
79        }
80
81        // slightly different behavior than load balancing, we only pick a new
82        // connection if we've issued enough queries or enough time has passed
83        // since we failed over, and that's all handled in pickNewConnection().
84        synchronized void dealWithInvocationException(InvocationTargetException e)
85                        throws SQLException, Throwable, InvocationTargetException {
86                Throwable t = e.getTargetException();
87
88                if (t != null) {
89                        if (failedOver) { // try and fall back
90                                createPrimaryConnection();
91                               
92                                if (this.currentConn != null) {
93                                        throw t;
94                                }
95                        }
96                       
97                        failOver();
98                       
99                        throw t;
100                }
101
102                throw e;
103        }
104
105        public Object invoke(Object proxy, Method method, Object[] args)
106                        throws Throwable {
107                String methodName = method.getName();
108               
109                if ("setPreferSlaveDuringFailover".equals(methodName)) {
110                        preferSlaveDuringFailover = ((Boolean) args[0]).booleanValue();
111                } else if ("clearHasTriedMaster".equals(methodName)) {
112                        hasTriedMaster = false;
113                } else if ("hasTriedMaster".equals(methodName)) {
114                        return Boolean.valueOf(hasTriedMaster);
115                } else if ("isMasterConnection".equals(methodName)) {
116                        return Boolean.valueOf(!failedOver);
117                } else if ("isSlaveConnection".equals(methodName)) {
118                        return Boolean.valueOf(failedOver);
119                } else if ("setReadOnly".equals(methodName)) {
120                        if (failedOver) {
121                                return null; // no-op when failed over
122                        }
123                } else if ("setAutoCommit".equals(methodName) && failedOver && 
124                                shouldFallBack() && Boolean.TRUE.equals(args[0]) && failedOver) {
125                        createPrimaryConnection();
126                       
127                        return super.invoke(proxy, method, args, failedOver);
128                } else if ("hashCode".equals(methodName)) {
129                return Integer.valueOf(this.hashCode());
130                } else if ("equals".equals(methodName)) {
131                        if (args[0] instanceof Proxy) {
132                                return Boolean.valueOf((((Proxy) args[0]).equals(this)));
133                        }
134                        return Boolean.valueOf(this.equals(args[0])); 
135                }
136                return super.invoke(proxy, method, args, failedOver);
137        }
138
139        private synchronized void createPrimaryConnection() throws SQLException {
140                try {
141                        this.currentConn = createConnectionForHost(this.primaryHostPortSpec);
142                        this.failedOver = false;
143                        this.hasTriedMaster = true;
144                       
145                        // reset failed-over state
146                        this.queriesIssuedFailedOver = 0;
147                } catch (SQLException sqlEx) {
148                        this.failedOver = true;
149
150                        if (this.currentConn != null) {
151                                this.currentConn.getLog().logWarn("Connection to primary host failed", sqlEx);
152                        }
153                }
154        }
155
156        synchronized void invalidateCurrentConnection() throws SQLException {
157                if (!this.failedOver) {
158                        this.failedOver = true;
159                        this.queriesIssuedFailedOver = 0;
160                        this.masterFailTimeMillis = System.currentTimeMillis();
161                }
162                super.invalidateCurrentConnection();
163        }
164
165        protected synchronized void pickNewConnection() throws SQLException {
166                if (this.primaryHostPortSpec == null) {
167                        this.primaryHostPortSpec = this.hostList.remove(0); // first connect
168                }
169
170                if (this.currentConn == null || (this.failedOver && shouldFallBack())) {
171                        createPrimaryConnection();
172                       
173                        if (this.currentConn != null) {
174                                return;
175                        }
176                }
177               
178                failOver();
179        }
180
181        private synchronized void failOver() throws SQLException {
182                if (failedOver) {
183                        Iterator<Map.Entry<String,ConnectionImpl>> iter = liveConnections.entrySet().iterator();
184                       
185                        while (iter.hasNext()) {
186                                Map.Entry<String,ConnectionImpl> entry = iter.next();
187                                        entry.getValue().close();
188                        }
189                       
190                        liveConnections.clear();
191                }
192               
193                super.pickNewConnection();
194               
195                if (this.currentConn.getFailOverReadOnly()) {
196                        this.currentConn.setReadOnly(true);
197                } else {
198                        this.currentConn.setReadOnly(false);
199                }
200               
201                this.failedOver = true;
202        }
203
204        /**
205         * Should we try to connect back to the master? We try when we've been
206         * failed over >= this.secondsBeforeRetryMaster _or_ we've issued >
207         * this.queriesIssuedFailedOver
208         *
209         * @return DOCUMENT ME!
210         */
211        private boolean shouldFallBack() {
212                long secondsSinceFailedOver = (System.currentTimeMillis() - this.masterFailTimeMillis) / 1000;
213
214                if (secondsSinceFailedOver >= this.secondsBeforeRetryMaster) {
215                        // reset the timer
216                        this.masterFailTimeMillis = System.currentTimeMillis();
217                       
218                        return true;
219                } else if (this.queriesBeforeRetryMaster != 0 && this.queriesIssuedFailedOver >= this.queriesBeforeRetryMaster) {
220                        return true;
221                }
222               
223                return false;
224        }
225}
Note: See TracBrowser for help on using the repository browser.