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