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