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