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.api;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import javax.servlet.http.HttpServletRequest;
036import javax.servlet.http.HttpServletResponse;
037
038import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
039import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
040import com.unboundid.directory.sdk.common.internal.Reconfigurable;
041import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
042import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
043import com.unboundid.directory.sdk.http.config.HTTPOperationLoggerConfig;
044import com.unboundid.directory.sdk.http.types.HTTPServerContext;
045import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
046import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.util.Extensible;
050import com.unboundid.util.ThreadSafety;
051import com.unboundid.util.ThreadSafetyLevel;
052import com.unboundid.util.args.ArgumentException;
053import com.unboundid.util.args.ArgumentParser;
054
055
056
057/**
058 * This class defines an API that must be implemented by extensions which
059 * record information about interaction with HTTP clients.  HTTP operation
060 * loggers may write information to files, but they may also write to other
061 * locations, including databases, message, queues, e-mail messages, or other
062 * targets.
063 * <BR>
064 * <H2>Configuring HTTP Operation Loggers</H2>
065 * In order to configure an HTTP operation logger created using this API, use a
066 * command like:
067 * <PRE>
068 *      dsconfig create-log-publisher \
069 *           --publisher-name "<I>{logger-name}</I>" \
070 *           --type third-party-http-operation \
071 *           --set enabled:true \
072 *           --set "extension-class:<I>{class-name}</I>" \
073 *           --set "extension-argument:<I>{name=value}</I>"
074 * </PRE>
075 * where "<I>{logger-name}</I>" is the name to use for the HTTP operation logger
076 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java class
077 * that extends
078 * {@code com.unboundid.directory.sdk.common.ds.HTTPOperationLogger}, and
079 * "<I>{name=value}</I>" represents name-value pairs for any arguments to
080 * provide to the logger.  If multiple arguments should be provided to the
081 * logger, then the "<CODE>--set extension-argument:<I>{name=value}</I></CODE>"
082 * option should be provided multiple times.
083 *
084 * @see  com.unboundid.directory.sdk.http.scripting.ScriptedHTTPOperationLogger
085 */
086@Extensible()
087@DirectoryServerExtension()
088@DirectoryProxyServerExtension(appliesToLocalContent=true,
089     appliesToRemoteContent=true)
090@MetricsEngineExtension()
091@BrokerExtension()
092@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
093public abstract class HTTPOperationLogger
094       implements UnboundIDExtension,
095                  Reconfigurable<HTTPOperationLoggerConfig>,
096                  ExampleUsageProvider
097{
098  /**
099   * The key that will be used to hold the request ID for the associated HTTP
100   * request.  It will always be present in the state map when the
101   * {@link #logRequest}  and {@link #logResponse} methods are invoked.  The
102   * value associated with this key will be a {@code java.lang.Long} object.
103   */
104  public static final String STATE_KEY_REQUEST_ID =
105       "___request_id___";
106
107
108
109  /**
110   * The key that will be used to hold the correlation ID for the associated
111   * HTTP request.  It will be present in the state map when the
112   * {@link #logRequest} and {@link #logResponse} methods are invoked, but only
113   * if the HTTP Connection Handler is configured to use correlation IDs.  The
114   * value associated with this key will be a {@code java.lang.String} object.
115   */
116  public static final String STATE_KEY_CORRELATION_ID =
117      "___correlation_id___";
118
119
120
121  /**
122   * The key that will be used to hold the time the request was received, in
123   * milliseconds since January 1, 1960, UTC, as reported by
124   * {@code System.currentTimeMillis()}.  It will always be present in the state
125   * map when the {@link #logRequest}  and {@link #logResponse} methods are
126   * invoked.  The value associated with this key will be a
127   * {@code java.lang.Long} object.
128   */
129  public static final String STATE_KEY_REQUEST_TIME_MILLIS =
130       "___request_time_millis___";
131
132
133
134  /**
135   * The key that will be used to hold the time the request was received, in
136   * nanoseconds, as reported by {@code System.nanoTime()}.  It will always be
137   * present in the state map when the {@link #logRequest}  and
138   * {@link #logResponse} methods are invoked.  The value associated with this
139   * key will be a {@code java.lang.Long} object.
140   */
141  public static final String STATE_KEY_REQUEST_TIME_NANOS =
142       "___request_time_nanos___";
143
144
145
146  /**
147   * The key that will be used to hold the response content length in the state
148   * map.  It will always be present in the state map when the
149   * {@link #logResponse} method is invoked.  The value associated with this key
150   * will be a {@code java.lang.Long} object.
151   */
152  public static final String STATE_KEY_RESPONSE_CONTENT_LENGTH =
153       "___response_content_length___";
154
155
156
157  /**
158   * The key that will be used to hold the operation processing time in
159   * milliseconds.  It will always be present in the state map when the
160   * {@link #logResponse} method is invoked.  The value associated with this key
161   * will be a {@code java.lang.Long} object.
162   */
163  public static final String STATE_KEY_PROCESSING_TIME_MILLIS =
164       "___processing_time_millis___";
165
166
167
168  /**
169   * The key that will be used to hold the operation processing time in
170   * nanoseconds.  It will always be present in the state map when the
171   * {@link #logResponse} method is invoked.  The value associated with this key
172   * will be a {@code java.lang.Long} object.
173   */
174  public static final String STATE_KEY_PROCESSING_TIME_NANOS =
175       "___processing_time_nanos___";
176
177
178
179  /**
180   * The key that will be used to hold the set of cookies included in the
181   * response to the client.  It will always be present in the state map when
182   * the {@link #logResponse} method is invoked.  The value associated with this
183   * key will be a {@code java.util.List&lt;javax.servlet.http.Cookie&gt;}
184   * object.
185   */
186  public static final String STATE_KEY_RESPONSE_COOKIES =
187       "___response_cookies___";
188
189
190
191  /**
192   * Creates a new instance of this HTTP operation logger.  All HTTP operation
193   * logger implementations must include a default constructor, but any
194   * initialization should generally be done in the
195   * {@code initializeHTTPOperationLogger} method.
196   */
197  public HTTPOperationLogger()
198  {
199    // No implementation is required.
200  }
201
202
203
204  /**
205   * {@inheritDoc}
206   */
207  public abstract String getExtensionName();
208
209
210
211  /**
212   * {@inheritDoc}
213   */
214  public abstract String[] getExtensionDescription();
215
216
217
218  /**
219   * {@inheritDoc}
220   */
221  public void defineConfigArguments(final ArgumentParser parser)
222         throws ArgumentException
223  {
224    // No arguments will be allowed by default.
225  }
226
227
228
229  /**
230   * Initializes this HTTP operation logger.
231   *
232   * @param  serverContext  A handle to the server context for the server in
233   *                        which this extension is running.
234   * @param  config         The general configuration for this HTTP operation
235   *                        logger.
236   * @param  parser         The argument parser which has been initialized from
237   *                        the configuration for this HTTP operation logger.
238   *
239   * @throws  LDAPException  If a problem occurs while initializing this HTTP
240   *                         operation logger.
241   */
242  public void initializeHTTPOperationLogger(
243                   final HTTPServerContext serverContext,
244                   final HTTPOperationLoggerConfig config,
245                   final ArgumentParser parser)
246         throws LDAPException
247  {
248    // No initialization will be performed by default.
249  }
250
251
252
253  /**
254   * {@inheritDoc}
255   */
256  public boolean isConfigurationAcceptable(
257                      final HTTPOperationLoggerConfig config,
258                      final ArgumentParser parser,
259                      final List<String> unacceptableReasons)
260  {
261    // No extended validation will be performed by default.
262    return true;
263  }
264
265
266
267  /**
268   * {@inheritDoc}
269   */
270  public ResultCode applyConfiguration(final HTTPOperationLoggerConfig config,
271                                       final ArgumentParser parser,
272                                       final List<String> adminActionsRequired,
273                                       final List<String> messages)
274  {
275    // By default, no configuration changes will be applied.  If there are any
276    // arguments, then add an admin action message indicating that the extension
277    // needs to be restarted for any changes to take effect.
278    if (! parser.getNamedArguments().isEmpty())
279    {
280      adminActionsRequired.add(
281           "No configuration change has actually been applied.  The new " +
282                "configuration will not take effect until this HTTP " +
283                "operation logger is disabled and re-enabled or until the " +
284                "server is restarted.");
285    }
286
287    return ResultCode.SUCCESS;
288  }
289
290
291
292  /**
293   * Performs any cleanup which may be necessary when this HTTP operation logger
294   * is to be taken out of service.
295   */
296  public void finalizeHTTPOperationLogger()
297  {
298    // No implementation is required.
299  }
300
301
302
303  /**
304   * Logs information about a servlet request that has been received from the
305   * client.
306   *
307   * @param  request   An object with information about the request received
308   *                   from the client.
309   * @param  stateMap  An empty map which may be updated to hold state
310   *                   information that can be used to correlate information
311   *                   between the request and response.  The same map instance
312   *                   will be passed to the {@link #logResponse} method.
313   */
314  public void logRequest(final HttpServletRequest request,
315                         final Map<String,Object> stateMap)
316  {
317    // No processing performed by default.
318  }
319
320
321
322  /**
323   * Logs information about a servlet response to be returned to the client.
324   *
325   * @param  request   An object with information about the request received
326   *                   from the client.
327   * @param  response  An object with information about the response to be
328   *                   returned to the client.
329   * @param  stateMap  A map containing state any information added while
330   *                   processing the {@link #logRequest} method.
331   */
332  public void logResponse(final HttpServletRequest request,
333                          final HttpServletResponse response,
334                          final Map<String,Object> stateMap)
335  {
336    // No processing performed by default.
337  }
338
339
340
341  /**
342   * {@inheritDoc}
343   */
344  public Map<List<String>,String> getExamplesArgumentSets()
345  {
346    return Collections.emptyMap();
347  }
348}