package org.apache.helix.ui.util; /* * 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 com.google.common.cache.*; import org.apache.helix.manager.zk.ZNRecordSerializer; import org.apache.helix.manager.zk.ZkClient; import org.apache.helix.ui.api.ClusterConnection; import org.apache.zookeeper.ZooKeeper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import java.net.URLDecoder; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; public class ClientCache { private static final Logger LOG = LoggerFactory.getLogger(ClientCache.class); private static final int DEFAULT_SESSION_TIMEOUT_MILLIS = 5000; private static final int DEFAULT_CONNECTION_TIMEOUT_MILLIS = 5000; private final ZkAddressValidator zkAddressValidator; private final Map<String, String> zkAliases; public ClientCache(ZkAddressValidator zkAddressValidator, Map<String, String> zkAliases) { this.zkAddressValidator = zkAddressValidator; this.zkAliases = zkAliases; } // Manages and caches lifecycle of connections to ZK final LoadingCache<String, ClusterConnection> clientCache = CacheBuilder.newBuilder() .maximumSize(3) .expireAfterAccess(5, TimeUnit.MINUTES) .removalListener(new RemovalListener<String, ClusterConnection>() { @Override public void onRemoval(RemovalNotification<String, ClusterConnection> removalNotification) { if (removalNotification.getValue() != null) { ZkClient zkClient = removalNotification.getValue().getZkClient(); if (zkClient != null) { zkClient.close(); LOG.info("Disconnected from {}", removalNotification.getKey()); } } } }) .build(new CacheLoader<String, ClusterConnection>() { @Override public ClusterConnection load(String zkAddress) throws Exception { ZkClient zkClient = new ZkClient( zkAddress, DEFAULT_SESSION_TIMEOUT_MILLIS, DEFAULT_CONNECTION_TIMEOUT_MILLIS, new ZNRecordSerializer()); zkClient.waitUntilConnected(); LOG.info("Connected to {}", zkAddress); return new ClusterConnection(zkClient); } }); public ClusterConnection get(String zkAddress) { // Map alias if exists if (zkAliases.containsKey(zkAddress)) { zkAddress = zkAliases.get(zkAddress); } try { zkAddress = URLDecoder.decode(zkAddress, "UTF-8"); } catch (Exception e) { throw new IllegalArgumentException(e); } if (!zkAddressValidator.validate(zkAddress)) { throw new WebApplicationException("Cannot access " + zkAddress, Response.Status.UNAUTHORIZED); } ClusterConnection clusterConnection; try { clusterConnection = clientCache.get(zkAddress); } catch (Exception e) { throw new WebApplicationException(e, Response.Status.GATEWAY_TIMEOUT); } if (!clusterConnection.getZkClient().getConnection().getZookeeperState().equals(ZooKeeper.States.CONNECTED)) { clientCache.invalidate(zkAddress); throw new WebApplicationException("ZooKeeper connection was dead", Response.Status.GATEWAY_TIMEOUT); } return clusterConnection; } public void invalidateAll() { clientCache.invalidateAll(); } public Set<String> getDeadConnections() { Set<String> deadConnections = new HashSet<String>(); for (Map.Entry<String, ClusterConnection> entry : clientCache.asMap().entrySet()) { if (!entry.getValue().getZkClient().getConnection().getZookeeperState().isAlive()) { deadConnections.add(entry.getKey()); } } return deadConnections; } }