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.api; 028 029 030 031import java.util.Collections; 032import java.util.List; 033import java.util.Map; 034 035import javax.servlet.Filter; 036import javax.servlet.http.HttpServlet; 037 038import com.unboundid.directory.sdk.broker.internal.BrokerExtension; 039import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider; 040import com.unboundid.directory.sdk.common.internal.Reconfigurable; 041import com.unboundid.directory.sdk.common.internal.UnboundIDExtension; 042import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension; 043import com.unboundid.directory.sdk.http.config.HTTPServletExtensionConfig; 044import com.unboundid.directory.sdk.http.types.HTTPServerContext; 045import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension; 046import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension; 047import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.ResultCode; 050import com.unboundid.util.Extensible; 051import com.unboundid.util.ThreadSafety; 052import com.unboundid.util.ThreadSafetyLevel; 053import com.unboundid.util.args.ArgumentException; 054import com.unboundid.util.args.ArgumentParser; 055 056 057 058/** 059 * This class defines an API that must be implemented by extensions which create 060 * servlets for use with an HTTP connection handler. 061 * <BR> 062 * <H2>Configuring HTTP Servlet Extensions</H2> 063 * In order to configure an HTTP servlet extension created using this API, use a 064 * command like: 065 * <PRE> 066 * dsconfig create-http-servlet-extension \ 067 * --extension-name "<I>{extension-name}</I>" \ 068 * --type third-party \ 069 * --set "extension-class:<I>{class-name}</I>" \ 070 * --set "extension-argument:<I>{name=value}</I>" 071 * </PRE> 072 * where "<I>{extension-name}</I>" is the name to use for the HTTP servlet 073 * extension instance, "<I>{class-name}</I>" is the fully-qualified name of the 074 * Java class that extends 075 * {@code com.unboundid.directory.sdk.ds.api.HTTPServletExtension}, 076 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to 077 * provide to the HTTP servlet extension. If multiple arguments should be 078 * provided to the HTTP servlet extension, then the 079 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be 080 * provided multiple times. 081 * <BR><BR> 082 * Note that the server also provides the ability to run full web applications, 083 * by creating a Web Application Extension and specifying the necessary 084 * properties. If that web application needs to perform internal operations or 085 * interact with the server in any way, then the 086 * {@link com.unboundid.directory.sdk.http.types.WebAppServerContextFactory} 087 * class may be called from the web application in order to obtain a server 088 * context. 089 * 090 * @see com.unboundid.directory.sdk.http.scripting.ScriptedHTTPServletExtension 091 */ 092@Extensible() 093@DirectoryServerExtension() 094@DirectoryProxyServerExtension(appliesToLocalContent=true, 095 appliesToRemoteContent=true) 096@MetricsEngineExtension() 097@BrokerExtension() 098@SynchronizationServerExtension(appliesToLocalContent=true, 099 appliesToSynchronizedContent=true) 100@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 101public abstract class HTTPServletExtension 102 implements UnboundIDExtension, 103 Reconfigurable<HTTPServletExtensionConfig>, 104 ExampleUsageProvider 105{ 106 /** 107 * Creates a new instance of this HTTP servlet extension. All HTTP servlet 108 * extension implementations must include a default constructor, but any 109 * initialization should generally be done in the 110 * {@code createServlet} method. 111 */ 112 public HTTPServletExtension() 113 { 114 // No implementation is required. 115 } 116 117 118 119 /** 120 * {@inheritDoc} 121 */ 122 public abstract String getExtensionName(); 123 124 125 126 /** 127 * {@inheritDoc} 128 */ 129 public abstract String[] getExtensionDescription(); 130 131 132 133 /** 134 * {@inheritDoc} 135 */ 136 public void defineConfigArguments(final ArgumentParser parser) 137 throws ArgumentException 138 { 139 // No arguments will be allowed by default. 140 } 141 142 143 144 /** 145 * {@inheritDoc} 146 */ 147 public boolean isConfigurationAcceptable( 148 final HTTPServletExtensionConfig config, 149 final ArgumentParser parser, 150 final List<String> unacceptableReasons) 151 { 152 // No extended validation will be performed by default. 153 return true; 154 } 155 156 157 158 /** 159 * {@inheritDoc} 160 */ 161 public ResultCode applyConfiguration(final HTTPServletExtensionConfig config, 162 final ArgumentParser parser, 163 final List<String> adminActionsRequired, 164 final List<String> messages) 165 { 166 // By default, no configuration changes will be applied. If there are any 167 // arguments, then add an admin action message indicating that the extension 168 // needs to be restarted for any changes to take effect. 169 if (! parser.getNamedArguments().isEmpty()) 170 { 171 adminActionsRequired.add( 172 "No configuration change has actually been applied. The new " + 173 "configuration will not take effect until this HTTP servlet " + 174 "extension is disabled and re-enabled or until the server is " + 175 "restarted."); 176 } 177 178 return ResultCode.SUCCESS; 179 } 180 181 182 183 /** 184 * {@inheritDoc} 185 */ 186 public Map<List<String>,String> getExamplesArgumentSets() 187 { 188 return Collections.emptyMap(); 189 } 190 191 192 193 /** 194 * Creates an HTTP servlet extension using the provided information. 195 * 196 * @param serverContext A handle to the server context for the server in 197 * which this extension is running. 198 * @param config The general configuration for this HTTP servlet 199 * extension. 200 * @param parser The argument parser which has been initialized from 201 * the configuration for this HTTP servlet extension. 202 * 203 * @return The HTTP servlet that has been created. 204 * 205 * @throws LDAPException If a problem is encountered while attempting to 206 * create the HTTP servlet. 207 */ 208 public abstract HttpServlet createServlet( 209 final HTTPServerContext serverContext, 210 final HTTPServletExtensionConfig config, 211 final ArgumentParser parser) 212 throws LDAPException; 213 214 215 216 /** 217 * Retrieves a list of the request paths for which the associated servlet 218 * should be invoked. This method will be called after the 219 * {@link #createServlet} method has been used to create the servlet instance. 220 * 221 * @return A list of the request paths for which the associated servlet 222 * should be invoked. 223 */ 224 public abstract List<String> getServletPaths(); 225 226 227 228 /** 229 * Retrieves a map of initialization parameters that should be provided to the 230 * servlet when it is initialized. 231 * 232 * @return A map of initialization parameters that should be provided to the 233 * servlet when it is initialized, or an empty map if no 234 * initialization parameters are needed. 235 */ 236 public Map<String,String> getServletInitParameters() 237 { 238 return Collections.emptyMap(); 239 } 240 241 242 243 /** 244 * Retrieves the order in which the servlet should be started. A value 245 * greater than or equal to zero guarantees that the servlet will be started 246 * as soon as the servlet engine has been started, in order of ascending 247 * servlet init order values, before the {@code doPostRegistrationProcessing} 248 * method has been called. If the value is less than zero, the servlet may 249 * not be started until a request is received for one of its registered paths. 250 * 251 * @return The order in which the servlet should be started, or a negative 252 * value if startup order does not matter. 253 */ 254 public int getServletInitOrder() 255 { 256 return -1; 257 } 258 259 260 261 /** 262 * Retrieves a list of servlet filter instances that should be installed with 263 * the created servlet instance, in the order they should be invoked. If the 264 * servlet is to be registered with multiple paths, then these filters will be 265 * installed for all of those paths. 266 * 267 * @return A list of servlet filter instances that should be installed with 268 * the created servlet instance, in the order that they should be 269 * invoked. It may be {@code null} or empty if no servlet filters 270 * should be installed. 271 */ 272 public List<Filter> getServletFilters() 273 { 274 return Collections.emptyList(); 275 } 276 277 278 279 /** 280 * Performs any processing that may be needed after the servlet has been 281 * registered with the servlet engine. If the value returned from 282 * {@link #getServletInitOrder()} is greater than or equal to zero, then the 283 * servlet will have been started before this method is called. If the value 284 * returned from {@code getServletInitOrder()} is negative, then the servlet 285 * may or may not have been started by the time this method is called. 286 * <BR><BR> 287 * Note that the associated servlet can also perform any necessary 288 * initialization processing in the {@code init} method provided by the 289 * servlet API. 290 */ 291 public void doPostRegistrationProcessing() 292 { 293 // No implementation required by default. 294 } 295 296 297 298 /** 299 * Performs any processing that may be needed after the servlet has been 300 * taken out of service and the associated servlet engine has been shut down. 301 * <BR><BR> 302 * Note that the associated servlet can also perform any necessary 303 * finalization processing in the {@code destroy} method provided by the 304 * servlet API. That method will be called after the servlet has been taken 305 * out of service, but before the servlet engine has been shut down. 306 */ 307 public void doPostShutdownProcessing() 308 { 309 // No implementation required by default. 310 } 311}