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