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