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