All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sentrysoftware.metricshub.engine.extension.ExtensionManager Maven / Gradle / Ivy

The newest version!
package org.sentrysoftware.metricshub.engine.extension;

/*-
 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
 * MetricsHub Engine
 * ჻჻჻჻჻჻
 * Copyright 2023 - 2024 Sentry Software
 * ჻჻჻჻჻჻
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
 */

import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.sentrysoftware.metricshub.engine.common.exception.InvalidConfigurationException;
import org.sentrysoftware.metricshub.engine.configuration.IConfiguration;
import org.sentrysoftware.metricshub.engine.connector.model.ConnectorStore;
import org.sentrysoftware.metricshub.engine.connector.model.common.DeviceKind;
import org.sentrysoftware.metricshub.engine.connector.model.identity.criterion.Criterion;
import org.sentrysoftware.metricshub.engine.connector.model.monitor.task.source.Source;
import org.sentrysoftware.metricshub.engine.telemetry.TelemetryManager;

/**
 * Manages and aggregates various types of extensions used within MetricsHub.
 * This class acts as a central point for accessing and managing protocol
 * extensions, strategy provider extensions, connector store provider
 * extensions and source computation extensions.
 * 

* The {@link ExtensionManager} is designed to be flexible and extensible, * supporting various types of extensions that can be added or removed as * needed. It uses the Builder pattern to simplify instantiation and setup. *

*/ @Data @AllArgsConstructor @NoArgsConstructor @Builder(setterPrefix = "with") public class ExtensionManager { @Default private List protocolExtensions = new ArrayList<>(); @Default private List strategyProviderExtensions = new ArrayList<>(); @Default private List connectorStoreProviderExtensions = new ArrayList<>(); @Default private List sourceComputationExtensions = new ArrayList<>(); @Default private List compositeSourceScriptExtensions = new ArrayList<>(); /** * Create a new empty instance of the Extension Manager. * @return a new instance of {@link ExtensionManager}. */ public static ExtensionManager empty() { return ExtensionManager.builder().build(); } /** * Find the extension which satisfies the processing of the given criterion according to the user's configuration. * * @param criterion Any {@link Criterion} implementation * @param telemetryManager {@link TelemetryManager} instance where the configurations are located. * @return an {@link Optional} of an {@link IProtocolExtension} instance. */ public Optional findCriterionExtension( final Criterion criterion, final TelemetryManager telemetryManager ) { final List criterionExtensions = protocolExtensions .stream() .filter(extension -> telemetryManager .getHostConfiguration() .getConfigurations() .values() .stream() .anyMatch(extension::isValidConfiguration) ) .filter(extension -> extension.getSupportedCriteria().contains(criterion.getClass())) .toList(); return filterWithBusinessLogic(telemetryManager, criterionExtensions); } /** * Filters a list of {@link IProtocolExtension} based on the business logic tied to the telemetry context * and the type of host configuration. The method selects the appropriate protocol extension based on the * type of host (Windows vs. others) and whether the operation is performed on localhost or not. * * The method first checks for extensions supporting "wmi", "winrm", and "oscommand" configuration types. * If the host is of type WINDOWS and it is not localhost, it prioritizes "wmi" and "winrm" extensions. * Otherwise, it returns the "oscommand" extension if present. If none of these conditions are met, * the method simply returns the first available extension from the list. * * @param telemetryManager The {@link TelemetryManager} instance used to access host configuration and properties. * @param candidateExtensions A list of {@link IProtocolExtension} to be filtered based on the supported configuration. * @return An {@link Optional} containing the selected {@link IProtocolExtension}, or empty if no suitable extension is found. */ private Optional filterWithBusinessLogic( final TelemetryManager telemetryManager, final List candidateExtensions ) { final Optional maybeWmiExtension = findExtensionByType(candidateExtensions, "wmi"); final Optional maybeWinrmExtension = findExtensionByType(candidateExtensions, "winrm"); final Optional maybeOsCommandExtension = findExtensionByType(candidateExtensions, "oscommand"); if ((maybeWmiExtension.isPresent() || maybeWinrmExtension.isPresent()) && maybeOsCommandExtension.isPresent()) { if ( // CHECKSTYLE:OFF DeviceKind.WINDOWS == telemetryManager.getHostConfiguration().getHostType() && !telemetryManager.getHostProperties().isLocalhost() // CHECKSTYLE:ON ) { return Stream .of(maybeWmiExtension, maybeWinrmExtension) .filter(Optional::isPresent) .map(Optional::get) .findFirst(); } else { return maybeOsCommandExtension; } } return candidateExtensions.stream().findFirst(); } /** * Finds the first protocol extension that supports the specified type. * * @param extensions List of extensions to search. * @param type The configuration type to match. * @return An {@code Optional} describing the first matching extension, or an empty {@code Optional} if none match. */ private Optional findExtensionByType( final List extensions, final String type ) { return extensions.stream().filter(extension -> extension.isSupportedConfigurationType(type)).findFirst(); } /** * Finds the first protocol extension that supports the specified type. * * @param type The configuration type to match. * @return An {@code Optional} describing the first matching extension, or an empty {@code Optional} if none match. */ public Optional findExtensionByType(final String type) { return findExtensionByType(protocolExtensions, type); } /** * Find the extension which satisfies the processing of the given source according to the user's configuration. * * @param source Any {@link Source} implementation * @param telemetryManager {@link TelemetryManager} instance where the configurations are located. * @return an {@link Optional} of an {@link IProtocolExtension} instance. */ public Optional findSourceExtension( final Source source, final TelemetryManager telemetryManager ) { final List sourceExtensions = protocolExtensions .stream() .filter(extension -> telemetryManager .getHostConfiguration() .getConfigurations() .values() .stream() .anyMatch(extension::isValidConfiguration) ) .filter(extension -> extension.getSupportedSources().contains(source.getClass())) .toList(); return filterWithBusinessLogic(telemetryManager, sourceExtensions); } /** * Find the extensions that satisfy the protocol check according to the user's configuration. * * @param telemetryManager {@link TelemetryManager} instance where the configurations are located. * @return a {@link List} of {@link IProtocolExtension} instances. */ public List findProtocolCheckExtensions(@NonNull TelemetryManager telemetryManager) { return protocolExtensions .stream() .filter(extension -> telemetryManager .getHostConfiguration() .getConfigurations() .values() .stream() .anyMatch(extension::isValidConfiguration) ) .collect(Collectors.toList()); } /** * Find a mapping between configuration classes and their corresponding sets * of source classes. * @return A map where the keys are classes extending {@link IConfiguration} * representing different types of protocol configurations, and the * values are sets of classes extending {@link Source}, indicating the * sources that are compatible and can be utilized with each * configuration type for data exchange operations. */ public Map, Set>> findConfigurationToSourceMapping() { return protocolExtensions .stream() .map(IProtocolExtension::getConfigurationToSourceMapping) .flatMap(map -> map.entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue)); } /** * Constructs a configuration object from a given JSON node based on the specified configuration type. * This method iterates over a collection of protocol extensions to find the first extension that supports * the specified configuration type. It then uses this extension to build a configuration object from the * provided JSON node. If no suitable extension is found, an null value is returned. * * @param configurationType A {@link String} representing the type of configuration to be created. * This is used to identify the appropriate protocol extension that can * handle the specified configuration type. * @param configurationJsonNode A {@link JsonNode} containing the configuration data in JSON format. * This data is used by the selected protocol extension to build the configuration object. * @param decrypt A {@link UnaryOperator} function that takes a {@code char[]} array and * returns a {@code char[]} array. This function is intended to decrypt the configuration * data if necessary before building the configuration object. * @return An {@link Optional} of {@link IConfiguration} representing the constructed configuration object. * Returns an empty {@link Optional} if no suitable protocol extension is found or if the configuration * object cannot be created for any reason. * @throws InvalidConfigurationException If the given configuration JSON node is invalid. */ public Optional buildConfigurationFromJsonNode( final String configurationType, final JsonNode configurationJsonNode, final UnaryOperator decrypt ) throws InvalidConfigurationException { for (IProtocolExtension extension : protocolExtensions) { if (extension.isSupportedConfigurationType(configurationType)) { return Optional.ofNullable(extension.buildConfiguration(configurationType, configurationJsonNode, decrypt)); } } return Optional.empty(); } /** * Aggregates connector stores from various provider extensions into a single {@link ConnectorStore}. * This method initializes a new {@link ConnectorStore} and iteratively loads data from each registered * connector store provider extension. All connectors from each extension are added to the newly created store. * * This centralized store can be used to manage or interpret connectors across different extensions efficiently, * providing a unified view of all connectors available in MetricsHub. * * @return A {@link ConnectorStore} containing all connectors from various extensions combined into one store. */ public ConnectorStore aggregateExtensionConnectorStores() { final ConnectorStore connectorStore = new ConnectorStore(); connectorStore.setStore(new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); connectorStoreProviderExtensions.forEach(connectorStoreProviderExtension -> { connectorStoreProviderExtension.load(); connectorStore.addMany(connectorStoreProviderExtension.getConnectorStore().getStore()); }); return connectorStore; } /** * Find the extension which satisfies the processing of the given source according to its type. * * @param source Any {@link Source} implementation * @return an {@link Optional} of an {@link ISourceComputationExtension} instance. */ public Optional findSourceComputationExtension(final Source source) { return sourceComputationExtensions.stream().filter(extension -> extension.isValidSource(source)).findFirst(); } /** * Find the extension which satisfies the processing of the given source according to its type. * * @param source Any {@link Source} implementation * @return an {@link Optional} of an {@link ICompositeSourceScriptExtension} instance. */ public Optional findCompositeSourceScriptExtension(final Source source) { return compositeSourceScriptExtensions.stream().filter(extension -> extension.isValidSource(source)).findFirst(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy