package rocks.inspectit.server.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import rocks.inspectit.server.event.AgentDeletedEvent;
import rocks.inspectit.shared.all.cmr.service.IKeepAliveService;
import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData;
import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData.AgentConnection;
import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData.InstrumentationStatus;
import rocks.inspectit.shared.all.spring.logger.Log;
/**
* Bean that saves the time when the last time platform ident received the data.
*
* @author Ivan Senic
*
*/
@Component
public class AgentStatusDataProvider implements InitializingBean, ApplicationListener<AgentDeletedEvent> {
/**
* Runnable for checking the status of the received keep-alive signals.
*/
private final Runnable keepAliveCheckRunner = new Runnable() {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
for (Entry<Long, AgentStatusData> entry : agentStatusDataMap.entrySet()) {
if (entry.getValue().getAgentConnection() != AgentConnection.CONNECTED) {
continue;
}
long timeToLastSignal = currentTime - entry.getValue().getLastKeepAliveTimestamp();
if (timeToLastSignal > IKeepAliveService.KA_TIMEOUT) {
registerKeepAliveTimeout(entry.getKey());
if (log.isInfoEnabled()) {
log.info("Platform " + entry.getKey() + " timed out.");
}
}
}
}
};
/**
* Logger for the class.
*/
@Log
Logger log;
/**
* {@link ExecutorService} for sending keep alive messages.
*/
@Autowired
@Resource(name = "scheduledExecutorService")
ScheduledExecutorService executorService;
/**
* Map that holds IDs of the platform idents and {@link AgentStatusData} objects.
*/
private final ConcurrentHashMap<Long, AgentStatusData> agentStatusDataMap = new ConcurrentHashMap<>(8, 0.75f, 1);
/**
* {@inheritDoc}
*/
@Override
public void onApplicationEvent(AgentDeletedEvent event) {
agentStatusDataMap.remove(event.getPlatformId());
}
/**
* Registers that the agent was connected.
*
* @param platformIdent
* ID of the platform ident.
*/
public void registerConnected(long platformIdent) {
AgentStatusData agentStatusData = agentStatusDataMap.get(platformIdent);
if (null == agentStatusData) {
agentStatusData = new AgentStatusData(AgentConnection.CONNECTED);
AgentStatusData existing = agentStatusDataMap.putIfAbsent(platformIdent, agentStatusData);
if (null != existing) {
agentStatusData = existing;
}
}
long currentTimeMillis = System.currentTimeMillis();
agentStatusData.setLastKeepAliveTimestamp(currentTimeMillis);
agentStatusData.setConnectionTimestamp(currentTimeMillis);
agentStatusData.setAgentConnection(AgentConnection.CONNECTED);
agentStatusData.setPendingSinceTime(currentTimeMillis);
// set instrumentation status up-to-date
agentStatusData.setInstrumentationStatus(InstrumentationStatus.UP_TO_DATE);
}
/**
* Registers that the agent has been disconnected.
*
* @param platformIdent
* ID of the platform ident.
* @return Returns <code>true</code> if the agent has been marked as disconnected,
* <code>false</code> if the agent with given ID does not exist.
*/
public boolean registerDisconnected(long platformIdent) {
AgentStatusData agentStatusData = agentStatusDataMap.get(platformIdent);
if (null != agentStatusData) {
agentStatusData.setAgentConnection(AgentConnection.DISCONNECTED);
return true;
} else {
return false;
}
}
/**
* Registers the time when last data was received for a given platform ident.
*
* @param platformIdent
* ID of the platform ident.
*/
public void registerDataSent(long platformIdent) {
AgentStatusData agentStatusData = agentStatusDataMap.get(platformIdent);
if (null != agentStatusData) {
agentStatusData.setLastDataSendTimestamp(System.currentTimeMillis());
}
}
/**
* Registers the time when the last keep-alive was received for a given platform ident.
*
* @param platformIdent
* ID of the platform ident.
*/
public void handleKeepAliveSignal(long platformIdent) {
AgentStatusData agentStatusData = agentStatusDataMap.get(platformIdent);
if (null != agentStatusData) {
agentStatusData.setLastKeepAliveTimestamp(System.currentTimeMillis());
// Updates the agent status if no keep-alive messages were received before
if (agentStatusData.getAgentConnection() == AgentConnection.NO_KEEP_ALIVE) {
agentStatusData.setAgentConnection(AgentConnection.CONNECTED);
if (log.isInfoEnabled()) {
log.info("Platform " + platformIdent + " sending keep-alive signals again.");
}
}
}
}
/**
* Registers that the agent is not sending keep-alive messages anymore.
*
* @param platformIdent
* ID of the platform ident.
*/
public void registerKeepAliveTimeout(long platformIdent) {
AgentStatusData agentStatusData = agentStatusDataMap.get(platformIdent);
if (null != agentStatusData) {
agentStatusData.setAgentConnection(AgentConnection.NO_KEEP_ALIVE);
}
}
/**
* @return Returns the map of platform ident IDs and dates when the last data was received.
*/
public Map<Long, AgentStatusData> getAgentStatusDataMap() {
long currentTime = System.currentTimeMillis();
Map<Long, AgentStatusData> map = new HashMap<>();
for (Entry<Long, AgentStatusData> entry : agentStatusDataMap.entrySet()) {
entry.getValue().setServerTimestamp(currentTime);
map.put(entry.getKey(), entry.getValue());
}
return map;
}
/**
* {@inheritDoc}
*
* Starts the continuous check of the keep-alive signals.
*/
@Override
public void afterPropertiesSet() throws Exception {
executorService.scheduleAtFixedRate(keepAliveCheckRunner, IKeepAliveService.KA_INITIAL_DELAY, IKeepAliveService.KA_TIMEOUT, TimeUnit.MILLISECONDS);
}
}