/* * Copyright 2014 NAVER Corp. * * 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.navercorp.pinpoint.collector.cluster.zookeeper; import com.navercorp.pinpoint.collector.cluster.ClusterPointRepository; import com.navercorp.pinpoint.collector.cluster.PinpointServerClusterPoint; import com.navercorp.pinpoint.common.server.util.concurrent.CommonStateContext; import com.navercorp.pinpoint.rpc.common.SocketStateCode; import com.navercorp.pinpoint.rpc.packet.HandshakePropertyType; import com.navercorp.pinpoint.rpc.server.PinpointServer; import com.navercorp.pinpoint.rpc.server.handler.ServerStateChangeEventHandler; import com.navercorp.pinpoint.rpc.util.MapUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * @author Taejin Koo */ public class ZookeeperProfilerClusterManager implements ServerStateChangeEventHandler { private static final Charset charset = Charset.forName("UTF-8"); private static final String PROFILER_SEPARATOR = "\r\n"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final ZookeeperJobWorker worker; private final CommonStateContext workerState; private final ClusterPointRepository profileCluster; private final Object lock = new Object(); // keep it simple - register on RUN, remove on FINISHED, skip otherwise // should only be instantiated when cluster is enabled. public ZookeeperProfilerClusterManager(ZookeeperClient client, String serverIdentifier, ClusterPointRepository profileCluster) { this.workerState = new CommonStateContext(); this.profileCluster = profileCluster; this.worker = new ZookeeperJobWorker(client, serverIdentifier); } public void start() { switch (this.workerState.getCurrentState()) { case NEW: if (this.workerState.changeStateInitializing()) { logger.info("start() started."); worker.start(); workerState.changeStateStarted(); logger.info("start() completed."); break; } case INITIALIZING: logger.info("start() failed. caused:already initializing."); break; case STARTED: logger.info("start() failed. caused:already started."); break; case DESTROYING: throw new IllegalStateException("Already destroying."); case STOPPED: throw new IllegalStateException("Already stopped."); case ILLEGAL_STATE: throw new IllegalStateException("Invalid State."); } } public void stop() { if (!(this.workerState.changeStateDestroying())) { logger.info("stop() failed. caused:unexpected state."); return; } logger.info("stop() started."); worker.stop(); this.workerState.changeStateStopped(); logger.info("stop() completed."); } @Override public void eventPerformed(PinpointServer pinpointServer, SocketStateCode stateCode) { if (workerState.isStarted()) { logger.info("eventPerformed() started. (PinpointServer={}, State={})", pinpointServer, stateCode); Map agentProperties = pinpointServer.getChannelProperties(); // skip when applicationName and agentId is unknown if (skipAgent(agentProperties)) { return; } synchronized (lock) { if (SocketStateCode.RUN_DUPLEX == stateCode) { profileCluster.addClusterPoint(new PinpointServerClusterPoint(pinpointServer)); worker.addPinpointServer(pinpointServer); } else if (SocketStateCode.isClosed(stateCode)) { profileCluster.removeClusterPoint(new PinpointServerClusterPoint(pinpointServer)); worker.removePinpointServer(pinpointServer); } } } else { logger.info("eventPerformed() failed. caused:unexpected state."); } } @Override public void exceptionCaught(PinpointServer pinpointServer, SocketStateCode stateCode, Throwable e) { logger.warn("exceptionCaught(). (pinpointServer:{}, PinpointServerStateCode:{}). caused:{}.", pinpointServer, stateCode, e.getMessage(), e); } public List<String> getClusterData() { byte[] contents = worker.getClusterData(); if (contents == null) { return Collections.emptyList(); } String clusterData = new String(contents, charset); List<String> allClusterData = com.navercorp.pinpoint.common.util.StringUtils.tokenizeToStringList(clusterData, PROFILER_SEPARATOR); List<String> result = new ArrayList<>(allClusterData.size()); for (String eachClusterData : allClusterData) { if (!StringUtils.isBlank(eachClusterData)) { result.add(eachClusterData); } } return result; } public void initZookeeperClusterData() { worker.clear(); synchronized (lock) { List clusterPointList = profileCluster.getClusterPointList(); for (Object clusterPoint : clusterPointList) { if (clusterPoint instanceof PinpointServerClusterPoint) { PinpointServer pinpointServer = ((PinpointServerClusterPoint) clusterPoint).getPinpointServer(); if (SocketStateCode.isRunDuplex(pinpointServer.getCurrentStateCode())) { worker.addPinpointServer(pinpointServer); } } } } } private boolean skipAgent(Map<Object, Object> agentProperties) { String applicationName = MapUtils.getString(agentProperties, HandshakePropertyType.APPLICATION_NAME.getName()); String agentId = MapUtils.getString(agentProperties, HandshakePropertyType.AGENT_ID.getName()); if (StringUtils.isBlank(applicationName) || StringUtils.isBlank(agentId)) { return true; } return false; } }