/hazelcast-client/src/main/java/com/hazelcast/client/ConnectionManager.java

https://bitbucket.org/gabral6_gmailcom/hazelcast · Java · 333 lines · 286 code · 30 blank · 17 comment · 53 complexity · 4b739df0f3014d76d8c9781f730f0d25 MD5 · raw file

  1. /*
  2. * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.hazelcast.client;
  17. import com.hazelcast.core.Member;
  18. import com.hazelcast.core.MembershipEvent;
  19. import com.hazelcast.core.MembershipListener;
  20. import com.hazelcast.logging.ILogger;
  21. import com.hazelcast.logging.Logger;
  22. import com.hazelcast.nio.SocketInterceptor;
  23. import com.hazelcast.util.Clock;
  24. import java.io.IOException;
  25. import java.net.InetSocketAddress;
  26. import java.util.*;
  27. import java.util.concurrent.CopyOnWriteArrayList;
  28. import java.util.concurrent.CountDownLatch;
  29. import java.util.concurrent.TimeUnit;
  30. import java.util.concurrent.atomic.AtomicInteger;
  31. import java.util.logging.Level;
  32. import static com.hazelcast.core.LifecycleEvent.LifecycleState.*;
  33. import static java.text.MessageFormat.format;
  34. public class ConnectionManager implements MembershipListener {
  35. private volatile Connection currentConnection;
  36. private final AtomicInteger connectionIdGenerator = new AtomicInteger(-1);
  37. private final List<InetSocketAddress> clusterMembers = new CopyOnWriteArrayList<InetSocketAddress>();
  38. private final List<InetSocketAddress> initialClusterMembers = new CopyOnWriteArrayList<InetSocketAddress>();
  39. private final ILogger logger = Logger.getLogger(getClass().getName());
  40. private final HazelcastClient client;
  41. private volatile int lastDisconnectedConnectionId = -1;
  42. private ClientBinder binder;
  43. private volatile boolean lookingForLiveConnection = false;
  44. private volatile boolean running = true;
  45. private final LifecycleServiceClientImpl lifecycleService;
  46. final Timer heartbeatTimer = new Timer();
  47. private final ClientConfig config;
  48. public ConnectionManager(HazelcastClient client, ClientConfig config, LifecycleServiceClientImpl lifecycleService) {
  49. this.config = config;
  50. this.client = client;
  51. this.lifecycleService = lifecycleService;
  52. this.clusterMembers.addAll(config.getAddressList());
  53. this.initialClusterMembers.addAll(config.getAddressList());
  54. if (config.isShuffle()) {
  55. Collections.shuffle(this.clusterMembers);
  56. Collections.shuffle(this.initialClusterMembers);
  57. }
  58. }
  59. void scheduleHeartbeatTimerTask() {
  60. final int TIMEOUT = config.getConnectionTimeout();
  61. heartbeatTimer.schedule(new TimerTask() {
  62. @Override
  63. public void run() {
  64. long diff = Clock.currentTimeMillis() - client.getInRunnable().lastReceived;
  65. try {
  66. if (diff >= TIMEOUT / 5 && diff < TIMEOUT) {
  67. logger.log(Level.FINEST,
  68. "Being idle for some time, Doing a getMembers() call to ping the server!");
  69. final CountDownLatch latch = new CountDownLatch(1);
  70. new Thread(new Runnable() {
  71. public void run() {
  72. Set<Member> members = client.getCluster().getMembers();
  73. if (members != null && members.size() >= 1) {
  74. latch.countDown();
  75. }
  76. }
  77. }).start();
  78. if (!latch.await(10000, TimeUnit.MILLISECONDS)) {
  79. logger.log(Level.WARNING, "Server didn't respond to client's ping call within 10 seconds!");
  80. }
  81. } else if (diff >= TIMEOUT) {
  82. logger.log(Level.WARNING, "Server didn't respond to client's requests for " + TIMEOUT / 1000 +
  83. " seconds. Assuming it is dead, closing the connection!");
  84. currentConnection.close();
  85. }
  86. } catch (InterruptedException e) {
  87. return;
  88. } catch (IOException ignored) {
  89. }
  90. }
  91. }, TIMEOUT, TIMEOUT / 10);
  92. }
  93. public Connection getInitConnection() throws IOException {
  94. if (currentConnection == null) {
  95. synchronized (this) {
  96. currentConnection = lookForLiveConnection(config.getInitialConnectionAttemptLimit(),
  97. config.getReConnectionTimeOut());
  98. }
  99. }
  100. return currentConnection;
  101. }
  102. public Connection lookForLiveConnection() throws IOException {
  103. return lookForLiveConnection(config.getReconnectionAttemptLimit(), config.getReConnectionTimeOut());
  104. }
  105. private Connection lookForLiveConnection(final int attemptsLimit,
  106. final int reconnectionTimeout) throws IOException {
  107. lookingForLiveConnection = true;
  108. try {
  109. boolean restored = false;
  110. int attempt = 0;
  111. while (currentConnection == null && running && !Thread.interrupted()) {
  112. final long next = Clock.currentTimeMillis() + reconnectionTimeout;
  113. synchronized (this) {
  114. if (currentConnection == null) {
  115. final Connection connection = searchForAvailableConnection();
  116. restored = connection != null;
  117. if (restored) {
  118. try {
  119. ClientConfig clientConfig = client.getClientConfig();
  120. if (clientConfig != null) {
  121. SocketInterceptor socketInterceptor = clientConfig.getSocketInterceptor();
  122. if (socketInterceptor != null) {
  123. socketInterceptor.onConnect(connection.getSocket());
  124. }
  125. }
  126. bindConnection(connection);
  127. currentConnection = connection;
  128. } catch (Throwable e) {
  129. closeConnection(connection);
  130. logger.log(Level.WARNING, "got an exception on getConnection:" + e.getMessage(), e);
  131. restored = false;
  132. }
  133. }
  134. }
  135. }
  136. if (currentConnection != null) {
  137. logger.log(Level.FINE, "Client is connecting to " + currentConnection);
  138. lookingForLiveConnection = false;
  139. break;
  140. }
  141. if (attempt >= attemptsLimit) {
  142. break;
  143. }
  144. attempt++;
  145. final long t = next - Clock.currentTimeMillis();
  146. logger.log(Level.INFO, format("Unable to get alive cluster connection," +
  147. " try in {0} ms later, attempt {1} of {2}.",
  148. Math.max(0, t), attempt, attemptsLimit));
  149. if (t > 0) {
  150. try {
  151. Thread.sleep(t);
  152. } catch (InterruptedException e) {
  153. break;
  154. }
  155. }
  156. }
  157. if (restored) {
  158. notifyConnectionIsRestored();
  159. }
  160. } finally {
  161. lookingForLiveConnection = false;
  162. }
  163. return currentConnection;
  164. }
  165. void closeConnection(final Connection connection) {
  166. try {
  167. if (connection != null) {
  168. connection.close();
  169. }
  170. } catch (Throwable e) {
  171. logger.log(Level.INFO, "got an exception on closeConnection "
  172. + connection + ":" + e.getMessage(), e);
  173. }
  174. }
  175. public Connection getConnection() throws IOException {
  176. if (currentConnection == null && running && !lookingForLiveConnection) {
  177. boolean restored = false;
  178. synchronized (this) {
  179. if (currentConnection == null) {
  180. Connection connection = searchForAvailableConnection();
  181. if (connection != null) {
  182. logger.log(Level.FINE, "Client is connecting to " + connection);
  183. try {
  184. bindConnection(connection);
  185. currentConnection = connection;
  186. } catch (Throwable e) {
  187. closeConnection(connection);
  188. logger.log(Level.WARNING, "got an exception on getConnection:" + e.getMessage(), e);
  189. }
  190. }
  191. restored = currentConnection != null;
  192. }
  193. }
  194. if (restored) {
  195. notifyConnectionIsRestored();
  196. }
  197. }
  198. return currentConnection;
  199. }
  200. void notifyConnectionIsRestored() {
  201. lifecycleService.fireLifecycleEvent(CLIENT_CONNECTION_OPENING);
  202. }
  203. void notifyConnectionIsOpened() {
  204. lifecycleService.fireLifecycleEvent(CLIENT_CONNECTION_OPENED);
  205. }
  206. void bindConnection(Connection connection) throws IOException {
  207. binder.bind(connection, config.getCredentials());
  208. }
  209. public void destroyConnection(final Connection connection) {
  210. boolean lost = false;
  211. synchronized (this) {
  212. if (currentConnection != null &&
  213. connection != null &&
  214. currentConnection.getVersion() == connection.getVersion()) {
  215. logger.log(Level.WARNING, "Connection to " + currentConnection + " is lost");
  216. // remove current connection's address from member list.
  217. // if address is IPv6 then remove all possible socket addresses (for all scopes)
  218. while (clusterMembers.remove(currentConnection.getAddress())) ;
  219. currentConnection = null;
  220. lost = true;
  221. try {
  222. connection.close();
  223. } catch (IOException e) {
  224. logger.log(Level.FINEST, e.getMessage(), e);
  225. }
  226. }
  227. }
  228. if (lost) {
  229. lifecycleService.fireLifecycleEvent(CLIENT_CONNECTION_LOST);
  230. }
  231. }
  232. private void popAndPush(List<InetSocketAddress> clusterMembers) {
  233. if (!clusterMembers.isEmpty()) {
  234. InetSocketAddress address = clusterMembers.remove(0);
  235. clusterMembers.add(address);
  236. }
  237. }
  238. private Connection searchForAvailableConnection() {
  239. Connection connection = null;
  240. popAndPush(clusterMembers);
  241. if(clusterMembers.isEmpty()){
  242. clusterMembers.addAll(initialClusterMembers);
  243. }
  244. int counter = clusterMembers.size();
  245. while (counter > 0) {
  246. try {
  247. connection = getNextConnection();
  248. break;
  249. } catch (Exception e) {
  250. logger.log(Level.FINEST, e.getMessage(), e);
  251. popAndPush(clusterMembers);
  252. counter--;
  253. }
  254. }
  255. logger.log(Level.FINEST, format("searchForAvailableConnection connection:{0}", connection));
  256. return connection;
  257. }
  258. protected Connection getNextConnection() {
  259. InetSocketAddress address = clusterMembers.get(0);
  260. return new Connection(config.getConnectionTimeout(), address, connectionIdGenerator.incrementAndGet());
  261. }
  262. public void memberAdded(MembershipEvent membershipEvent) {
  263. InetSocketAddress address = membershipEvent.getMember().getInetSocketAddress();
  264. Collection<InetSocketAddress> addresses = AddressHelper.getPossibleSocketAddresses(address.getAddress(),
  265. address.getPort());
  266. clusterMembers.addAll(addresses);
  267. initialClusterMembers.addAll(addresses);
  268. }
  269. public void memberRemoved(MembershipEvent membershipEvent) {
  270. InetSocketAddress address = membershipEvent.getMember().getInetSocketAddress();
  271. Collection<InetSocketAddress> addresses = AddressHelper.getPossibleSocketAddresses(address.getAddress(),
  272. address.getPort());
  273. clusterMembers.removeAll(addresses);
  274. }
  275. public void updateMembers() {
  276. Set<Member> members = client.getCluster().getMembers();
  277. clusterMembers.clear();
  278. for (Member member : members) {
  279. InetSocketAddress address = member.getInetSocketAddress();
  280. Collection<InetSocketAddress> addresses = AddressHelper.getPossibleSocketAddresses(address.getAddress(),
  281. address.getPort());
  282. clusterMembers.addAll(addresses);
  283. }
  284. }
  285. public boolean shouldExecuteOnDisconnect(Connection connection) {
  286. if (connection == null || lastDisconnectedConnectionId >= connection.getVersion()) {
  287. return false;
  288. }
  289. lastDisconnectedConnectionId = connection.getVersion();
  290. return true;
  291. }
  292. public void setBinder(ClientBinder binder) {
  293. this.binder = binder;
  294. }
  295. List<InetSocketAddress> getClusterMembers() {
  296. return clusterMembers;
  297. }
  298. public void shutdown() {
  299. logger.log(Level.INFO, getClass().getSimpleName() + " shutdown");
  300. running = false;
  301. heartbeatTimer.cancel();
  302. }
  303. }