package rocks.inspectit.server.messaging; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import rocks.inspectit.server.ci.event.ClassInstrumentationChangedEvent; import rocks.inspectit.server.event.AgentDeletedEvent; import rocks.inspectit.server.event.AgentRegisteredEvent; import rocks.inspectit.server.util.AgentStatusDataProvider; import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData; import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData.InstrumentationStatus; import rocks.inspectit.shared.all.communication.message.UpdatedInstrumentationMessage; import rocks.inspectit.shared.all.instrumentation.config.impl.InstrumentationDefinition; import rocks.inspectit.shared.all.spring.logger.Log; /** * This class stores the changed, updated, removed or added {@link InstrumentationDefinition} which * should be send to the agent in the future. * * @author Marius Oehler * */ @Component public class AgentInstrumentationMessageGate implements ApplicationListener<ApplicationEvent> { /** * Logger of this class. */ @Log private Logger log; /** * The {@link AgentMessageProvider}. */ @Autowired private AgentMessageProvider messageProvider; /** * The {@link AgentStatusDataProvider}. */ @Autowired private AgentStatusDataProvider agentStatusDataProvider; /** * Map which maps agent IDs to a map. The inner map maps a class name to the latest * {@link InstrumentationDefinition}. */ private final Map<Long, Map<String, InstrumentationDefinition>> definitionBuffer = new HashMap<>(); /** * {@inheritDoc} */ @Override public synchronized void onApplicationEvent(ApplicationEvent event) { if (event instanceof ClassInstrumentationChangedEvent) { handleClassInstrumentationChangedEvent((ClassInstrumentationChangedEvent) event); } else if (event instanceof AgentDeletedEvent) { handleAgentDeletedEvent((AgentDeletedEvent) event); } else if (event instanceof AgentRegisteredEvent) { handleAgentRegisteredEvent((AgentRegisteredEvent) event); } } /** * Handles an event of type {@link AgentRegisteredEvent}. * * @param event * the event instance */ private void handleAgentRegisteredEvent(AgentRegisteredEvent event) { clear(event.getPlatformId()); } /** * Handles an event of type {@link AgentDeletedEvent}. * * @param event * the event instance */ private void handleAgentDeletedEvent(AgentDeletedEvent event) { clear(event.getPlatformId()); } /** * Handles an event of type {@link ClassInstrumentationChangedEvent}. * * @param event * the event instance */ private void handleClassInstrumentationChangedEvent(ClassInstrumentationChangedEvent event) { if (log.isDebugEnabled()) { log.debug("Putting instrumentation definitions for agent {} into the definition buffer.", event.getAgentId()); } Map<String, InstrumentationDefinition> pendingDefinitions = definitionBuffer.get(event.getAgentId()); if (pendingDefinitions == null) { pendingDefinitions = new HashMap<>(); definitionBuffer.put(event.getAgentId(), pendingDefinitions); } for (InstrumentationDefinition definition : event.getInstrumentationDefinitions()) { pendingDefinitions.put(definition.getClassName(), definition); } AgentStatusData agentStatusData = agentStatusDataProvider.getAgentStatusDataMap().get(event.getAgentId()); if (agentStatusData != null) { if (agentStatusData.getInstrumentationStatus() != InstrumentationStatus.PENDING) { agentStatusData.setInstrumentationStatus(InstrumentationStatus.PENDING); agentStatusData.setPendingSinceTime(System.currentTimeMillis()); } } } /** * Creates an {@link UpdatedInstrumentationMessage} which contains all stored * {@link InstrumentationDefinition}. The created message is put in the message provider for the * agent to fetch. * * @param platformId * the id of the platform which {@link InstrumentationDefinition}s should be provided * for fetching */ public synchronized void flush(long platformId) { if (log.isDebugEnabled()) { log.debug("Flushing new instrumentations for agent {}.", platformId); } Map<String, InstrumentationDefinition> pendingDefinitions = definitionBuffer.put(platformId, new HashMap<String, InstrumentationDefinition>()); if (MapUtils.isNotEmpty(pendingDefinitions)) { UpdatedInstrumentationMessage message = new UpdatedInstrumentationMessage(); message.getMessageContent().addAll(pendingDefinitions.values()); messageProvider.provideMessage(platformId, message); } AgentStatusData agentStatusData = agentStatusDataProvider.getAgentStatusDataMap().get(platformId); if (agentStatusData != null) { agentStatusData.setInstrumentationStatus(InstrumentationStatus.UP_TO_DATE); } } /** * Removes all {@link InstrumentationDefinition} which have been stored to send to the agent in * a later point of time. * * @param platformId * the id of the platform which {@link InstrumentationDefinition}s should be removed */ public synchronized void clear(long platformId) { Map<String, InstrumentationDefinition> pendingDefinitions = definitionBuffer.get(platformId); if (MapUtils.isNotEmpty(pendingDefinitions)) { if (log.isDebugEnabled()) { log.debug("Clearing stored instrumentations for agent {}.", platformId); } pendingDefinitions.clear(); } } }