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