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 2013 UnboundID Corp.
026 */
027 package com.unboundid.directory.sdk.common.api;
028
029
030 import com.unboundid.directory.sdk.broker.internal.IdentityBrokerExtension;
031 import com.unboundid.directory.sdk.common.config.VelocityContextProviderConfig;
032 import com.unboundid.directory.sdk.common.internal.ExampleUsageProvider;
033 import com.unboundid.directory.sdk.common.internal.Reconfigurable;
034 import com.unboundid.directory.sdk.common.internal.UnboundIDExtension;
035 import com.unboundid.directory.sdk.common.types.ServerContext;
036 import com.unboundid.directory.sdk.common.types.VelocityContext;
037 import com.unboundid.directory.sdk.ds.internal.DirectoryServerExtension;
038 import com.unboundid.directory.sdk.metrics.internal.MetricsEngineExtension;
039 import com.unboundid.directory.sdk.proxy.internal.DirectoryProxyServerExtension;
040 import com.unboundid.directory.sdk.sync.internal.SynchronizationServerExtension;
041 import com.unboundid.ldap.sdk.LDAPException;
042 import com.unboundid.ldap.sdk.ResultCode;
043 import com.unboundid.util.Extensible;
044 import com.unboundid.util.ThreadSafety;
045 import com.unboundid.util.ThreadSafetyLevel;
046 import com.unboundid.util.args.ArgumentException;
047 import com.unboundid.util.args.ArgumentParser;
048
049 import javax.servlet.http.HttpServletRequest;
050 import javax.servlet.http.HttpServletResponse;
051 import javax.servlet.http.HttpSession;
052 import java.util.List;
053
054 import static com.unboundid.directory.sdk.common.config
055 .VelocityContextProviderConfig.ObjectScope;
056 import static com.unboundid.directory.sdk.common.config
057 .VelocityContextProviderConfig.ObjectScope.*;
058
059
060 /**
061 * This class defines an API that must be implemented by extensions which
062 * contribute content to server pages authored as Velocity templates.
063 * These pages are rendered by the Velocity HTTP Servlet Extension included
064 * with the server. During rendering of a Velocity page, the template is merged
065 * with a 'context' that provides values for variables, properties, and method
066 * references in the template.
067 * <p/>
068 * A context provider can be restricted to contributing content for certain
069 * pages by specifying one or more included or excluded views names. A view
070 * name is the URL request path to to a template that does not include the
071 * HTTP servlet extension's base context path, nor a starting path separator.
072 * So for example if a template was accessible by the URL
073 * http://localhost:8080/view/path/to/template the view name would be
074 * 'path/to/template'.
075 * <H2>Configuring Velocity Context Providers</H2>
076 * In order to configure a Velocity context provider created using this API,
077 * use a command like:
078 * <PRE>
079 * dsconfig create-virtual-attribute \
080 * --extension-name "<I>{extension}</I>" \
081 * --provider-name "<I>{provider}</I>" \
082 * --type third-party \
083 * --set enabled:true \
084 * --set "extension-class:<I>{class-name}</I>" \
085 * --set "extension-argument:<I>{name=value}</I>"
086 * </PRE>
087 * where "<I>{extension}</I>" is the name of the Velocity HTTP servlet
088 * extension, ("Velocity" by default)
089 * "<I>{provider}</I>" is the name to use for the Velocity context provider
090 * instance, "<I>{class-name}</I>" is the fully-qualified name of the Java
091 * class that extends
092 * {@code com.unboundid.directory.sdk.common.api.VelocityContextProvider},
093 * and "<I>{name=value}</I>" represents name-value pairs for any arguments to
094 * provide to the virtual attribute provider. If multiple arguments should be
095 * provided to the virtual attribute provider, then the
096 * "<CODE>--set extension-argument:<I>{name=value}</I></CODE>" option should be
097 * provided multiple times.
098 */
099 @Extensible()
100 @DirectoryServerExtension()
101 @DirectoryProxyServerExtension(appliesToLocalContent = true,
102 appliesToRemoteContent = false)
103 @SynchronizationServerExtension(appliesToLocalContent = true,
104 appliesToSynchronizedContent = false)
105 @MetricsEngineExtension()
106 @IdentityBrokerExtension()
107 @ThreadSafety(level = ThreadSafetyLevel.INTERFACE_THREADSAFE)
108 public abstract class VelocityContextProvider
109 implements UnboundIDExtension,
110 Reconfigurable<VelocityContextProviderConfig>,
111 ExampleUsageProvider
112 {
113
114
115 // The current configuration.
116 private VelocityContextProviderConfig config;
117
118
119
120 /**
121 * {@inheritDoc}
122 */
123 public abstract String getExtensionName();
124
125
126
127 /**
128 * {@inheritDoc}
129 */
130 public abstract String[] getExtensionDescription();
131
132
133
134 /**
135 * {@inheritDoc}
136 */
137 public void defineConfigArguments(final ArgumentParser parser)
138 throws ArgumentException
139 {
140 // No arguments will be allowed by default.
141 }
142
143
144
145 /**
146 * Initializes this Velocity context provider.
147 *
148 * @param serverContext A handle to the server context for the server in
149 * which this extension is running.
150 * @param config The general configuration for this Velocity context
151 * provider.
152 * @param parser The argument parser which has been initialized from
153 * the configuration for this Velocity context
154 * provider.
155 * @throws LDAPException If a problem occurs while initializing this context
156 * provider.
157 */
158 public void initializeVelocityContextProvider(
159 final ServerContext serverContext,
160 final VelocityContextProviderConfig config,
161 final ArgumentParser parser)
162 throws LDAPException
163 {
164 this.config = config;
165 }
166
167
168
169 /**
170 * {@inheritDoc}
171 */
172 public boolean isConfigurationAcceptable(
173 final VelocityContextProviderConfig config,
174 final ArgumentParser parser,
175 final List<String> unacceptableReasons)
176 {
177 // No extended validation will be performed by default.
178 return true;
179 }
180
181
182
183 /**
184 * {@inheritDoc}
185 */
186 public ResultCode applyConfiguration(
187 final VelocityContextProviderConfig config,
188 final ArgumentParser parser,
189 final List<String> adminActionsRequired,
190 final List<String> messages)
191 {
192 // By default, no configuration changes will be applied. If there are any
193 // arguments, then add an admin action message indicating that the extension
194 // needs to be restarted for any changes to take effect.
195 if (!parser.getNamedArguments().isEmpty())
196 {
197 adminActionsRequired.add(
198 "No configuration change has actually been applied. The new " +
199 "configuration will not take effect until this Velocity " +
200 "context provider is disabled and re-enabled or until the " +
201 "server is restarted.");
202 }
203
204 return ResultCode.SUCCESS;
205 }
206
207
208
209 /**
210 * Performs any cleanup which may be necessary when this virtual attribute
211 * provider is to be taken out of service.
212 */
213 public void finalizeVelocityContextProvider()
214 {
215 // No implementation is required.
216 }
217
218
219
220 /**
221 * Return an object to be placed into a Velocity context for rending a
222 * template.
223 *
224 * @param context to update.
225 * @param request for the the view implemented by a template.
226 * @param response to the client.
227 */
228 public abstract void updateContext(VelocityContext context,
229 HttpServletRequest request,
230 HttpServletResponse response);
231
232
233
234 /**
235 * Gets an object from the current user session or servlet context
236 * depending on the object scope currently configured for this provider.
237 * This method can be used as a convenience for providers the maintain
238 * a set of context objects for a particular scope.
239 *
240 * @param <T> the type of object to return. If an object exists in
241 * the current scope with the given name but is not of type
242 * T this method returns {@code null}
243 *
244 * @param name of the object.
245 * @param request current user request.
246 *
247 * @return the named object or {@code null} if no object exists by the
248 * provided name or an input parameter is (@code null}.
249 */
250 protected <T> T getNamedObject(final String name,
251 final HttpServletRequest request)
252 {
253 T object = null;
254 if (this.config != null)
255 {
256 object = getNamedObject(name, request, config.getObjectScope());
257 }
258 return object;
259 }
260
261
262
263 /**
264 * Stores an object in current user request, session or servlet context
265 * depending on the object scope currently configured for this provider.
266 * This method can be used as a convenience for providers the maintain
267 * a set of context objects for a particular scope.
268 *
269 * @param name of the object.
270 * @param object to store.
271 * @param request current user request.
272 */
273 protected void setNamedObject(final String name,
274 final Object object,
275 final HttpServletRequest request)
276 {
277 if (this.config != null)
278 {
279 setNamedObject(name, object, request, config.getObjectScope());
280 }
281 }
282
283
284
285 /**
286 * Gets an object from the current user session or servlet context
287 * depending on the object scope currently configured for this provider.
288 * This method can be used as a convenience for providers the maintain
289 * a set of context objects for a particular scope.
290 *
291 * @param <T> the type of object to return. If an object exists in
292 * the current scope with the given name but is not of type
293 * T this method returns {@code null}
294 *
295 * @param name of the object.
296 * @param request current user request.
297 * @param scope from which to retrieve the object.
298 * @return the named object or {@code null} if no object exists by the
299 * provided name or an input parameter is (@code null}.
300 */
301 @SuppressWarnings("unchecked")
302 public static <T> T getNamedObject(final String name,
303 final HttpServletRequest request,
304 final ObjectScope scope)
305 {
306 T object = null;
307 Object o = null;
308 if (name != null && request != null)
309 {
310 if (REQUEST.equals(scope))
311 {
312 o = request.getAttribute(name);
313 }
314 else if (SESSION.equals(scope))
315 {
316 HttpSession session = request.getSession(false);
317 if (session != null)
318 {
319 o = session.getAttribute(name);
320 }
321 }
322 else if (APPLICATION.equals(scope))
323 {
324 o = request.getServletContext().getAttribute(name);
325 }
326 }
327 try
328 {
329 object = (T) o;
330 }
331 catch (ClassCastException cce)
332 {
333 // ignore
334 }
335 return object;
336 }
337
338
339
340 /**
341 * Stores an object in current user request, session or servlet context
342 * depending on the object scope currently configured for this provider.
343 * This method can be used as a convenience for providers the maintain
344 * a set of context objects for a particular scope.
345 *
346 * @param name of the object.
347 * @param object to store.
348 * @param request current user request.
349 * @param scope in which to set the object.
350 */
351 public static void setNamedObject(final String name,
352 final Object object,
353 final HttpServletRequest request,
354 final ObjectScope scope)
355 {
356 if (scope != null && request != null && name != null)
357 {
358 if (REQUEST.equals(scope))
359 {
360 request.setAttribute(name, object);
361 }
362 else if (SESSION.equals(scope))
363 {
364 HttpSession session = request.getSession(true);
365 session.setAttribute(name, object);
366 }
367 else if (APPLICATION.equals(scope))
368 {
369 request.getServletContext().setAttribute(name, object);
370 }
371 }
372 }
373
374 }