/** * Copyright 2014 The CmRaft Project * * 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. */ package com.chicm.cmraft; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.chicm.cmraft.common.CmRaftConfiguration; import com.chicm.cmraft.common.Configuration; import com.chicm.cmraft.common.ServerInfo; import com.chicm.cmraft.core.ClusterMemberManager; import com.chicm.cmraft.protobuf.generated.RaftProtos.DeleteRequest; import com.chicm.cmraft.protobuf.generated.RaftProtos.DeleteResponse; import com.chicm.cmraft.protobuf.generated.RaftProtos.GetRequest; import com.chicm.cmraft.protobuf.generated.RaftProtos.GetResponse; import com.chicm.cmraft.protobuf.generated.RaftProtos.KeyValuePair; import com.chicm.cmraft.protobuf.generated.RaftProtos.ListRequest; import com.chicm.cmraft.protobuf.generated.RaftProtos.ListResponse; import com.chicm.cmraft.protobuf.generated.RaftProtos.LookupLeaderRequest; import com.chicm.cmraft.protobuf.generated.RaftProtos.LookupLeaderResponse; import com.chicm.cmraft.protobuf.generated.RaftProtos.SetRequest; import com.chicm.cmraft.protobuf.generated.RaftProtos.SetResponse; import com.chicm.cmraft.rpc.RpcClient; import com.google.common.base.Preconditions; import com.google.protobuf.ByteString; import com.google.protobuf.ServiceException; public class ConnectionManager { static final Log LOG = LogFactory.getLog(ConnectionManager.class); private RpcClient rpcClient; private ConnectionImpl userConnection; private KeyValueStore kvs; private ServerInfo remoteServer; private ConnectionManager(Configuration conf, Set<ServerInfo> servers) { Preconditions.checkNotNull(servers); Preconditions.checkArgument(!servers.isEmpty()); boolean connected = false; LOG.info("Raft servers:" + servers); for(ServerInfo server: servers) { LOG.info("Trying connect to:" + server); try { RpcClient rpc = new RpcClient(conf, server); if(rpc.connect()) { rpcClient = rpc; remoteServer = server; connected = true; break; } } catch(Exception e) { LOG.error("Failed connecting to:" + server, e); } } if(!connected) { String msg = "Could not connect to all raft servers"; LOG.error(msg); RuntimeException e = new RuntimeException(msg); throw e; } else { LOG.info("Connected to: " + remoteServer); } connected = false; ServerInfo leader = lookupLeader(); LOG.info("lookupLeader:" + leader); if(leader != null && !leader.equals(remoteServer)) { LOG.info("closing connection to " + remoteServer + ", connecting to " + leader); try { rpcClient.close(); RpcClient rpcLeader = new RpcClient(conf, leader); if(rpcLeader.connect()) { rpcClient = rpcLeader; remoteServer = leader; connected = true; } } catch(Exception e) { LOG.error("Failed connecting to:" + leader, e); } } else if(leader != null && leader.equals(remoteServer)) { LOG.info("This connected server is leader:" + leader); connected = true; } else if(leader == null) { LOG.error("Could not get Leader info"); } if(!connected) { RuntimeException e = new RuntimeException("Could not connect to leader:" + leader); throw e; } Preconditions.checkArgument(rpcClient.isConnected()); userConnection = new ConnectionImpl(); kvs = new KeyValueStoreImpl(); } public ServerInfo getRemoteServer() { return remoteServer; } public static Connection getConnection() { return getConnection(CmRaftConfiguration.create()); } public static Connection getConnection(Configuration conf) { ConnectionManager mgr = new ConnectionManager(conf, ClusterMemberManager.getRaftServers(conf)); return mgr.userConnection; } public void close() { rpcClient.close(); } private ServerInfo lookupLeader() { LookupLeaderRequest.Builder builder = LookupLeaderRequest.newBuilder(); try { LookupLeaderResponse response = rpcClient.getStub().lookupLeader(null, builder.build()); if(response.getSuccess()) { return ServerInfo.copyFrom(response.getLeader()); } } catch(Exception e) { LOG.error("lookupLeader failed:" + e.getMessage(), e); } LOG.error("lookupLeader returned null"); return null; } private class ConnectionImpl implements Connection { @Override public KeyValueStore getKeyValueStore() { return kvs; } @Override public void close() { rpcClient.close(); } } private class KeyValueStoreImpl implements KeyValueStore { @Override public boolean set(byte[] key, byte[] value) { Preconditions.checkNotNull(key); Preconditions.checkArgument(key.length > 0); Preconditions.checkNotNull(value); Preconditions.checkArgument(value.length > 0); KeyValuePair.Builder kvBuilder = KeyValuePair.newBuilder(); kvBuilder.setKey(ByteString.copyFrom(key)); kvBuilder.setValue(ByteString.copyFrom(value)); SetRequest.Builder builder = SetRequest.newBuilder(); builder.setKv(kvBuilder.build()); try { SetResponse response = rpcClient.getStub().set(null, builder.build()); if(response != null && response.getSuccess()) return true; } catch(Exception e) { LOG.error("set failed:" + e.getMessage()); } return false; } @Override public boolean set(String key, String value) { Preconditions.checkNotNull(key); Preconditions.checkNotNull(value); return set(key.getBytes(), value.getBytes()); } @Override public byte[] get(byte[] key) { Preconditions.checkNotNull(key); GetRequest.Builder builder = GetRequest.newBuilder(); builder.setKey(ByteString.copyFrom(key)); try { GetResponse response = rpcClient.getStub().get(null, builder.build()); if(response.getSuccess()) { return response.getValue().toByteArray(); } else { return null; } } catch (ServiceException e) { LOG.error("Get exception,", e); } catch (Exception e) { LOG.error("Get exception,", e); } return null; } @Override public String get(String key) { Preconditions.checkNotNull(key); Preconditions.checkArgument(!key.isEmpty()); byte[] result = get(key.getBytes()); return result==null ? null : new String(result) ; } @Override public boolean delete(byte[] key) { Preconditions.checkNotNull(key); Preconditions.checkArgument(key.length > 0); DeleteRequest.Builder builder = DeleteRequest.newBuilder(); builder.setKey(ByteString.copyFrom(key)); try { DeleteResponse response = rpcClient.getStub().delete(null, builder.build()); return response.getSuccess(); } catch (ServiceException e) { LOG.error("Delete exception,", e); } catch (Exception e) { LOG.error("Delete exception,", e); } return false; } @Override public boolean delete(String key) { return delete(key.getBytes()); } @Override public List<KeyValue> list(byte[] pattern) { ListRequest.Builder builder = ListRequest.newBuilder(); if(pattern != null) { builder.setPattern(ByteString.copyFrom(pattern)); } List<KeyValue> result = new ArrayList<>(); try { ListResponse response = rpcClient.getStub().list(null, builder.build()); if(response != null && response.getSuccess()) { for(KeyValuePair kvp: response.getResultsList()) { KeyValue kv = KeyValue.copyFrom(kvp); result.add(kv); } } } catch(Exception e) { LOG.error("list failed:" + e.getMessage()); } return result; } @Override public List<KeyValue> list(String pattern) { if(pattern != null) return list(pattern.getBytes()); else return list("".getBytes()); } } }