/* * Copyright 1999-2012 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.cobar.jdbc; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Random; /** * @author xianmao.hexm 2012-4-27 */ public class UrlProvider { private static final String URL_COBAR_PREFIX = "jdbc:cobar://"; private static final String URL_MYSQL_PREFIX = "jdbc:mysql://"; private static final int PREFIX_LENGTH = URL_COBAR_PREFIX.length(); private static final int SOCKET_CONNECT_TIMEOUT = 10 * 1000; private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final Random RANDOM = new Random(); /** * 只处理非空并且以jdbc:cobar://开头的url */ public static final String getUrl(String url, Properties info) throws SQLException { if (url != null && url.regionMatches(true, 0, URL_COBAR_PREFIX, 0, PREFIX_LENGTH)) { UrlConnection c = null; try { ConnectInfo ci = parseUrl(url, info); c = new UrlConnection(ci.host, ci.port, ci.user, ci.password, ci.database); c.connect(SOCKET_CONNECT_TIMEOUT); return selectUrl(c.getServerList(), url, ci); } catch (Throwable e) { throw new SQLException(e); } finally { if (c != null) { c.close(); } } } else { return url; } } /** * 取得MySQL格式的URL */ public static final String getMySQLUrl(String url) { if (url != null && url.regionMatches(true, 0, URL_COBAR_PREFIX, 0, PREFIX_LENGTH)) { StringBuilder sb = new StringBuilder(); sb.append(URL_MYSQL_PREFIX).append(url.substring(PREFIX_LENGTH)); url = sb.toString(); } return url; } private static ConnectInfo parseUrl(String url, Properties info) { ConnectInfo ci = new ConnectInfo(); ci.user = info.getProperty("user"); ci.password = info.getProperty("password"); ci.database = info.getProperty("DBNAME"); // 解析参数 int index = url.indexOf('?'); if (index != -1) { String paramString = url.substring(index + 1); url = url.substring(0, index); ci.paramString = paramString; String[] params = split(paramString, '&'); for (String param : params) { int indexOfEquals = 0; if (param != null && (indexOfEquals = param.indexOf('=')) != -1) { String key = param.substring(0, indexOfEquals); if ("user".equals(key)) { if (indexOfEquals + 1 < param.length()) { ci.user = param.substring(indexOfEquals + 1); } } else if ("password".equals(key)) { if (indexOfEquals + 1 < param.length()) { ci.password = param.substring(indexOfEquals + 1); } } else if ("DBNAME".equals(key)) { if (indexOfEquals + 1 < param.length()) { ci.database = param.substring(indexOfEquals + 1); } } } } } // 解析host/port/database url = url.substring(PREFIX_LENGTH); String hostStuff = null; int index2 = url.indexOf('/'); if (index2 != -1) { hostStuff = url.substring(0, index2); if (index2 + 1 < url.length()) { ci.database = url.substring(index2 + 1); } } else { hostStuff = url; } // 如果有多个主机只取第一个 int index3 = hostStuff.indexOf(','); if (index3 != -1) { hostStuff = hostStuff.substring(0, index3); } int index4 = hostStuff.indexOf(':'); if (index4 != -1) { ci.host = hostStuff.substring(0, index4).trim(); if (index4 + 1 < hostStuff.length()) { ci.port = Integer.parseInt(hostStuff.substring(index4 + 1).trim()); } } else { ci.host = hostStuff.trim(); ci.port = 8066; } return ci; } /** * 结合权重和随机策略 */ private static String selectUrl(List<CobarNode> list, String originUrl, ConnectInfo info) { int total = 0; for (CobarNode node : list) { total += node.getWeight(); } // 如果总权重小于等于零则使用原来的url if (total <= 0) { return originUrl; } int rnd = 1 + RANDOM.nextInt(total); String host = null; for (CobarNode node : list) { if ((rnd -= node.getWeight()) <= 0) { host = node.getHost(); break; } } if (host == null) { int i = RANDOM.nextInt(list.size()); host = list.get(i).getHost(); } // 无法取得新的host,则返回原来的url。 if (host == null) { return originUrl; } // 生成新的URL StringBuilder url = new StringBuilder(); url.append(URL_MYSQL_PREFIX).append(host).append(':').append(info.port); if (info.database != null) { url.append('/').append(info.database); } if (info.paramString != null) { url.append('?').append(info.paramString); } return url.toString(); } private static String[] split(String src, char separatorChar) { if (src == null) { return null; } int length = src.length(); if (length == 0) { return EMPTY_STRING_ARRAY; } List<String> list = new LinkedList<String>(); int i = 0; int start = 0; boolean match = false; while (i < length) { if (src.charAt(i) == separatorChar) { if (match) { list.add(src.substring(start, i)); match = false; } start = ++i; continue; } match = true; i++; } if (match) { list.add(src.substring(start, i)); } return list.toArray(new String[list.size()]); } private static class ConnectInfo { private String host; private int port; private String user; private String password; private String database; private String paramString; } }