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-2021 Ping Identity Corporation
026 */
027package com.unboundid.directory.sdk.http.scripting;
028
029
030
031import java.util.Collections;
032import java.util.List;
033import java.util.Map;
034
035import javax.servlet.Filter;
036import javax.servlet.http.HttpServlet;
037
038import com.unboundid.directory.sdk.broker.internal.BrokerExtension;
039import com.unboundid.directory.sdk.common.internal.Reconfigurable;
040import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
041import com.unboundid.directory.sdk.http.config.HTTPServletExtensionConfig;
042import com.unboundid.directory.sdk.http.types.HTTPServerContext;
043import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
044import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
045import com.unboundid.ldap.sdk.LDAPException;
046import com.unboundid.ldap.sdk.ResultCode;
047import com.unboundid.util.Extensible;
048import com.unboundid.util.ThreadSafety;
049import com.unboundid.util.ThreadSafetyLevel;
050import com.unboundid.util.args.ArgumentException;
051import 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@BrokerExtension()
086@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
087public 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}