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    }