loadbalancingconnectionproxy.java

来自「mysql5.0 JDBC 驱动 放在glassfish或者tomcat的li」· Java 代码 · 共 535 行 · 第 1/2 页

JAVA
535
字号
	 * Closes current connection and removes it from required mappings.
	 * 
	 * @throws SQLException
	 */
	synchronized void invalidateCurrentConnection() throws SQLException {
		try {
			if (!this.currentConn.isClosed()) {
				this.currentConn.close();
			}

		} finally {
			// add host to the global blacklist, if enabled
			if (this.isGlobalBlacklistEnabled()) {
				this.addToGlobalBlacklist((String) this.connectionsToHostsMap.get(this.currentConn)); 
				
			}
			// remove from liveConnections
			this.liveConnections.remove(this.connectionsToHostsMap
					.get(this.currentConn));
			
			int hostIndex = ((Integer) this.hostsToListIndexMap
					.get(this.connectionsToHostsMap.get(this.currentConn)))
					.intValue();

			// reset the statistics for the host
			synchronized (this.responseTimes) {
				this.responseTimes[hostIndex] = 0;
			}

			this.connectionsToHostsMap.remove(this.currentConn);
			}
	}

	/**
	 * Proxies method invocation on the java.sql.Connection interface, trapping
	 * "close", "isClosed" and "commit/rollback" (to switch connections for load
	 * balancing).
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {

		String methodName = method.getName();

		if ("equals".equals(methodName) && args.length == 1) {
			if (args[0] instanceof Proxy) {
				return Boolean.valueOf((((Proxy)args[0]).equals(this)));
			}
			
			return Boolean.valueOf(equals(args[0]));
		}
		
		if ("close".equals(methodName)) {
			synchronized (this.liveConnections) {
				// close all underlying connections
				Iterator allConnections = this.liveConnections.values()
						.iterator();

				while (allConnections.hasNext()) {
					((Connection) allConnections.next()).close();
				}

				if (!this.isClosed) {
					this.balancer.destroy();
				}
				
				this.liveConnections.clear();
				this.connectionsToHostsMap.clear();
			}

			return null;
		}

		if ("isClosed".equals(methodName)) {
			return Boolean.valueOf(this.isClosed);
		}

		if (this.isClosed) {
			throw SQLError.createSQLException(
					"No operations allowed after connection closed.",
					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
		}

		if (!inTransaction) {
			this.inTransaction = true;
			this.transactionStartTime = getLocalTimeBestResolution();
		}

		Object result = null;

		try {
			result = method.invoke(this.currentConn, args);

			if (result != null) {
				if (result instanceof com.mysql.jdbc.Statement) {
					((com.mysql.jdbc.Statement)result).setPingTarget(this);
				}
				
				result = proxyIfInterfaceIsJdbc(result, result.getClass());
			}
		} catch (InvocationTargetException e) {
			dealWithInvocationException(e);
		} finally {
			if ("commit".equals(methodName) || "rollback".equals(methodName)) {
				this.inTransaction = false;

				// Update stats
				String host = (String) this.connectionsToHostsMap.get(this.currentConn);
				// avoid NPE if the connection has already been removed from connectionsToHostsMap
				// in invalidateCurrenctConnection()
				if ( host != null ) {
					int hostIndex = ((Integer) this.hostsToListIndexMap
							.get( host ))
							.intValue();
	
					
					synchronized (this.responseTimes) {
						this.responseTimes[hostIndex] = getLocalTimeBestResolution()
								- this.transactionStartTime;
					}
				}

				pickNewConnection();
			}
		}

		return result;
	}

	/**
	 * Picks the "best" connection to use for the next transaction based on the
	 * BalanceStrategy in use.
	 * 
	 * @throws SQLException
	 */
	private synchronized void pickNewConnection() throws SQLException {
		if (this.currentConn == null) {
			this.currentConn = this.balancer.pickConnection(this,
					Collections.unmodifiableList(this.hostList),
					Collections.unmodifiableMap(this.liveConnections),
					(long[]) this.responseTimes.clone(),
					this.retriesAllDown);

			return;
		}

		Connection newConn = this.balancer.pickConnection(this,
				Collections.unmodifiableList(this.hostList),
				Collections.unmodifiableMap(this.liveConnections),
				(long[]) this.responseTimes.clone(),
				this.retriesAllDown);

		newConn.setTransactionIsolation(this.currentConn
				.getTransactionIsolation());
		newConn.setAutoCommit(this.currentConn.getAutoCommit());
		this.currentConn = newConn;
	}

	/**
	 * Recursively checks for interfaces on the given object to determine if it
	 * implements a java.sql interface, and if so, proxies the instance so that
	 * we can catch and fire SQL errors.
	 * 
	 * @param toProxy
	 * @param clazz
	 * @return
	 */
	Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) {
		Class[] interfaces = clazz.getInterfaces();

		for (int i = 0; i < interfaces.length; i++) {
			String packageName = interfaces[i].getPackage().getName();

			if ("java.sql".equals(packageName)
					|| "javax.sql".equals(packageName)) {
				return Proxy.newProxyInstance(toProxy.getClass()
						.getClassLoader(), interfaces,
						new ConnectionErrorFiringInvocationHandler(toProxy));
			}

			return proxyIfInterfaceIsJdbc(toProxy, interfaces[i]);
		}

		return toProxy;
	}

	/**
	 * Returns best-resolution representation of local time, using nanoTime() if
	 * availble, otherwise defaulting to currentTimeMillis().
	 */
	private static long getLocalTimeBestResolution() {
		if (getLocalTimeMethod != null) {
			try {
				return ((Long) getLocalTimeMethod.invoke(null, null))
						.longValue();
			} catch (IllegalArgumentException e) {
				// ignore - we fall through to currentTimeMillis()
			} catch (IllegalAccessException e) {
				// ignore - we fall through to currentTimeMillis()
			} catch (InvocationTargetException e) {
				// ignore - we fall through to currentTimeMillis()
			}
		}

		return System.currentTimeMillis();
	}

	public synchronized void doPing() throws SQLException {
		Iterator allConns = this.liveConnections.values().iterator();
		
		while (allConns.hasNext()) {
			((Connection)allConns.next()).ping();
		}
	}
	
	public void addToGlobalBlacklist(String host) {
		if(this.isGlobalBlacklistEnabled()) {
			synchronized(globalBlacklist){
				globalBlacklist.put(host, new Long(System.currentTimeMillis() 
					+ this.globalBlacklistTimeout));
			}
		}
	}
	
	public boolean isGlobalBlacklistEnabled() {
		return (this.globalBlacklistTimeout > 0);
	}
	
	public Map getGlobalBlacklist() {
		if(!this.isGlobalBlacklistEnabled()) {
			return new HashMap(1);
		}
		
		// Make a local copy of the blacklist
		Map blacklistClone = new HashMap(globalBlacklist.size());
		// Copy everything from synchronized global blacklist to local copy for manipulation
		synchronized (globalBlacklist) {
			blacklistClone.putAll(globalBlacklist);
		}
		Set keys = blacklistClone.keySet();
		
		// we're only interested in blacklisted hosts that are in the hostList
		keys.retainAll(this.hostList);
		if(keys.size() == this.hostList.size()){
			// return an empty blacklist, let the BalanceStrategy implementations try to connect to everything
			// since it appears that all hosts are unavailable - we don't want to wait for 
			// loadBalanceBlacklistTimeout to expire.
			return new HashMap(1); 			
		}
		
		// Don't need to synchronize here as we using a local copy
		for(Iterator i = keys.iterator(); i.hasNext(); ) {
			String host = (String) i.next();
			// OK if null is returned because another thread already purged Map entry.
			Long timeout = (Long) globalBlacklist.get(host);
			if(timeout != null && timeout.longValue() < System.currentTimeMillis()){
				// Timeout has expired, remove from blacklist
				synchronized(globalBlacklist){
					globalBlacklist.remove(host);
				}
				blacklistClone.remove(host);
			}
				
		}
		
		return blacklistClone;
	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?