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 }