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 * Portions Copyright 2011-2024 Ping Identity Corporation 026 */ 027package com.unboundid.directory.sdk.http.scripting; 028 029 030 031import java.util.List; 032import java.util.Map; 033 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletResponse; 036 037import com.unboundid.directory.sdk.broker.internal.BrokerExtension; 038import com.unboundid.directory.sdk.common.internal.Reconfigurable; 039import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 040import com.unboundid.directory.sdk.http.config.HTTPOperationLoggerConfig; 041import com.unboundid.directory.sdk.http.types.HTTPServerContext; 042import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension; 043import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 044import com.unboundid.ldap.sdk.LDAPException; 045import com.unboundid.ldap.sdk.ResultCode; 046import com.unboundid.util.Extensible; 047import com.unboundid.util.ThreadSafety; 048import com.unboundid.util.ThreadSafetyLevel; 049import com.unboundid.util.args.ArgumentException; 050import 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@BrokerExtension() 088@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 089public 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}