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 *      Copyright 2011-2018 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 time the request was received, in
111   * milliseconds since January 1, 1960, UTC, as reported by
112   * {@code System.currentTimeMillis()}.  It will always be present in the state
113   * map when the {@link #logRequest}  and {@link #logResponse} methods are
114   * invoked.  The value associated with this key will be a
115   * {@code java.lang.Long} object.
116   */
117  public static final String STATE_KEY_REQUEST_TIME_MILLIS =
118       "___request_time_millis___";
119
120
121
122  /**
123   * The key that will be used to hold the time the request was received, in
124   * nanoseconds, as reported by {@code System.nanoTime()}.  It will always be
125   * present in the state map when the {@link #logRequest}  and
126   * {@link #logResponse} methods are invoked.  The value associated with this
127   * key will be a {@code java.lang.Long} object.
128   */
129  public static final String STATE_KEY_REQUEST_TIME_NANOS =
130       "___request_time_nanos___";
131
132
133
134  /**
135   * The key that will be used to hold the response content length in the state
136   * map.  It will always be present in the state map when the
137   * {@link #logResponse} method is invoked.  The value associated with this key
138   * will be a {@code java.lang.Long} object.
139   */
140  public static final String STATE_KEY_RESPONSE_CONTENT_LENGTH =
141       "___response_content_length___";
142
143
144
145  /**
146   * The key that will be used to hold the operation processing time in
147   * milliseconds.  It will always be present in the state map when the
148   * {@link #logResponse} method is invoked.  The value associated with this key
149   * will be a {@code java.lang.Long} object.
150   */
151  public static final String STATE_KEY_PROCESSING_TIME_MILLIS =
152       "___processing_time_millis___";
153
154
155
156  /**
157   * The key that will be used to hold the operation processing time in
158   * nanoseconds.  It will always be present in the state map when the
159   * {@link #logResponse} method is invoked.  The value associated with this key
160   * will be a {@code java.lang.Long} object.
161   */
162  public static final String STATE_KEY_PROCESSING_TIME_NANOS =
163       "___processing_time_nanos___";
164
165
166
167  /**
168   * The key that will be used to hold the set of cookies included in the
169   * response to the client.  It will always be present in the state map when
170   * the {@link #logResponse} method is invoked.  The value associated with this
171   * key will be a {@code java.util.List&lt;javax.servlet.http.Cookie&gt;}
172   * object.
173   */
174  public static final String STATE_KEY_RESPONSE_COOKIES =
175       "___response_cookies___";
176
177
178
179  /**
180   * Creates a new instance of this HTTP operation logger.  All HTTP operation
181   * logger implementations must include a default constructor, but any
182   * initialization should generally be done in the
183   * {@code initializeHTTPOperationLogger} method.
184   */
185  public HTTPOperationLogger()
186  {
187    // No implementation is required.
188  }
189
190
191
192  /**
193   * {@inheritDoc}
194   */
195  public abstract String getExtensionName();
196
197
198
199  /**
200   * {@inheritDoc}
201   */
202  public abstract String[] getExtensionDescription();
203
204
205
206  /**
207   * {@inheritDoc}
208   */
209  public void defineConfigArguments(final ArgumentParser parser)
210         throws ArgumentException
211  {
212    // No arguments will be allowed by default.
213  }
214
215
216
217  /**
218   * Initializes this HTTP operation logger.
219   *
220   * @param  serverContext  A handle to the server context for the server in
221   *                        which this extension is running.
222   * @param  config         The general configuration for this HTTP operation
223   *                        logger.
224   * @param  parser         The argument parser which has been initialized from
225   *                        the configuration for this HTTP operation logger.
226   *
227   * @throws  LDAPException  If a problem occurs while initializing this HTTP
228   *                         operation logger.
229   */
230  public void initializeHTTPOperationLogger(
231                   final HTTPServerContext serverContext,
232                   final HTTPOperationLoggerConfig config,
233                   final ArgumentParser parser)
234         throws LDAPException
235  {
236    // No initialization will be performed by default.
237  }
238
239
240
241  /**
242   * {@inheritDoc}
243   */
244  public boolean isConfigurationAcceptable(
245                      final HTTPOperationLoggerConfig config,
246                      final ArgumentParser parser,
247                      final List<String> unacceptableReasons)
248  {
249    // No extended validation will be performed by default.
250    return true;
251  }
252
253
254
255  /**
256   * {@inheritDoc}
257   */
258  public ResultCode applyConfiguration(final HTTPOperationLoggerConfig config,
259                                       final ArgumentParser parser,
260                                       final List<String> adminActionsRequired,
261                                       final List<String> messages)
262  {
263    // By default, no configuration changes will be applied.  If there are any
264    // arguments, then add an admin action message indicating that the extension
265    // needs to be restarted for any changes to take effect.
266    if (! parser.getNamedArguments().isEmpty())
267    {
268      adminActionsRequired.add(
269           "No configuration change has actually been applied.  The new " +
270                "configuration will not take effect until this HTTP " +
271                "operation logger is disabled and re-enabled or until the " +
272                "server is restarted.");
273    }
274
275    return ResultCode.SUCCESS;
276  }
277
278
279
280  /**
281   * Performs any cleanup which may be necessary when this HTTP operation logger
282   * is to be taken out of service.
283   */
284  public void finalizeHTTPOperationLogger()
285  {
286    // No implementation is required.
287  }
288
289
290
291  /**
292   * Logs information about a servlet request that has been received from the
293   * client.
294   *
295   * @param  request   An object with information about the request received
296   *                   from the client.
297   * @param  stateMap  An empty map which may be updated to hold state
298   *                   information that can be used to correlate information
299   *                   between the request and response.  The same map instance
300   *                   will be passed to the {@link #logResponse} method.
301   */
302  public void logRequest(final HttpServletRequest request,
303                         final Map<String,Object> stateMap)
304  {
305    // No processing performed by default.
306  }
307
308
309
310  /**
311   * Logs information about a servlet response to be returned to the client.
312   *
313   * @param  request   An object with information about the request received
314   *                   from the client.
315   * @param  response  An object with information about the response to be
316   *                   returned to the client.
317   * @param  stateMap  A map containing state any information added while
318   *                   processing the {@link #logRequest} method.
319   */
320  public void logResponse(final HttpServletRequest request,
321                          final HttpServletResponse response,
322                          final Map<String,Object> stateMap)
323  {
324    // No processing performed by default.
325  }
326
327
328
329  /**
330   * {@inheritDoc}
331   */
332  public Map<List<String>,String> getExamplesArgumentSets()
333  {
334    return Collections.emptyMap();
335  }
336}