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-2014 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.broker.internal.IdentityBrokerExtension;
039    import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
040    import com.unboundid.directory.sdk.common.internal.Reconfigurable;
041    import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
042    import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
043    import com.unboundid.directory.sdk.http.config.HTTPOperationLoggerConfig;
044    import com.unboundid.directory.sdk.http.types.HTTPServerContext;
045    import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
046    import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
047    import com.unboundid.ldap.sdk.LDAPException;
048    import com.unboundid.ldap.sdk.ResultCode;
049    import com.unboundid.util.Extensible;
050    import com.unboundid.util.ThreadSafety;
051    import com.unboundid.util.ThreadSafetyLevel;
052    import com.unboundid.util.args.ArgumentException;
053    import 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    @IdentityBrokerExtension()
092    @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
093    public 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    }