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 }