package rocks.inspectit.server.instrumentation.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rocks.inspectit.server.ci.ConfigurationInterfaceManager;
import rocks.inspectit.server.instrumentation.config.applier.ExceptionSensorInstrumentationApplier;
import rocks.inspectit.server.instrumentation.config.applier.IInstrumentationApplier;
import rocks.inspectit.server.instrumentation.config.applier.JmxMonitoringApplier;
import rocks.inspectit.server.instrumentation.config.applier.MethodSensorInstrumentationApplier;
import rocks.inspectit.server.instrumentation.config.applier.SpecialInstrumentationApplier;
import rocks.inspectit.server.instrumentation.config.applier.TimerMethodSensorInstrumentationApplier;
import rocks.inspectit.shared.all.exception.BusinessException;
import rocks.inspectit.shared.all.exception.enumeration.ConfigurationInterfaceErrorCodeEnum;
import rocks.inspectit.shared.all.pattern.IMatchPattern;
import rocks.inspectit.shared.all.pattern.PatternFactory;
import rocks.inspectit.shared.all.spring.logger.Log;
import rocks.inspectit.shared.cs.ci.AgentMapping;
import rocks.inspectit.shared.cs.ci.AgentMappings;
import rocks.inspectit.shared.cs.ci.Environment;
import rocks.inspectit.shared.cs.ci.Profile;
import rocks.inspectit.shared.cs.ci.assignment.AbstractClassSensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.ISensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.impl.ExceptionSensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.impl.JmxBeanSensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.impl.MethodSensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.impl.SpecialMethodSensorAssignment;
import rocks.inspectit.shared.cs.ci.assignment.impl.TimerMethodSensorAssignment;
import rocks.inspectit.shared.cs.ci.exclude.ExcludeRule;
import rocks.inspectit.shared.cs.ci.factory.SpecialMethodSensorAssignmentFactory;
import rocks.inspectit.shared.cs.ci.profile.data.AbstractProfileData;
import rocks.inspectit.shared.cs.ci.profile.data.ExcludeRulesProfileData;
import rocks.inspectit.shared.cs.ci.profile.data.JmxDefinitionProfileData;
import rocks.inspectit.shared.cs.ci.profile.data.SensorAssignmentProfileData;
import rocks.inspectit.shared.cs.cmr.service.IRegistrationService;
/**
* Component that can resolve the different CI configuration points.
*
* @author Ivan Senic
*
*/
@Component
public class ConfigurationResolver {
/**
* Logger for the class.
*/
@Log
Logger log;
/**
* {@link ConfigurationInterfaceManager}.
*/
@Autowired
private ConfigurationInterfaceManager configurationInterfaceManager;
/**
* {@link IRegistrationService} needed for the instrumentation appliers.
*/
@Autowired
private IRegistrationService registrationService;
/**
* {@link SpecialMethodSensorAssignmentFactory}.
*/
@Autowired
private SpecialMethodSensorAssignmentFactory specialAssignmentFactory;
/**
* Returns all instrumentation appliers for one environment.
*
* @param environment
* {@link Environment} to get appliers for.
* @return Returns all {@link IInstrumentationApplier}s contained in all profiles for the given
* environment and all functional applier defined by environment.
*/
public Collection<IInstrumentationApplier> getInstrumentationAppliers(Environment environment) {
if (null == environment) {
return Collections.emptyList();
}
Collection<IInstrumentationApplier> appliers = new ArrayList<>();
for (String profileId : environment.getProfileIds()) {
try {
Profile profile = configurationInterfaceManager.getProfile(profileId);
// don't include inactive profiles
if (!profile.isActive()) {
continue;
}
// all assignments
AbstractProfileData<?> profileData = profile.getProfileData();
if (profileData.isOfType(SensorAssignmentProfileData.class)) {
List<? extends AbstractClassSensorAssignment<?>> assignments = profileData.getData(SensorAssignmentProfileData.class);
if (CollectionUtils.isNotEmpty(assignments)) {
for (AbstractClassSensorAssignment<?> assignment : assignments) {
appliers.add(getInstrumentationApplier(assignment, environment));
}
}
}
} catch (BusinessException e) {
// on exception just exclude the profile
if (log.isDebugEnabled()) {
log.debug("Profile with id " + profileId + " ignored during collecting method sensor assignments due to the exception.", e);
}
continue;
}
}
// collect functionals as well
for (SpecialMethodSensorAssignment functionalAssignment : specialAssignmentFactory.getSpecialAssignments(environment)) {
appliers.add(getInstrumentationApplier(functionalAssignment, environment));
}
return appliers;
}
/**
* Returns the {@link IInstrumentationApplier} for the given sensor assignment and the
* {@link Environment} it's being used in.
*
* @param sensorAssignment
* {@link ISensorAssignment}
* @param environment
* Environment being used.
* @return {@link IInstrumentationApplier}
* @throws IllegalArgumentException
* If supplied assignment is not of known type.
*/
public IInstrumentationApplier getInstrumentationApplier(ISensorAssignment<?> sensorAssignment, Environment environment) throws IllegalArgumentException {
// switch based on the class
Class<?> sensorAssigmentClass = sensorAssignment.getClass();
if (TimerMethodSensorAssignment.class.isAssignableFrom(sensorAssigmentClass)) {
return new TimerMethodSensorInstrumentationApplier((TimerMethodSensorAssignment) sensorAssignment, environment, registrationService);
} else if (ExceptionSensorAssignment.class.isAssignableFrom(sensorAssigmentClass)) {
return new ExceptionSensorInstrumentationApplier((ExceptionSensorAssignment) sensorAssignment, environment, registrationService);
} else if (SpecialMethodSensorAssignment.class.isAssignableFrom(sensorAssigmentClass)) {
return new SpecialInstrumentationApplier((SpecialMethodSensorAssignment) sensorAssignment, environment, registrationService);
} else if (MethodSensorAssignment.class.isAssignableFrom(sensorAssigmentClass)) {
return new MethodSensorInstrumentationApplier((MethodSensorAssignment) sensorAssignment, environment, registrationService);
}
throw new IllegalArgumentException("Instrumentation applier can be created. Assignment " + sensorAssignment + " is of unknow type.");
}
/**
* Returns all JMX monitoring appliers for one environment.
*
* @param environment
* {@link Environment} to get appliers for.
* @return Returns all {@link JmxMonitoringApplier}s contained in all profiles for the given
* environment.
*/
public Collection<JmxMonitoringApplier> getJmxMonitoringAppliers(Environment environment) {
if (null == environment) {
return Collections.emptyList();
}
Collection<JmxMonitoringApplier> appliers = new ArrayList<>();
for (String profileId : environment.getProfileIds()) {
try {
Profile profile = configurationInterfaceManager.getProfile(profileId);
// don't include inactive profiles
if (!profile.isActive()) {
continue;
}
// all assignments
AbstractProfileData<?> profileData = profile.getProfileData();
if (profileData.isOfType(JmxDefinitionProfileData.class)) {
List<JmxBeanSensorAssignment> assignments = profileData.getData(JmxDefinitionProfileData.class);
if (CollectionUtils.isNotEmpty(assignments)) {
for (JmxBeanSensorAssignment assignment : assignments) {
appliers.add(new JmxMonitoringApplier(assignment, environment, registrationService));
}
}
}
} catch (BusinessException e) {
// on exception just exclude the profile
if (log.isDebugEnabled()) {
log.debug("Profile with id " + profileId + " ignored during collecting JMX sensor assignments due to the exception.", e);
}
continue;
}
}
return appliers;
}
/**
* Returns all {@link ExcludeRule}s contained in all profiles for the given environment.
*
* @param environment
* {@link Environment} to get rules for.
* @return Returns all {@link ExcludeRule}s contained in all profiles for the given environment.
*/
public Collection<ExcludeRule> getAllExcludeRules(Environment environment) {
if (null == environment) {
return Collections.emptyList();
}
Collection<ExcludeRule> rules = new ArrayList<>();
for (String profileId : environment.getProfileIds()) {
try {
Profile profile = configurationInterfaceManager.getProfile(profileId);
// don't include inactive profiles
if (!profile.isActive()) {
continue;
}
AbstractProfileData<?> profileData = profile.getProfileData();
if (profileData.isOfType(ExcludeRulesProfileData.class)) {
List<ExcludeRule> data = profileData.getData(ExcludeRulesProfileData.class);
if (CollectionUtils.isNotEmpty(data)) {
rules.addAll(data);
}
}
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Profile with id " + profileId + " ignored during profile difference calculation due to the exception.", e);
}
continue;
}
}
return rules;
}
/**
* Returns the configuration info based on the given {@link Environment}.
*
* @param environment
* {@link Environment}.
* @return Configuration info
*/
public String getConfigurationInfo(Environment environment) {
if (null == environment) {
return null;
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Assigned environment: " + environment.getName() + "\n"); // NOPMD
// all active profiles
stringBuilder.append("Active profiles:\n");
for (String profileId : environment.getProfileIds()) {
try {
Profile profile = configurationInterfaceManager.getProfile(profileId);
// don't include inactive profiles
if (!profile.isActive()) {
continue;
}
stringBuilder.append("|-" + profile.getName() + "\n");
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Profile with id " + profileId + " ignored during configuration info creation due to the exception.", e);
}
continue;
}
}
// some options
stringBuilder.append("Options:\n"); // NOPMD
stringBuilder.append("|-class loading delegation: " + environment.isClassLoadingDelegation() + "\n"); // NOPMD
stringBuilder.append("|-enhanced exception sensor: " + environment.getExceptionSensorConfig().isEnhanced()); // NOPMD
return stringBuilder.toString();
}
/**
* Tries to locate one {@link Environment} for the given agent name and IPs. If only one
* {@link Environment} fits the agent by current mappings this one will be returned. Otherwise
* an exception will be raised.
*
* @param definedIPs
* The list of all network interfaces.
* @param agentName
* The self-defined name of the inspectIT Agent. Can be <code>null</code>.
* @return {@link Environment}.
* @throws BusinessException
* Throws {@link Exception} if there is no matching environment for the agent or if
* there is more than one valid environment for the agent.
*/
public Environment getEnvironmentForAgent(List<String> definedIPs, String agentName) throws BusinessException {
AgentMappings agentMappings = configurationInterfaceManager.getAgentMappings();
if (CollectionUtils.isEmpty(agentMappings.getMappings())) {
throw new BusinessException("Determine an environment to use for the agent with name '" + agentName + "' and IP adress(es): " + definedIPs,
ConfigurationInterfaceErrorCodeEnum.NO_MAPPING_DEFINED);
}
List<AgentMapping> mappings = new ArrayList<>(agentMappings.getMappings());
for (Iterator<AgentMapping> it = mappings.iterator(); it.hasNext();) {
AgentMapping agentMapping = it.next();
if (!agentMapping.isActive() || !matches(agentMapping, definedIPs, agentName)) {
it.remove();
}
}
if (CollectionUtils.isEmpty(mappings)) {
throw new BusinessException("Determine an environment to use for the agent with name '" + agentName + "' and IP adress(es): " + definedIPs,
ConfigurationInterfaceErrorCodeEnum.ENVIRONMENT_FOR_AGENT_NOT_FOUND);
} else if (mappings.size() > 1) {
throw new BusinessException("Determine an environment to use for the agent with name '" + agentName + "' and IP adress(es): " + definedIPs,
ConfigurationInterfaceErrorCodeEnum.MORE_THAN_ONE_ENVIRONMENT_FOR_AGENT_FOUND);
} else {
String environmentId = mappings.get(0).getEnvironmentId();
return configurationInterfaceManager.getEnvironment(environmentId);
}
}
/**
* Checks if the specified {@link AgentMapping} is matching the agent name and IPs.
*
* @param agentMapping
* {@link AgentMapping} to check.
* @param definedIPs
* The list of all network interfaces.
* @param agentName
* The self-defined name of the inspectIT Agent.
* @return <code>true</code> if the name and any of the IP addresses match the defined
* {@link AgentMapping}
*/
private boolean matches(AgentMapping agentMapping, List<String> definedIPs, String agentName) {
// first match name
String definedName = agentMapping.getAgentName();
IMatchPattern namePattern = PatternFactory.getPattern(definedName);
if (!namePattern.match(agentName)) {
return false;
}
String definedIps = agentMapping.getIpAddress();
// then all IPs, if any matches return true
IMatchPattern ipPattern = PatternFactory.getPattern(definedIps);
for (String ip : definedIPs) {
if (ipPattern.match(ip)) {
return true;
}
}
return false;
}
}