package rocks.inspectit.server.messaging; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.CollectionUtils; 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.event.AgentDeletedEvent; import rocks.inspectit.server.event.AgentRegisteredEvent; import rocks.inspectit.shared.all.communication.message.IAgentMessage; 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; import rocks.inspectit.shared.cs.cmr.service.IRegistrationService; /** * Provides {@link IAgentMessage} for the agent to fetch. * * @author Marius Oehler * */ @Component public class AgentMessageProvider implements ApplicationListener<ApplicationEvent> { /** * Logger of this class. */ @Log private Logger log; /** * The registration service. */ @Autowired private IRegistrationService registrationService; /** * Map containing messages which can be fetched by the agent. */ private final Map<Long, List<IAgentMessage<?>>> agentMessageMap = new HashMap<>(); /** * Fetches all available messages for the agent with the given id. The returned list is an * ordered list, ordered by time (ascending -> index 0 is the oldest). * * @param platformId * the agent id * @return {@link List} of {@link IAgentMessage}s. */ public synchronized List<IAgentMessage<?>> fetchMessages(long platformId) { if (log.isTraceEnabled()) { log.trace("Fetch messages for agent {}.", platformId); } List<IAgentMessage<?>> currentList = agentMessageMap.put(platformId, new ArrayList<IAgentMessage<?>>()); if (CollectionUtils.isEmpty(currentList)) { currentList = Collections.emptyList(); } // update timestamp of method idents (resulting in a disabled method ident) updateMethodIdentTimestamps(platformId, currentList); return currentList; } /** * Updates the timestamp of all method idents matching the {@link InstrumentationDefinition}s in * the given message. * * @param platformId * the platform id * @param messages * all agent messages */ private void updateMethodIdentTimestamps(long platformId, List<IAgentMessage<?>> messages) { for (IAgentMessage<?> agentMessage : messages) { if (agentMessage instanceof UpdatedInstrumentationMessage) { UpdatedInstrumentationMessage message = (UpdatedInstrumentationMessage) agentMessage; for (InstrumentationDefinition definition : message.getMessageContent()) { String fqn = definition.getClassName(); int index = fqn.lastIndexOf('.'); String packageName = fqn.substring(0, index); String className = fqn.substring(index + 1, fqn.length()); registrationService.updateMethodIdentTimestamp(platformId, packageName, className); } } } } /** * Puts the given {@link IAgentMessage} in the list which can be fetched by the agent. * * @param platformId * the id of the agent to receive the message * @param message * the {@link IAgentMessage} */ public synchronized void provideMessage(long platformId, IAgentMessage<?> message) { if (message == null) { throw new IllegalArgumentException("The agent message may not be null."); } List<IAgentMessage<?>> messageList = Arrays.<IAgentMessage<?>> asList(message); provideMessages(platformId, messageList); } /** * Puts the given {@link Collection} of {@link IAgentMessage}s in the list which can be fetched * by the agent. * * @param platformId * the id of the agent to receive the message * @param messages * the {@link Collection} of {@link IAgentMessage}s */ public synchronized void provideMessages(long platformId, Collection<IAgentMessage<?>> messages) { if (CollectionUtils.isEmpty(messages)) { throw new IllegalArgumentException("The agent messages may not be null or empty."); } if (log.isDebugEnabled()) { log.debug("Provide new messages for agent {}.", platformId); } List<IAgentMessage<?>> messageList = agentMessageMap.get(platformId); if (messageList == null) { messageList = new ArrayList<>(); agentMessageMap.put(platformId, messageList); } messageList.addAll(messages); } /** * Removes all available messages of the agent with the given ID. * * @param platformId * id of the agent which messages should be removed */ public synchronized void clear(long platformId) { List<IAgentMessage<?>> messages = agentMessageMap.get(platformId); if (CollectionUtils.isNotEmpty(messages)) { if (log.isDebugEnabled()) { log.debug("Clearing messages of agent {}.", platformId); } messages.clear(); } } /** * {@inheritDoc} */ @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof AgentDeletedEvent) { handleAgentDeletedEvent((AgentDeletedEvent) event); } else if (event instanceof AgentRegisteredEvent) { handleAgentRegisteredEvent((AgentRegisteredEvent) event); } } /** * 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 AgentRegisteredEvent}. * * @param event * the event instance */ private void handleAgentRegisteredEvent(AgentRegisteredEvent event) { clear(event.getPlatformId()); } }