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