/*
* 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
*
*
* Copyright 2010-2016 UnboundID Corp.
*/
package com.unboundid.directory.sdk.examples.groovy;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.unboundid.directory.sdk.http.config.HTTPOperationLoggerConfig;
import com.unboundid.directory.sdk.http.scripting.ScriptedHTTPOperationLogger;
import com.unboundid.directory.sdk.http.types.HTTPServerContext;
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 scripted HTTP operation logger that
* will append a message to a specified file for any type of operation. 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 ExampleScriptedHTTPOperationLogger
extends ScriptedHTTPOperationLogger
{
/**
* 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 path to the log file to be written.
private volatile File logFile;
// The server context for the server in which this extension is running.
private HTTPServerContext serverContext;
// 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;
/**
* Creates a new instance of this HTTP operation logger. All HTTP operation
* logger implementations must include a default constructor, but any
* initialization should generally be done in the
* {@code initializeHTTPOperationLogger} method.
*/
public ExampleScriptedHTTPOperationLogger()
{
loggerLock = new Object();
}
/**
* Updates the provided argument parser to define any configuration arguments
* which may be used by this HTTP operation 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 HTTP operation 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 HTTP operation 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 HTTP operation
* logger.
* @param parser The argument parser which has been initialized from
* the configuration for this HTTP operation logger.
*
* @throws LDAPException If a problem occurs while initializing this HTTP
* operation logger.
*/
@Override()
public void initializeHTTPOperationLogger(
final HTTPServerContext serverContext,
final HTTPOperationLoggerConfig config,
final ArgumentParser parser)
throws LDAPException
{
this.serverContext = serverContext;
// 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), 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);
}
}
/**
* Indicates whether the configuration contained in the provided argument
* parser represents a valid configuration for this extension.
*
* @param config The general configuration for this HTTP
* operation 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 HTTPOperationLoggerConfig 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 HTTP
* operation 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 HTTPOperationLoggerConfig config,
final ArgumentParser parser,
final List<String> adminActionsRequired,
final List<String> messages)
{
// 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), 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 HTTP operation logger
* is to be taken out of service.
*/
@Override()
public void finalizeHTTPOperationLogger()
{
synchronized (loggerLock)
{
writer.close();
writer = null;
logFile = null;
}
}
/**
* Logs information about a servlet request that has been received from the
* client.
*
* @param request An object with information about the request received
* from the client.
* @param stateMap An empty map which may be updated to hold state
* information that can be used to correlate information
* between the request and response. The same map instance
* will be passed to the {@link #logResponse} method.
*/
@Override()
public void logRequest(final HttpServletRequest request,
final Map<String,Object> stateMap)
{
// All of the processing for this logger will be done during the course of
// handling the response.
}
/**
* Logs information about a servlet response to be returned to the client.
*
* @param request An object with information about the request received
* from the client.
* @param response An object with information about the response to be
* returned to the client.
* @param stateMap A map containing state any information added while
* processing the {@link #logRequest} method.
*/
@Override()
public void logResponse(final HttpServletRequest request,
final HttpServletResponse response,
final Map<String,Object> stateMap)
{
final Long processingTimeMillis =
(Long) stateMap.get(STATE_KEY_PROCESSING_TIME_MILLIS);
final Long bytesSent =
(Long) stateMap.get(STATE_KEY_RESPONSE_CONTENT_LENGTH);
final StringBuilder buffer = new StringBuilder();
buffer.append(new Date().toString());
buffer.append(' ');
buffer.append("request=\"");
buffer.append(request.getMethod());
buffer.append(' ');
buffer.append(request.getRequestURI());
if (request.getMethod().equals("GET"))
{
final String queryString = request.getQueryString();
if ((queryString != null) && (queryString.length() > 0))
{
buffer.append('?');
buffer.append(queryString);
}
}
buffer.append(' ');
buffer.append(request.getProtocol());
buffer.append("\" statusCode=");
buffer.append(response.getStatus());
buffer.append(" bytesSent=");
buffer.append(bytesSent);
buffer.append(" processingTimeMillis=");
buffer.append(processingTimeMillis);
synchronized (loggerLock)
{
if (writer == null)
{
// This should only happen if the logger has been shut down.
return;
}
writer.println(buffer.toString());
writer.flush();
}
}
}
|