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    }