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