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