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