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<javax.servlet.http.Cookie>}
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 }