/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * docs/licenses/cddl.txt * or http://www.opensource.org/licenses/cddl1.php. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * docs/licenses/cddl.txt. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2010-2023 Ping Identity Corporation */ package com.unboundid.directory.sdk.examples; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.unboundid.directory.sdk.common.api.MonitorProvider; import com.unboundid.directory.sdk.common.config.MonitorProviderConfig; import com.unboundid.directory.sdk.common.types.ServerContext; import com.unboundid.ldap.sdk.Attribute; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.util.StaticUtils; import com.unboundid.util.args.ArgumentException; import com.unboundid.util.args.ArgumentParser; import com.unboundid.util.args.FileArgument; /** * This class provides a simple example of a monitor provider that will report * information about a specified file. Monitor attributes will provide the * name of the file, the size in bytes, the time that it was last modified, and * the contents of that file. It takes a single configuration argument: * <UL> * <LI>data-file -- The path to the file containing the data to use for this * monitor provider.</LI> * </UL> */ public final class ExampleMonitorProvider extends MonitorProvider { /** * The name of the argument that will be used for the argument used to specify * the path to the data file. */ private static final String ARG_NAME_DATA_FILE = "data-file"; // The path to the data file to be read. private volatile File dataFile; // General configuration for this monitor provider. private volatile MonitorProviderConfig config; // The server context for the server in which this extension is running. private ServerContext serverContext; /** * Creates a new instance of this monitor provider. All monitor provider * implementations must include a default constructor, but any initialization * should generally be done in the {@code initializeMonitorProvider} method. */ public ExampleMonitorProvider() { // No implementation required. } /** * Retrieves a human-readable name for this extension. * * @return A human-readable name for this extension. */ @Override() public String getExtensionName() { return "Example Monitor Provider"; } /** * Retrieves a human-readable description for this extension. Each element * of the array that is returned will be considered a separate paragraph in * generated documentation. * * @return A human-readable description for this extension, or {@code null} * or an empty array if no description should be available. */ @Override() public String[] getExtensionDescription() { return new String[] { "This monitor provider serves an example that may be used to " + "demonstrate the process for creating a third-party monitor " + "provider. It will provide information about a specified file on " + "filesystem, including its size, last-modified time, and content." }; } /** * Updates the provided argument parser to define any configuration arguments * which may be used by this monitor provider. The argument parser may also * be updated to define relationships between arguments (e.g., to specify * required, exclusive, or dependent argument sets). * * @param parser The argument parser to be updated with the configuration * arguments which may be used by this monitor provider. * * @throws ArgumentException If a problem is encountered while updating the * provided argument parser. */ @Override() public void defineConfigArguments(final ArgumentParser parser) throws ArgumentException { // Add an argument that allows you to specify the path to the data file. Character shortIdentifier = null; String longIdentifier = ARG_NAME_DATA_FILE; boolean required = true; int maxOccurrences = 1; String placeholder = "{path}"; String description = "The path to the data file to be read. " + "Non-absolute paths will be treated as relative to the server root."; boolean fileMustExist = false; boolean parentMustExist = true; boolean mustBeFile = true; boolean mustBeDirectory = false; parser.addArgument(new FileArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description, fileMustExist, parentMustExist, mustBeFile, mustBeDirectory)); } /** * Initializes this monitor provider. * * @param serverContext A handle to the server context for the server in * which this extension is running. * @param config The general configuration for this monitor provider. * @param parser The argument parser which has been initialized from * the configuration for this monitor provider. * * @throws LDAPException If a problem occurs while initializing this monitor * provider. */ @Override() public void initializeMonitorProvider(final ServerContext serverContext, final MonitorProviderConfig config, final ArgumentParser parser) throws LDAPException { this.serverContext = serverContext; this.config = config; // Get the data file path. final FileArgument arg = (FileArgument) parser.getNamedArgument(ARG_NAME_DATA_FILE); dataFile = arg.getValue(); } /** * Indicates whether the configuration contained in the provided argument * parser represents a valid configuration for this extension. * * @param config The general configuration for this monitor * provider. * @param parser The argument parser which has been initialized * with the proposed configuration. * @param unacceptableReasons A list that can be updated with reasons that * the proposed configuration is not acceptable. * * @return {@code true} if the proposed configuration is acceptable, or * {@code false} if not. */ @Override() public boolean isConfigurationAcceptable(final MonitorProviderConfig config, final ArgumentParser parser, final List<String> unacceptableReasons) { // The argument parser will handle all of the necessary validation, so // we don't need to do anything here. return true; } /** * Attempts to apply the configuration contained in the provided argument * parser. * * @param config The general configuration for this monitor * provider. * @param parser The argument parser which has been * initialized with the new configuration. * @param adminActionsRequired A list that can be updated with information * about any administrative actions that may be * required before one or more of the * configuration changes will be applied. * @param messages A list that can be updated with information * about the result of applying the new * configuration. * * @return A result code that provides information about the result of * attempting to apply the configuration change. */ @Override() public ResultCode applyConfiguration(final MonitorProviderConfig config, final ArgumentParser parser, final List<String> adminActionsRequired, final List<String> messages) { // Get the path to the data file from the new config. final FileArgument arg = (FileArgument) parser.getNamedArgument(ARG_NAME_DATA_FILE); dataFile = arg.getValue(); this.config = config; return ResultCode.SUCCESS; } /** * Performs any cleanup which may be necessary when this monitor provider is * to be taken out of service. */ @Override() public void finalizeMonitorProvider() { // No implementation required. } /** * Retrieves the name that identifies this monitor provider instance. It * will be used as the value of the naming attribute for monitor entries. * Each monitor provider instance must have a unique name. * * @return The name that identifies this monitor provider instance. */ @Override() public String getMonitorInstanceName() { // The monitor instance name will be based on the name of the config entry. return "Example Monitor Provider " + config.getConfigObjectName(); } /** * Retrieves the name of the object class that will be used for monitor * entries created from this monitor provider. It may be {@code null} if * a default object class should be used. * * @return The name of the object class that will be used for monitor entries * created from this monitor provider. */ @Override() public String getMonitorObjectClass() { return "example-monitor-entry"; } /** * Retrieves the update interval in milliseconds that should be used for this * monitor provider. A value that is greater than zero will cause the * {@link #updateMonitorData} method to be repeatedly invoked at that * interval. A value less than or equal to zero indicates that the monitor * provider should not be periodically updated. * * @return The update interval in milliseconds that should be used for this * monitor provider. */ @Override() public long getUpdateIntervalMillis() { // This monitor provider will not be periodically updated by default. return 0L; } /** * Updates the information collected by this monitor provider. This method * will be periodically invoked if the {@link #getUpdateIntervalMillis} method * returns a positive value. */ @Override() public void updateMonitorData() { // No implementation provided by default. } /** * Retrieves a list of attributes containing the data to include in the * monitor entry generated from this monitor provider. * * @return A list of attributes containing the data to include in the monitor * entry generated from this monitor provider. */ @Override() public List<Attribute> getMonitorAttributes() { // Get a local reference to the data file in case the configuration changes // while we're in the middle of the method. final File f = dataFile; final ArrayList<Attribute> attrList = new ArrayList<Attribute>(4); attrList.add(new Attribute("data-file", f.getAbsolutePath())); if (f.exists()) { final int length = (int) f.length(); attrList.add(new Attribute("file-size", String.valueOf(length))); attrList.add(new Attribute("last-modified-time", new Date(f.lastModified()).toString())); BufferedInputStream inputStream = null; try { inputStream = new BufferedInputStream(new FileInputStream(f)); final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(length); final byte[] buffer = new byte[8192]; while (true) { final int bytesRead = inputStream.read(buffer); if (bytesRead < 0) { break; } else { outputStream.write(buffer, 0, bytesRead); } } attrList.add(new Attribute("file-data", outputStream.toByteArray())); } catch (final Exception e) { serverContext.debugCaught(e); attrList.add(new Attribute("message", "Unable to read the file contents: " + StaticUtils.getExceptionMessage(e))); } finally { if (inputStream != null) { try { inputStream.close(); } catch (final Exception e) { serverContext.debugCaught(e); } } } } else { attrList.add(new Attribute("message", "The file does not exist")); } return attrList; } /** * Retrieves a map containing examples of configurations that may be used for * this extension. The map key should be a list of sample arguments, and the * corresponding value should be a description of the behavior that will be * exhibited by the extension when used with that configuration. * * @return A map containing examples of configurations that may be used for * this extension. It may be {@code null} or empty if there should * not be any example argument sets. */ @Override() public Map<List<String>,String> getExamplesArgumentSets() { final LinkedHashMap<List<String>,String> exampleMap = new LinkedHashMap<List<String>,String>(1); exampleMap.put( Arrays.asList(ARG_NAME_DATA_FILE + "=config/monitor-data.txt"), "Provide a monitor file with access to information contained in " + "the config/monitor-data.txt file below the server root."); return exampleMap; } }