/*
* 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.web.websocket;
import com.navercorp.pinpoint.common.server.util.AgentLifeCycleState;
import com.navercorp.pinpoint.web.service.AgentService;
import com.navercorp.pinpoint.web.vo.AgentInfo;
import com.navercorp.pinpoint.web.vo.AgentStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @Author Taejin Koo
*/
public class WorkerActiveManager {
private static final long DEFAULT_RECONNECT_DELAY = 5000;
private static final long DEFAULT_AGENT_CHECk_DELAY = 10000;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final PinpointWebSocketResponseAggregator responseAggregator;
private final String applicationName;
private final AgentService agentService;
private final Timer timer;
private final AtomicBoolean isStopped = new AtomicBoolean();
private final Object lock = new Object();
private final AtomicBoolean onReconnectTimerTask = new AtomicBoolean(false);
private final Set<String> reactiveWorkerRepository = new CopyOnWriteArraySet<>();
private final AtomicBoolean onAgentCheckTimerTask = new AtomicBoolean(false);
private final List<String> defaultAgentIdList = new CopyOnWriteArrayList<>();
public WorkerActiveManager(PinpointWebSocketResponseAggregator responseAggregator, AgentService agentService, Timer timer) {
this.responseAggregator = responseAggregator;
this.applicationName = responseAggregator.getApplicationName();
this.agentService = agentService;
this.timer = timer;
}
public void close() {
synchronized (lock) {
isStopped.compareAndSet(false, true);
onReconnectTimerTask.set(false);
reactiveWorkerRepository.clear();
onAgentCheckTimerTask.set(false);
defaultAgentIdList.clear();
}
}
public void addReactiveWorker(AgentInfo agentInfo) {
if (applicationName.equals(agentInfo.getApplicationName())) {
addReactiveWorker(agentInfo.getAgentId());
}
}
public void addReactiveWorker(String agentId) {
logger.info("addReactiveWorker. applicationName:{}, agent:{}", applicationName, agentId);
synchronized (lock) {
if (isStopped.get()) {
return;
}
reactiveWorkerRepository.add(agentId);
boolean turnOn = onReconnectTimerTask.compareAndSet(false, true);
logger.info("addReactiveWorker turnOn:{}", turnOn);
if (turnOn) {
timer.schedule(new ReactiveTimerTask(), DEFAULT_RECONNECT_DELAY);
}
}
}
public void startAgentCheckJob() {
logger.info("startAgentCheckJob. applicationName:{}", applicationName);
boolean turnOn = onAgentCheckTimerTask.compareAndSet(false, true);
if (turnOn) {
timer.schedule(new AgentCheckTimerTask(), DEFAULT_AGENT_CHECk_DELAY);
}
}
private class ReactiveTimerTask extends TimerTask {
@Override
public void run() {
logger.info("ReactiveTimerTask started.");
Set<String> reactiveWorkerCandidates = new HashSet<>(reactiveWorkerRepository.size());
synchronized (lock) {
reactiveWorkerCandidates.addAll(reactiveWorkerRepository);
reactiveWorkerRepository.clear();
boolean turnOff = onReconnectTimerTask.compareAndSet(true, false);
}
for (String agentId : reactiveWorkerCandidates) {
try {
AgentInfo newAgentInfo = agentService.getAgentInfo(applicationName, agentId);
if (newAgentInfo != null) {
responseAggregator.addActiveWorker(newAgentInfo);
}
} catch (Exception e) {
logger.warn("failed while to get AgentInfo(applicationName:{}, agentId:{}). error:{}.", applicationName, agentId, e.getMessage(), e);
}
}
}
}
private class AgentCheckTimerTask extends TimerTask {
@Override
public void run() {
logger.info("AgentCheckTimerTask started.");
List<AgentInfo> agentInfoList = Collections.emptyList();
try {
agentInfoList = agentService.getRecentAgentInfoList(applicationName);
} catch (Exception e) {
logger.warn("failed while to get RecentAgentInfoList(applicationName:{}). error:{}.", applicationName, e.getMessage(), e);
}
try {
for (AgentInfo agentInfo : agentInfoList) {
String agentId = agentInfo.getAgentId();
if (defaultAgentIdList.contains(agentId)) {
continue;
}
AgentStatus agentStatus = agentInfo.getStatus();
if (agentStatus != null && agentStatus.getState() != AgentLifeCycleState.UNKNOWN) {
addActiveWorker(agentInfo);
} else if (agentService.isConnected(agentInfo)) {
addActiveWorker(agentInfo);
}
}
} finally {
if (timer != null && onAgentCheckTimerTask.get() && !isStopped.get()) {
timer.schedule(new AgentCheckTimerTask(), DEFAULT_AGENT_CHECk_DELAY);
}
}
}
private void addActiveWorker(AgentInfo agentInfo) {
try {
responseAggregator.addActiveWorker(agentInfo);
defaultAgentIdList.add(agentInfo.getAgentId());
} catch (Exception e) {
logger.warn("failed while adding active worker. error:{}", e.getMessage(), e);
}
}
}
}