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