001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * docs/licenses/cddl.txt
011 * or http://www.opensource.org/licenses/cddl1.php.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * docs/licenses/cddl.txt.  If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 *      Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 *      Portions Copyright 2011-2023 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.http.scripting;
028
029
030
031import java.util.List;
032import java.util.Map;
033
034import javax.servlet.http.HttpServletRequest;
035import javax.servlet.http.HttpServletResponse;
036
037import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
038import com.unboundid.directory.sdk.common.internal.Reconfigurable;
039import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
040import com.unboundid.directory.sdk.http.config.HTTPOperationLoggerConfig;
041import com.unboundid.directory.sdk.http.types.HTTPServerContext;
042import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
043import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
044import com.unboundid.ldap.sdk.LDAPException;
045import com.unboundid.ldap.sdk.ResultCode;
046import com.unboundid.util.Extensible;
047import com.unboundid.util.ThreadSafety;
048import com.unboundid.util.ThreadSafetyLevel;
049import com.unboundid.util.args.ArgumentException;
050import com.unboundid.util.args.ArgumentParser;
051
052
053
054/**
055 * This class defines an API that must be implemented by extensions which
056 * record information about interaction with HTTP clients.  Scripted HTTP
057 * operation loggers may write information to files, but they may also write to
058 * other locations, including databases, message, queues, e-mail messages, or
059 * other targets.
060 * <BR>
061 * <H2>Configuring Scripted HTTP Operation Loggers</H2>
062 * In order to configure a scripted HTTP operation logger created using this
063 * API, use a command like:
064 * <PRE>
065 *      dsconfig create-log-publisher \
066 *           --publisher-name "<I>{logger-name}</I>" \
067 *           --type groovy-scripted-http-operation \
068 *           --set enabled:true \
069 *           --set "script-class:<I>{class-name}</I>" \
070 *           --set "script-argument:<I>{name=value}</I>"
071 * </PRE>
072 * where "<I>{logger-name}</I>" is the name to use for the scripted HTTP
073 * operation logger instance, "<I>{script-class}</I>" is the fully-qualified
074 * name of the Groovy class written using this API, and "<I>{name=value}</I>"
075 * represents name-value pairs for any arguments to provide to the logger.  If
076 * multiple arguments should be provided to the logger, then the
077 * "<CODE>--set script-argument:<I>{name=value}</I></CODE>" option should be
078 * provided multiple times.
079 *
080 * @see  com.unboundid.directory.sdk.http.api.HTTPOperationLogger
081 */
082@Extensible()
083@DirectoryServerExtension()
084@DirectoryProxyServerExtension(appliesToLocalContent=true,
085     appliesToRemoteContent=true)
086@MetricsEngineExtension()
087@BrokerExtension()
088@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
089public abstract class ScriptedHTTPOperationLogger
090       implements Reconfigurable<HTTPOperationLoggerConfig>
091{
092  /**
093   * The key that will be used to hold the time the request was received, in
094   * milliseconds since January 1, 1960, UTC, as reported by
095   * {@code System.currentTimeMillis()}.  It will always be present in the state
096   * map when the {@link #logResponse} method is invoked.  The value associated
097   * with this key will be a {@code java.lang.Long} object.
098   */
099  public static final String STATE_KEY_REQUEST_TIME_MILLIS =
100       "___request_time_millis___";
101
102
103
104  /**
105   * The key that will be used to hold the response content length in the state
106   * map.  It will always be present in the state map when the
107   * {@link #logResponse} method is invoked.  The value associated with this key
108   * will be a {@code java.lang.Long} object.
109   */
110  public static final String STATE_KEY_RESPONSE_CONTENT_LENGTH =
111       "___response_content_length___";
112
113
114
115  /**
116   * The key that will be used to hold the operation processing time in
117   * milliseconds.  It will always be present in the state map when the
118   * {@link #logResponse} method is invoked.  The value associated with this key
119   * will be a {@code java.lang.Long} object.
120   */
121  public static final String STATE_KEY_PROCESSING_TIME_MILLIS =
122       "___processing_time_millis___";
123
124
125
126  /**
127   * The key that will be used to hold the operation processing time in
128   * nanoseconds.  It will always be present in the state map when the
129   * {@link #logResponse} method is invoked.  The value associated with this key
130   * will be a {@code java.lang.Long} object.
131   */
132  public static final String STATE_KEY_PROCESSING_TIME_NANOS =
133       "___processing_time_nanos___";
134
135
136
137  /**
138   * Creates a new instance of this scripted HTTP operation logger.  All
139   * scripted HTTP operation logger implementations must include a default
140   * constructor, but any initialization should generally be done in the
141   * {@code initializeHTTPOperationLogger} method.
142   */
143  public ScriptedHTTPOperationLogger()
144  {
145    // No implementation is required.
146  }
147
148
149
150  /**
151   * {@inheritDoc}
152   */
153  public void defineConfigArguments(final ArgumentParser parser)
154         throws ArgumentException
155  {
156    // No arguments will be allowed by default.
157  }
158
159
160
161  /**
162   * Initializes this scripted HTTP operation logger.
163   *
164   * @param  serverContext  A handle to the server context for the server in
165   *                        which this extension is running.
166   * @param  config         The general configuration for this HTTP operation
167   *                        logger.
168   * @param  parser         The argument parser which has been initialized from
169   *                        the configuration for this HTTP operation logger.
170   *
171   * @throws  LDAPException  If a problem occurs while initializing this HTTP
172   *                         operation logger.
173   */
174  public void initializeHTTPOperationLogger(
175                   final HTTPServerContext serverContext,
176                   final HTTPOperationLoggerConfig config,
177                   final ArgumentParser parser)
178         throws LDAPException
179  {
180    // No initialization will be performed by default.
181  }
182
183
184
185  /**
186   * {@inheritDoc}
187   */
188  public boolean isConfigurationAcceptable(
189                      final HTTPOperationLoggerConfig config,
190                      final ArgumentParser parser,
191                      final List<String> unacceptableReasons)
192  {
193    // No extended validation will be performed by default.
194    return true;
195  }
196
197
198
199  /**
200   * {@inheritDoc}
201   */
202  public ResultCode applyConfiguration(final HTTPOperationLoggerConfig config,
203                                       final ArgumentParser parser,
204                                       final List<String> adminActionsRequired,
205                                       final List<String> messages)
206  {
207    // By default, no configuration changes will be applied.  If there are any
208    // arguments, then add an admin action message indicating that the extension
209    // needs to be restarted for any changes to take effect.
210    if (! parser.getNamedArguments().isEmpty())
211    {
212      adminActionsRequired.add(
213           "No configuration change has actually been applied.  The new " +
214                "configuration will not take effect until this scripted HTTP " +
215                "operation logger is disabled and re-enabled or until the " +
216                "server is restarted.");
217    }
218
219    return ResultCode.SUCCESS;
220  }
221
222
223
224  /**
225   * Performs any cleanup which may be necessary when this HTTP operation logger
226   * is to be taken out of service.
227   */
228  public void finalizeHTTPOperationLogger()
229  {
230    // No implementation is required.
231  }
232
233
234
235  /**
236   * Logs information about a servlet request that has been received from the
237   * client.
238   *
239   * @param  request   An object with information about the request received
240   *                   from the client.
241   * @param  stateMap  An empty map which may be updated to hold state
242   *                   information that can be used to correlate information
243   *                   between the request and response.  The same map instance
244   *                   will be passed to the {@link #logResponse} method.
245   */
246  public void logRequest(final HttpServletRequest request,
247                         final Map<String,Object> stateMap)
248  {
249    // No processing performed by default.
250  }
251
252
253
254  /**
255   * Logs information about a servlet response to be returned to the client.
256   *
257   * @param  request   An object with information about the request received
258   *                   from the client.
259   * @param  response  An object with information about the response to be
260   *                   returned to the client.
261   * @param  stateMap  A map containing state any information added while
262   *                   processing the {@link #logRequest} method.
263   */
264  public void logResponse(final HttpServletRequest request,
265                          final HttpServletResponse response,
266                          final Map<String,Object> stateMap)
267  {
268    // No processing performed by default.
269  }
270}