package org.apache.blur.thrift; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_CONNECTION; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_TIMEOUT; import static org.apache.blur.utils.BlurConstants.BLUR_ZOOKEEPER_TIMEOUT_DEFAULT; import java.io.Closeable; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.blur.BlurConfiguration; import org.apache.blur.log.Log; import org.apache.blur.log.LogFactory; import org.apache.blur.thirdparty.thrift_0_9_0.TException; import org.apache.blur.thrift.commands.BlurCommand; import org.apache.blur.thrift.generated.Blur.Client; import org.apache.blur.thrift.generated.Blur.Iface; import org.apache.blur.thrift.generated.BlurException; import org.apache.blur.zookeeper.WatchChildren; import org.apache.blur.zookeeper.WatchChildren.OnChange; import org.apache.blur.zookeeper.ZooKeeperClient; import org.apache.blur.zookeeper.ZookeeperPathConstants; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; public class BlurClient { private static final Log LOG = LogFactory.getLog(BlurClient.class); public static class BlurClientInvocationHandler implements InvocationHandler { private final List<Connection> _connections; private final int _maxRetries; private final long _backOffTime; private final long _maxBackOffTime; public BlurClientInvocationHandler(List<Connection> connections, int maxRetries, long backOffTime, long maxBackOffTime) { _connections = connections; _maxRetries = maxRetries; _backOffTime = backOffTime; _maxBackOffTime = maxBackOffTime; } public BlurClientInvocationHandler(List<Connection> connections) { this(connections, BlurClientManager.MAX_RETRIES, BlurClientManager.BACK_OFF_TIME, BlurClientManager.MAX_BACK_OFF_TIME); } public List<Connection> getConnections() { return _connections; } @Override public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { return BlurClientManager.execute(_connections, new BlurCommand<Object>() { @Override public Object call(Client client) throws BlurException, TException { try { return method.invoke(client, args); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof BlurException) { throw (BlurException) targetException; } if (targetException instanceof TException) { throw (TException) targetException; } throw new RuntimeException(targetException); } } }, _maxRetries, _backOffTime, _maxBackOffTime); } } private static class ZooKeeperConntrollerWatchInfo implements Closeable { final List<Connection> _connections = new CopyOnWriteArrayList<Connection>(); final ZooKeeper _zooKeeper; final WatchChildren _watchConntrollers; final String _zooKeeperConnectionStr; final int _zkSessionTimeout; ZooKeeperConntrollerWatchInfo(BlurConfiguration conf) throws IOException, KeeperException, InterruptedException { _zooKeeperConnectionStr = conf.getExpected(BLUR_ZOOKEEPER_CONNECTION); _zkSessionTimeout = conf.getInt(BLUR_ZOOKEEPER_TIMEOUT, BLUR_ZOOKEEPER_TIMEOUT_DEFAULT); _zooKeeper = new ZooKeeperClient(_zooKeeperConnectionStr, _zkSessionTimeout, new Watcher() { @Override public void process(WatchedEvent event) { } }); setConnections(_zooKeeper.getChildren(ZookeeperPathConstants.getOnlineControllersPath(), false)); _watchConntrollers = new WatchChildren(_zooKeeper, ZookeeperPathConstants.getOnlineControllersPath()); _watchConntrollers.watch(new OnChange() { @Override public void action(List<String> children) { setConnections(children); } }); } void setConnections(List<String> children) { Set<Connection> goodConnections = new HashSet<Connection>(); for (String s : children) { Connection connection = new Connection(s); goodConnections.add(connection); if (!_connections.contains(connection)) { _connections.add(connection); } } Set<Connection> badConnections = new HashSet<Connection>(); for (Connection c : _connections) { if (!goodConnections.contains(c)) { badConnections.add(c); } } _connections.removeAll(badConnections); } @Override public void close() throws IOException { closeQuietly(_watchConntrollers); closeQuietly(new Closeable() { @Override public void close() throws IOException { try { _zooKeeper.close(); } catch (InterruptedException e) { throw new IOException(e); } } }); } } private static ConcurrentMap<String, ZooKeeperConntrollerWatchInfo> _zkConnectionInfo = new ConcurrentHashMap<String, ZooKeeperConntrollerWatchInfo>(); private static BlurConfiguration _defaultBlurConfiguration; public static Iface getClient() { try { return getClient(getBlurConfiguration()); } catch (IOException e) { throw new RuntimeException("Unable to load configurations.", e); } } private static synchronized BlurConfiguration getBlurConfiguration() throws IOException { if (_defaultBlurConfiguration == null) { _defaultBlurConfiguration = new BlurConfiguration(); } return _defaultBlurConfiguration; } public static Iface getClient(BlurConfiguration conf) { return getClient(getOnlineControllers(conf)); } /** * Returns a client interface to Blur based on the connectionStr. * * <pre> * Blur.Iface client = Blur.getClient("controller1:40010,controller2:40010"); * </pre> * * The connectionStr also supports passing a proxy host/port (e.g. a SOCKS * proxy configuration): * * <pre> * Blur.Iface client = Blur.getClient("host1:port/proxyhost1:proxyport"); * </pre> * * @param connectionStr * - a comma-delimited list of host:port of Shard Controllers. * @return */ public static Iface getClient(String connectionStr) { List<Connection> connections = BlurClientManager.getConnections(connectionStr); return getClient(connections); } public static Iface getClient(String connectionStr, int maxRetries, long backOffTime, long maxBackOffTime) { List<Connection> connections = BlurClientManager.getConnections(connectionStr); return getClient(connections, maxRetries, backOffTime, maxBackOffTime); } public static Iface getClient(Connection connection) { return getClient(Arrays.asList(connection)); } public static Iface getClient(List<Connection> connections) { return (Iface) Proxy.newProxyInstance(Iface.class.getClassLoader(), new Class[] { Iface.class }, new BlurClientInvocationHandler(connections)); } public static Iface getClient(Connection connection, int maxRetries, long backOffTime, long maxBackOffTime) { return getClient(Arrays.asList(connection), maxRetries, backOffTime, maxBackOffTime); } public static Iface getClient(List<Connection> connections, int maxRetries, long backOffTime, long maxBackOffTime) { return (Iface) Proxy.newProxyInstance(Iface.class.getClassLoader(), new Class[] { Iface.class }, new BlurClientInvocationHandler(connections, maxRetries, backOffTime, maxBackOffTime)); } private static List<Connection> getOnlineControllers(BlurConfiguration conf) { String zooKeeperConnectionStr = getZooKeeperConnectionStr(conf); ZooKeeperConntrollerWatchInfo zooKeeperConntrollerWatchInfo = _zkConnectionInfo.get(zooKeeperConnectionStr); if (zooKeeperConntrollerWatchInfo != null) { return zooKeeperConntrollerWatchInfo._connections; } setupZooKeeper(conf); zooKeeperConntrollerWatchInfo = _zkConnectionInfo.get(zooKeeperConnectionStr); return zooKeeperConntrollerWatchInfo._connections; } private static String getZooKeeperConnectionStr(BlurConfiguration conf) { return conf.getExpected(BLUR_ZOOKEEPER_CONNECTION); } private static synchronized void setupZooKeeper(BlurConfiguration conf) { String zooKeeperConnectionStr = getZooKeeperConnectionStr(conf); ZooKeeperConntrollerWatchInfo zooKeeperConntrollerWatchInfo = _zkConnectionInfo.get(zooKeeperConnectionStr); if (zooKeeperConntrollerWatchInfo == null) { try { final ZooKeeperConntrollerWatchInfo zkcwi = new ZooKeeperConntrollerWatchInfo(conf); _zkConnectionInfo.put(zooKeeperConnectionStr, zkcwi); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { closeQuietly(zkcwi); } })); } catch (Exception e) { throw new RuntimeException("Unknown error while trying to connect to ZooKeeper and fetch controllers.", e); } } } public static void closeQuietly(Closeable closeable) { try { closeable.close(); } catch (IOException e) { LOG.error("Unknown error while trying to close [{0}]", e); } } public static void closeZooKeeper() { Collection<ZooKeeperConntrollerWatchInfo> values = _zkConnectionInfo.values(); for (ZooKeeperConntrollerWatchInfo zooKeeperConntrollerWatchInfo : values) { closeQuietly(zooKeeperConntrollerWatchInfo); } } public static Iface getClientFromZooKeeperConnectionStr(String zkConnectionString) { BlurConfiguration blurConfiguration; try { blurConfiguration = new BlurConfiguration(); } catch (IOException e) { throw new RuntimeException(e); } blurConfiguration.set(BLUR_ZOOKEEPER_CONNECTION, zkConnectionString); return getClient(blurConfiguration); } public static void init(BlurConfiguration configuration) { _defaultBlurConfiguration = configuration; ClientPool clientPool = BlurClientManager.getClientPool(); clientPool.setBlurConfiguration(_defaultBlurConfiguration); } }