/* * 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-2024 Ping Identity Corporation */ package com.unboundid.directory.sdk.examples; import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.security.auth.x500.X500Principal; import com.unboundid.directory.sdk.common.api.AccessLogger; import com.unboundid.directory.sdk.common.api.DiskSpaceConsumer; import com.unboundid.directory.sdk.common.config.AccessLoggerConfig; import com.unboundid.directory.sdk.common.operation.AbandonRequest; import com.unboundid.directory.sdk.common.operation.AddRequest; import com.unboundid.directory.sdk.common.operation.AddResult; import com.unboundid.directory.sdk.common.operation.BindResult; import com.unboundid.directory.sdk.common.operation.CompareRequest; import com.unboundid.directory.sdk.common.operation.CompareResult; import com.unboundid.directory.sdk.common.operation.DeleteRequest; import com.unboundid.directory.sdk.common.operation.DeleteResult; import com.unboundid.directory.sdk.common.operation.ExtendedRequest; import com.unboundid.directory.sdk.common.operation.ExtendedResult; import com.unboundid.directory.sdk.common.operation.GenericResult; import com.unboundid.directory.sdk.common.operation.ModifyRequest; import com.unboundid.directory.sdk.common.operation.ModifyResult; import com.unboundid.directory.sdk.common.operation.ModifyDNRequest; import com.unboundid.directory.sdk.common.operation.ModifyDNResult; import com.unboundid.directory.sdk.common.operation.SASLBindRequest; import com.unboundid.directory.sdk.common.operation.SearchRequest; import com.unboundid.directory.sdk.common.operation.SearchResult; import com.unboundid.directory.sdk.common.operation.SimpleBindRequest; import com.unboundid.directory.sdk.common.operation.UnbindRequest; import com.unboundid.directory.sdk.common.types.AssuredReplicationRequirements; import com.unboundid.directory.sdk.common.types.AssuredReplicationResult; import com.unboundid.directory.sdk.common.types.ClientContext; import com.unboundid.directory.sdk.common.types.CompletedOperationContext; import com.unboundid.directory.sdk.common.types.CompletedSearchOperationContext; import com.unboundid.directory.sdk.common.types.DisconnectReason; import com.unboundid.directory.sdk.common.types.Entry; import com.unboundid.directory.sdk.common.types.ForwardTarget; import com.unboundid.directory.sdk.common.types.OperationContext; import com.unboundid.directory.sdk.common.types.RegisteredDiskSpaceConsumer; import com.unboundid.directory.sdk.common.types.ServerContext; import com.unboundid.ldap.sdk.Control; import com.unboundid.ldap.sdk.IntermediateResponse; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.ldap.sdk.unboundidds.controls. AssuredReplicationServerResult; 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 an access logger that will append a * message to a specified file for any type of client communication. It takes * a single configuration argument: * <UL> * <LI>log-file -- The path to the log file that will be written. This must * be provided.</LI> * </UL> */ public final class ExampleAccessLogger extends AccessLogger implements DiskSpaceConsumer { /** * The name of the argument that will be used for the argument used to specify * the path to the log file. */ private static final String ARG_NAME_LOG_FILE = "log-file"; // The current general configuration for this access logger. private volatile AccessLoggerConfig config; // The path to the log file to be written. private volatile File logFile; // The lock that will be used to synchronize logging activity. private final Object loggerLock; // The print writer that will be used to actually write the log messages. private volatile PrintWriter writer; // The registered disk space consumer for this logger. private volatile RegisteredDiskSpaceConsumer registeredConsumer; // The server context for the server in which this extension is running. private ServerContext serverContext; /** * Creates a new instance of this access logger. All access logger * implementations must include a default constructor, but any initialization * should generally be done in the {@code initializeAccessLogger} method. */ public ExampleAccessLogger() { loggerLock = new Object(); } /** * Retrieves a human-readable name for this extension. * * @return A human-readable name for this extension. */ @Override() public String getExtensionName() { return "Example Access Logger"; } /** * 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 access logger serves an example that may be used to demonstrate " + "the process for creating a third-party access logger. It will " + "write a line to a specified file for each kind of interaction " + "with the client.", "For simplicity, this example logger does not take care of all tasks " + "that would be necessary for production use. For example, it " + "does not include any rotation or retention functionality, so the " + "file will grow without bounds. Further, while it is threadsafe, " + "it does so using simple Java synchronized blocks which is a " + "simple way to achieve the necessary safety, but does not " + "necessarily allow for the highest performance or concurrency.", "Because this access logger writes to disk, it also serves as an " + "example of a disk space consumer so that the server may track " + "available space on the disk containing the log file and " + "potentially warn administrators if usable space becomes too low." }; } /** * Updates the provided argument parser to define any configuration arguments * which may be used by this access logger. 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 access logger. * * @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 log file. Character shortIdentifier = null; String longIdentifier = ARG_NAME_LOG_FILE; boolean required = true; int maxOccurrences = 1; String placeholder = "{path}"; String description = "The path to the log file to be written. " + "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 access logger. * * @param serverContext A handle to the server context for the server in * which this extension is running. * @param config The general configuration for this access logger. * @param parser The argument parser which has been initialized from * the configuration for this access logger. * * @throws LDAPException If a problem occurs while initializing this access * logger. */ @Override() public void initializeAccessLogger(final ServerContext serverContext, final AccessLoggerConfig config, final ArgumentParser parser) throws LDAPException { this.serverContext = serverContext; this.config = config; // Create the logger and open the log file. try { final FileArgument arg = (FileArgument) parser.getNamedArgument(ARG_NAME_LOG_FILE); logFile = arg.getValue(); writer = new PrintWriter(new FileWriter(logFile, true)); } catch (final Exception e) { serverContext.debugCaught(e); throw new LDAPException(ResultCode.OTHER, "Unable to open file " + logFile.getAbsolutePath() + " for writing: " + StaticUtils.getExceptionMessage(e), e); } // Register as a disk space consumer since the log file may potentially // consume a significant amount of disk space. registeredConsumer = serverContext.registerDiskSpaceConsumer(this); } /** * Indicates whether the configuration contained in the provided argument * parser represents a valid configuration for this extension. * * @param config The general configuration for this access * logger. * @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 AccessLoggerConfig 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 access * logger. * @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 AccessLoggerConfig config, final ArgumentParser parser, final List<String> adminActionsRequired, final List<String> messages) { this.config = config; // Get the path to the log file from the new config. final FileArgument arg = (FileArgument) parser.getNamedArgument(ARG_NAME_LOG_FILE); final File newFile = arg.getValue(); // If the log file path hasn't changed, then we don't need to do anything. if (newFile.equals(logFile)) { return ResultCode.SUCCESS; } // Create a print writer that can be used to write to the new log file. final PrintWriter newWriter; try { newWriter = new PrintWriter(new FileWriter(newFile, true)); } catch (final Exception e) { serverContext.debugCaught(e); messages.add("Unable to open new log file " + newFile.getAbsolutePath() + " for writing: " + StaticUtils.getExceptionMessage(e)); return ResultCode.OTHER; } // Swap the new logger into place. final PrintWriter oldWriter; synchronized (loggerLock) { oldWriter = writer; writer = newWriter; logFile = newFile; } // Close the old logger and return success. oldWriter.close(); return ResultCode.SUCCESS; } /** * Performs any cleanup which may be necessary when this access logger is * to be taken out of service. */ @Override() public void finalizeAccessLogger() { synchronized (loggerLock) { if (registeredConsumer != null) { serverContext.deregisterDiskSpaceConsumer(registeredConsumer); registeredConsumer = null; } writer.close(); writer = null; logFile = null; } } /** * Logs a message indicating that a new connection has been established. * * @param clientContext Information about the client connection that has * been accepted. */ @Override() public void logConnect(final ClientContext clientContext) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" CONNECT conn="); buffer.append(clientContext.getConnectionID()); final InetAddress clientAddress = clientContext.getClientInetAddress(); if (clientAddress != null) { buffer.append(" from=\""); buffer.append(clientAddress.getHostAddress()); buffer.append('"'); } final InetAddress serverAddress = clientContext.getServerInetAddress(); if (serverAddress != null) { buffer.append(" to="); buffer.append(serverAddress.getHostAddress()); buffer.append('"'); } buffer.append(" protocol=\""); buffer.append(clientContext.getProtocol()); buffer.append('"'); write(buffer); } /** * Logs a message indicating that a connection has been closed. * * @param clientContext Information about the client connection that has * been closed. * @param disconnectReason A general reason that the connection has been * closed. * @param message A message with additional information about the * closure. It may be {@code null} if none is * available. */ @Override() public void logDisconnect(final ClientContext clientContext, final DisconnectReason disconnectReason, final String message) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" DISCONNECT conn="); buffer.append(clientContext.getConnectionID()); buffer.append(" reason=\""); buffer.append(disconnectReason.getClosureMessage()); buffer.append('"'); if (message != null) { buffer.append(" message=\""); buffer.append(message); buffer.append('"'); } write(buffer); } /** * Logs a message about security negotiation performed by a client. * * @param clientContext Information about the client connection on which * the negotiation was completed. * @param protocol The security protocol selected by the negotiation. * It may be {@code null} if no protocol is available. * @param cipher The cipher suite selected by the negotiation. It * may be {@code null} if no cipher is available. * @param properties A set of additional properties that may be included * in the log message. It may be {@code null} or empty * if no additional properties are needed. */ @Override() public void logSecurityNegotiation(final ClientContext clientContext, final String protocol, final String cipher, final Map<String,String> properties) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" SECURITY-NEGOTIATION conn="); buffer.append(clientContext.getConnectionID()); if (protocol != null) { buffer.append(" protocol=\""); buffer.append(protocol); buffer.append('"'); } if (cipher != null) { buffer.append(" cipher=\""); buffer.append(cipher); buffer.append('"'); } if ((properties != null) && (! properties.isEmpty())) { for (final Map.Entry<String,String> e : properties.entrySet()) { buffer.append(e.getKey()); buffer.append("=\""); buffer.append(e.getValue()); buffer.append('"'); } } write(buffer); } /** * Logs a message about a certificate chain presented by a client. * * @param clientContext Information about the client that presented the * certificate chain. * @param certChain The certificate chain presented by the client. * @param authDN The DN of the user as whom the client was * automatically authenticated, or {@code null} if the * client was not automatically authenticated. */ @Override() public void logClientCertificateChain(final ClientContext clientContext, final Certificate[] certChain, final String authDN) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" CERTIFICATE conn="); buffer.append(clientContext.getConnectionID()); if (certChain.length > 0) { final Certificate cert = certChain[0]; if (cert instanceof X509Certificate) { final X509Certificate c = (X509Certificate) cert; final String subject = c.getSubjectX500Principal().getName(X500Principal.RFC2253); buffer.append(" subject=\""); buffer.append(subject); buffer.append('"'); } } if (authDN != null) { buffer.append(" authDN=\""); buffer.append(authDN); buffer.append('"'); } write(buffer); } /** * Logs a message about an abandon request received from a client. * * @param opContext The operation context for the abandon operation. * @param request The abandon request that was received. */ @Override() public void logAbandonRequest(final OperationContext opContext, final AbandonRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" ABANDON REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" idToAbandon="); buffer.append(request.getIDToAbandon()); write(buffer); } /** * Logs a message about an abandon request that will be forwarded to another * server. * * @param opContext The operation context for the abandon operation. * @param request The abandon request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logAbandonForward(final OperationContext opContext, final AbandonRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about the result of processing an abandon request. * * @param opContext The operation context for the abandon operation. * @param request The abandon request that was received. * @param result The result of processing the abandon request. */ @Override() public void logAbandonResult(final CompletedOperationContext opContext, final AbandonRequest request, final GenericResult result) { writeResult(opContext, result); } /** * Logs a message about an add request received from a client. * * @param opContext The operation context for the add operation. * @param request The add request that was received. */ @Override() public void logAddRequest(final OperationContext opContext, final AddRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" ADD REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(request.getEntry().getDN()); buffer.append('"'); write(buffer); } /** * Logs a message about an add request that will be forwarded to another * server. * * @param opContext The operation context for the add operation. * @param request The add request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logAddForward(final OperationContext opContext, final AddRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward an * add request to another server. * * @param opContext The operation context for the add operation. * @param request The add request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logAddForwardFailure(final OperationContext opContext, final AddRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing an add request. * * @param opContext The operation context for the add operation. * @param request The add request that was received. * @param result The result of processing the add request. */ @Override() public void logAddResponse(final CompletedOperationContext opContext, final AddRequest request, final AddResult result) { writeResult(opContext, result); } /** * Logs a message about the result of replication assurance processing for an * add operation. * * @param opContext The operation context for the add operation. * @param request The add request that was received. * @param result The result of processing the add request. * @param assuranceResult The replication assurance processing result. */ @Override() public void logAddAssuranceCompleted( final CompletedOperationContext opContext, final AddRequest request, final AddResult result, final AssuredReplicationResult assuranceResult) { writeAssuranceResult(opContext, result.getAssuredReplicationRequirements(), assuranceResult); } /** * Logs a message about a simple bind request received from a client. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. */ @Override() public void logBindRequest(final OperationContext opContext, final SimpleBindRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" BIND REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" authType=SIMPLE dn=\""); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append('"'); write(buffer); } /** * Logs a message about a simple bind request that will be forwarded to * another server. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logBindForward(final OperationContext opContext, final SimpleBindRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * simple bind request to another server. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logBindForwardFailure(final OperationContext opContext, final SimpleBindRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a simple bind request. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param result The result of processing the bind request. */ @Override() public void logBindResponse(final CompletedOperationContext opContext, final SimpleBindRequest request, final BindResult result) { writeResult(opContext, result); } /** * Logs a message about a SASL bind request received from a client. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. */ @Override() public void logBindRequest(final OperationContext opContext, final SASLBindRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" BIND REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" authType=SASL mechanism=\""); buffer.append(request.getSASLMechanism()); buffer.append('"'); write(buffer); } /** * Logs a message about a SASL bind request that will be forwarded to * another server. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logBindForward(final OperationContext opContext, final SASLBindRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * SASL bind request to another server. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logBindForwardFailure(final OperationContext opContext, final SASLBindRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a SASL bind request. * * @param opContext The operation context for the bind operation. * @param request The bind request that was received. * @param result The result of processing the bind request. */ @Override() public void logBindResponse(final CompletedOperationContext opContext, final SASLBindRequest request, final BindResult result) { writeResult(opContext, result); } /** * Logs a message about a compare request received from a client. * * @param opContext The operation context for the compare operation. * @param request The compare request that was received. */ @Override() public void logCompareRequest(final OperationContext opContext, final CompareRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" COMPARE REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append('"'); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append("\" attribute=\""); buffer.append(request.getAttributeType()); buffer.append('"'); write(buffer); } /** * Logs a message about a compare request that will be forwarded to another * server. * * @param opContext The operation context for the compare operation. * @param request The compare request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logCompareForward(final OperationContext opContext, final CompareRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * compare request to another server. * * @param opContext The operation context for the compare operation. * @param request The compare request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logCompareForwardFailure(final OperationContext opContext, final CompareRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a compare request. * * @param opContext The operation context for the compare operation. * @param request The compare request that was received. * @param result The result of processing the compare request. */ @Override() public void logCompareResponse(final CompletedOperationContext opContext, final CompareRequest request, final CompareResult result) { writeResult(opContext, result); } /** * Logs a message about a delete request received from a client. * * @param opContext The operation context for the delete operation. * @param request The delete request that was received. */ @Override() public void logDeleteRequest(final OperationContext opContext, final DeleteRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" DELETE REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append('"'); write(buffer); } /** * Logs a message about a delete request that will be forwarded to another * server. * * @param opContext The operation context for the delete operation. * @param request The delete request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logDeleteForward(final OperationContext opContext, final DeleteRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * delete request to another server. * * @param opContext The operation context for the delete operation. * @param request The delete request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logDeleteForwardFailure(final OperationContext opContext, final DeleteRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a delete request. * * @param opContext The operation context for the delete operation. * @param request The delete request that was received. * @param result The result of processing the delete request. */ @Override() public void logDeleteResponse(final CompletedOperationContext opContext, final DeleteRequest request, final DeleteResult result) { writeResult(opContext, result); } /** * Logs a message about the result of replication assurance processing for a * delete operation. * * @param opContext The operation context for the delete operation. * @param request The delete request that was received. * @param result The result of processing the delete request. * @param assuranceResult The replication assurance processing result. */ @Override() public void logDeleteAssuranceCompleted( final CompletedOperationContext opContext, final DeleteRequest request, final DeleteResult result, final AssuredReplicationResult assuranceResult) { writeAssuranceResult(opContext, result.getAssuredReplicationRequirements(), assuranceResult); } /** * Logs a message about an extended request received from a client. * * @param opContext The operation context for the extended operation. * @param request The extended request that was received. */ @Override() public void logExtendedRequest(final OperationContext opContext, final ExtendedRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" EXTENDED REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" oid=\""); buffer.append(request.getRequestOID()); buffer.append('"'); write(buffer); } /** * Logs a message about an extended request that will be forwarded to another * server. * * @param opContext The operation context for the extended operation. * @param request The extended request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logExtendedForward(final OperationContext opContext, final ExtendedRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward an * extended request to another server. * * @param opContext The operation context for the extended operation. * @param request The extended request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logExtendedForwardFailure(final OperationContext opContext, final ExtendedRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing an extended request. * * @param opContext The operation context for the extended operation. * @param request The extended request that was received. * @param result The result of processing the extended request. */ @Override() public void logExtendedResponse(final CompletedOperationContext opContext, final ExtendedRequest request, final ExtendedResult result) { writeResult(opContext, result); } /** * Logs a message about a modify request received from a client. * * @param opContext The operation context for the modify operation. * @param request The modify request that was received. */ @Override() public void logModifyRequest(final OperationContext opContext, final ModifyRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" MODIFY REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append('"'); write(buffer); } /** * Logs a message about a modify request that will be forwarded to another * server. * * @param opContext The operation context for the modify operation. * @param request The modify request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logModifyForward(final OperationContext opContext, final ModifyRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * modify request to another server. * * @param opContext The operation context for the modify operation. * @param request The modify request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logModifyForwardFailure(final OperationContext opContext, final ModifyRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a modify request. * * @param opContext The operation context for the modify operation. * @param request The modify request that was received. * @param result The result of processing the modify request. */ @Override() public void logModifyResponse(final CompletedOperationContext opContext, final ModifyRequest request, final ModifyResult result) { writeResult(opContext, result); } /** * Logs a message about the result of replication assurance processing for a * modify operation. * * @param opContext The operation context for the modify operation. * @param request The modify request that was received. * @param result The result of processing the modify request. * @param assuranceResult The replication assurance processing result. */ @Override() public void logModifyAssuranceCompleted( final CompletedOperationContext opContext, final ModifyRequest request, final ModifyResult result, final AssuredReplicationResult assuranceResult) { writeAssuranceResult(opContext, result.getAssuredReplicationRequirements(), assuranceResult); } /** * Logs a message about a modify DN request received from a client. * * @param opContext The operation context for the modify DN operation. * @param request The modify DN request that was received. */ @Override() public void logModifyDNRequest(final OperationContext opContext, final ModifyDNRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" MODIFY_DN REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(request.getDN()); buffer.append("\" newRDN=\""); buffer.append(request.getNewRDN()); buffer.append("\" deleteOldRDN="); buffer.append(request.deleteOldRDN()); final String newSuperior = request.getNewSuperiorDN(); if (newSuperior != null) { buffer.append(" newSuperior=\""); buffer.append(newSuperior); buffer.append('"'); } write(buffer); } /** * Logs a message about a modify DN request that will be forwarded to another * server. * * @param opContext The operation context for the modify DN operation. * @param request The modify DN request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logModifyDNForward(final OperationContext opContext, final ModifyDNRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * modify DN request to another server. * * @param opContext The operation context for the modify DN operation. * @param request The modify DN request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logModifyDNForwardFailure(final OperationContext opContext, final ModifyDNRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about the result of processing a modify DN request. * * @param opContext The operation context for the modify DN operation. * @param request The modify DN request that was received. * @param result The result of processing the modify DN request. */ @Override() public void logModifyDNResponse(final CompletedOperationContext opContext, final ModifyDNRequest request, final ModifyDNResult result) { writeResult(opContext, result); } /** * Logs a message about the result of replication assurance processing for a * modify DN operation. * * @param opContext The operation context for the modify DN operation. * @param request The modify DN request that was received. * @param result The result of processing the modify DN request. * @param assuranceResult The replication assurance processing result. */ @Override() public void logModifyDNAssuranceCompleted( final CompletedOperationContext opContext, final ModifyDNRequest request, final ModifyDNResult result, final AssuredReplicationResult assuranceResult) { writeAssuranceResult(opContext, result.getAssuredReplicationRequirements(), assuranceResult); } /** * Logs a message about a search request received from a client. * * @param opContext The operation context for the search operation. * @param request The search request that was received. */ @Override() public void logSearchRequest(final OperationContext opContext, final SearchRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" SEARCH REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" base=\""); buffer.append(request.getBaseDN()); buffer.append("\" scope="); buffer.append(request.getScope().intValue()); buffer.append(" filter=\""); buffer.append(request.getFilter().toString()); buffer.append('"'); write(buffer); } /** * Logs a message about a search request that will be forwarded to another * server. * * @param opContext The operation context for the search operation. * @param request The search request that was received. * @param target Information about the server to which the request will * be forwarded. */ @Override() public void logSearchForward(final OperationContext opContext, final SearchRequest request, final ForwardTarget target) { writeForward(opContext, target); } /** * Logs a message about a failure encountered while attempting to forward a * search request to another server. * * @param opContext The operation context for the search operation. * @param request The search request that was received. * @param target Information about the server to which the request was * forwarded. * @param failure The exception that was received when attempting to * forward the request. */ @Override() public void logSearchForwardFailure(final OperationContext opContext, final SearchRequest request, final ForwardTarget target, final LDAPException failure) { writeForwardFailure(opContext, target, failure); } /** * Logs a message about a search result entry that was returned to the client. * * @param opContext The operation context for the search operation. * @param request The search request that was received. * @param entry The entry that was returned. * @param controls The set of controls included with the entry, or an empty * list if there were none. */ @Override() public void logSearchResultEntry(final OperationContext opContext, final SearchRequest request, final Entry entry, final List<Control> controls) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" SEARCH ENTRY conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" dn=\""); buffer.append(entry.getDN()); buffer.append('"'); write(buffer); } /** * Logs a message about a search result reference that was returned to the * client. * * @param opContext The operation context for the search operation. * @param request The search request that was received. * @param referralURLs The referral URLs for the reference that was * returned. * @param controls The set of controls included with the reference, or * an empty list if there were none. */ @Override() public void logSearchResultReference(final OperationContext opContext, final SearchRequest request, final List<String> referralURLs, final List<Control> controls) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" SEARCH REFERENCE conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" referralURLs={"); final Iterator<String> iterator = referralURLs.iterator(); while (! iterator.hasNext()) { buffer.append('"'); buffer.append(iterator.next()); buffer.append('"'); if (iterator.hasNext()) { buffer.append(", "); } } buffer.append('}'); write(buffer); } /** * Logs a message about the result of processing a search request. * * @param opContext The operation context for the search operation. * @param request The search request that was received. * @param result The result of processing the search request. */ @Override() public void logSearchResultDone( final CompletedSearchOperationContext opContext, final SearchRequest request, final SearchResult result) { writeResult(opContext, result); } /** * Logs a message about an unbind request received from a client. * * @param opContext The operation context for the unbind operation. * @param request The unbind request that was received. */ @Override() public void logUnbindRequest(final OperationContext opContext, final UnbindRequest request) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" UNBIND REQUEST conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); write(buffer); } /** * Logs a message about an intermediate response that was returned to the * client. * * @param opContext The operation context for the associated * operation. * @param intermediateResponse The intermediate response that was returned. */ @Override() public void logIntermediateResponse(final OperationContext opContext, final IntermediateResponse intermediateResponse) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(" INTERMEDIATE RESPONSE conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); final String oid = intermediateResponse.getOID(); if (oid != null) { buffer.append(" oid=\""); buffer.append(oid); buffer.append('"'); } final String name = intermediateResponse.getIntermediateResponseName(); if ((name != null) && (! name.equals(oid))) { buffer.append(" name=\""); buffer.append(name); buffer.append('"'); } final String valueStr = intermediateResponse.valueToString(); if (valueStr != null) { buffer.append(" value=\""); buffer.append(valueStr); buffer.append('"'); } write(buffer); } /** * Logs information about an operation to be forwarded. * * @param opContext The operation context for the operation. * @param target The forward target for the operation. */ private void writeForward(final OperationContext opContext, final ForwardTarget target) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(' '); buffer.append(opContext.getOperationType().getOperationName()); buffer.append(" FORWARD conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" targetAddress="); buffer.append(target.getForwardTargetAddress()); buffer.append(" targetPort="); buffer.append(target.getForwardTargetPort()); write(buffer); } /** * Logs information about a failed forward attempt. * * @param opContext The operation context for the operation. * @param target The forward target for the operation. * @param failure The exception caught when trying to forward the * operation. */ private void writeForwardFailure(final OperationContext opContext, final ForwardTarget target, final LDAPException failure) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(' '); buffer.append(opContext.getOperationType().getOperationName()); buffer.append(" FORWARD FAILURE conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" targetAddress="); buffer.append(target.getForwardTargetAddress()); buffer.append(" targetPort="); buffer.append(target.getForwardTargetPort()); buffer.append(" resultCode="); buffer.append(failure.getResultCode()); final String diagnosticMessage = failure.getDiagnosticMessage(); if (diagnosticMessage != null) { buffer.append(" diagnosticMessage=\""); buffer.append(diagnosticMessage); buffer.append('"'); } final String matchedDN = failure.getMatchedDN(); if (matchedDN != null) { buffer.append(" matchedDN=\""); buffer.append(matchedDN); buffer.append('"'); } final String[] referralURLs = failure.getReferralURLs(); if ((referralURLs != null) && (referralURLs.length > 0)) { buffer.append(" referralURLs={"); for (int i=0; i < referralURLs.length; i++) { if (i > 0) { buffer.append(", "); } buffer.append('"'); buffer.append(referralURLs[i]); buffer.append('"'); } buffer.append('}'); } write(buffer); } /** * Logs information about the result of the provided operation. * * @param opContext The operation context for the operation. * @param result The result to be logged. */ private void writeResult(final CompletedOperationContext opContext, final GenericResult result) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(' '); buffer.append(opContext.getOperationType().getOperationName()); buffer.append(" RESULT conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" resultCode="); buffer.append(result.getResultCode()); final String diagnosticMessage = result.getDiagnosticMessage(); if (diagnosticMessage != null) { buffer.append(" diagnosticMessage=\""); buffer.append(diagnosticMessage); buffer.append('"'); } final String matchedDN = result.getMatchedDN(); if (matchedDN != null) { buffer.append(" matchedDN=\""); buffer.append(matchedDN); buffer.append('"'); } final List<String> referralURLs = result.getReferralURLs(); if ((referralURLs != null) && (! referralURLs.isEmpty())) { buffer.append(" referralURLs={"); final Iterator<String> iterator = referralURLs.iterator(); while (! iterator.hasNext()) { buffer.append('"'); buffer.append(iterator.next()); buffer.append('"'); if (iterator.hasNext()) { buffer.append(", "); } } buffer.append('}'); } final String additionalMessage = result.getAdditionalLogMessage(); if (additionalMessage != null) { buffer.append(" additionalLogMessage=\""); buffer.append(additionalMessage); buffer.append('"'); } buffer.append(" elapsedTimeMillis="); buffer.append(opContext.getProcessingTimeMillis()); if (opContext instanceof CompletedSearchOperationContext) { final CompletedSearchOperationContext c = (CompletedSearchOperationContext) opContext; buffer.append(" entriesReturned="); buffer.append(c.getEntryCount()); buffer.append(" referencesReturned="); buffer.append(c.getReferenceCount()); } if (result instanceof ExtendedResult) { final ExtendedResult r = (ExtendedResult) result; final String resultOID = r.getResultOID(); if (resultOID != null) { buffer.append(" resultOID=\""); buffer.append(resultOID); buffer.append('"'); } } write(buffer); } /** * Logs information about the result of assurance processing for the provided * operation. * * @param opContext The operation context for the operation. * @param assuranceRequirements The assurance requirements for the * operation. * @param assuranceResult The assurance result for the operation. */ private void writeAssuranceResult(final CompletedOperationContext opContext, final AssuredReplicationRequirements assuranceRequirements, final AssuredReplicationResult assuranceResult) { final StringBuilder buffer = new StringBuilder(); buffer.append(new Date().toString()); buffer.append(' '); buffer.append(opContext.getOperationType().getOperationName()); buffer.append(" ASSURANCE-RESULT conn="); buffer.append(opContext.getConnectionID()); buffer.append(" op="); buffer.append(opContext.getOperationID()); buffer.append(" localLevel="); buffer.append(assuranceRequirements.getLocalLevel().name()); buffer.append(" localAssuranceSatisfied="); buffer.append(assuranceResult.isLocalAssuranceSatisfied()); buffer.append(" remoteLevel="); buffer.append(assuranceRequirements.getRemoteLevel().name()); buffer.append(" remoteAssuranceSatisfied="); buffer.append(assuranceResult.isRemoteAssuranceSatisfied()); final Collection<AssuredReplicationServerResult> serverResults = assuranceResult.getServerResults(); if ((serverResults != null) && (! serverResults.isEmpty())) { buffer.append(" serverAssuranceResults='"); final Iterator<AssuredReplicationServerResult> iterator = serverResults.iterator(); while (iterator.hasNext()) { iterator.next().toString(buffer); if (iterator.hasNext()) { buffer.append(", "); } } buffer.append('\''); } write(buffer); } /** * Writes the contents of the provided buffer to the logger. * * @param buffer The buffer containing the data to write. */ private void write(final StringBuilder buffer) { synchronized (loggerLock) { if (writer == null) { // This should only happen if the logger has been shut down. return; } writer.println(buffer.toString()); } } /** * 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_LOG_FILE + "=logs/example-access.log"), "Write access log messages to the logs/example-access.log file " + "below the server root."); return exampleMap; } /** * Retrieves the name that should be used to identify this disk space * consumer. * * @return The name that should be used to identify this disk space consumer. */ public String getDiskSpaceConsumerName() { return "Example Access Logger " + config.getConfigObjectName(); } /** * Retrieves a list of filesystem paths in which this disk space consumer may * store files which may consume a significant amount of space. It is * generally recommended that the paths be directories, but they may also be * individual files. * * @return A list of filesystem paths in which this disk space consumer may * store files which may consume a significant amount of space. */ public List<File> getDiskSpaceConsumerPaths() { return Arrays.asList(logFile); } }