001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.graph; 025 026 import java.security.AccessControlContext; 027 import java.security.AccessController; 028 import javax.security.auth.Subject; 029 import javax.security.auth.callback.CallbackHandler; 030 import javax.security.auth.login.Configuration; 031 import javax.security.auth.login.LoginContext; 032 import javax.security.auth.login.LoginException; 033 import javax.security.auth.spi.LoginModule; 034 import net.jcip.annotations.Immutable; 035 import org.jboss.dna.common.component.ClassLoaderFactory; 036 import org.jboss.dna.common.component.StandardClassLoaderFactory; 037 import org.jboss.dna.common.util.CheckArg; 038 import org.jboss.dna.common.util.Logger; 039 import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector; 040 import org.jboss.dna.graph.mimetype.MimeTypeDetector; 041 import org.jboss.dna.graph.property.NamespaceRegistry; 042 import org.jboss.dna.graph.property.Property; 043 import org.jboss.dna.graph.property.PropertyFactory; 044 import org.jboss.dna.graph.property.ValueFactories; 045 import org.jboss.dna.graph.property.basic.BasicPropertyFactory; 046 import org.jboss.dna.graph.property.basic.SimpleNamespaceRegistry; 047 import org.jboss.dna.graph.property.basic.StandardValueFactories; 048 import org.jboss.dna.graph.property.basic.ThreadSafeNamespaceRegistry; 049 050 /** 051 * An ExecutionContext is a representation of the environment or context in which a component or operation is operating. Some 052 * components require this context to be passed into individual methods, allowing the context to vary with each method invocation. 053 * Other components require the context to be provided before it's used, and will use that context for all its operations (until 054 * it is given a different one). 055 * <p> 056 * ExecutionContext instances are {@link Immutable immutable}, so components may hold onto references to them without concern of 057 * those contexts changing. Contexts may be used to create other contexts that vary the environment and/or security context. For 058 * example, an ExecutionContext could be used to create another context that references the same {@link #getNamespaceRegistry() 059 * namespace registry} but which has a different {@link #getSubject() JAAS subject}. 060 * </p> 061 * 062 * @author Randall Hauch 063 * @author John Verhaeg 064 */ 065 @Immutable 066 public class ExecutionContext implements ClassLoaderFactory, Cloneable { 067 068 private final ClassLoaderFactory classLoaderFactory; 069 private final LoginContext loginContext; 070 private final AccessControlContext accessControlContext; 071 private final Subject subject; 072 private final PropertyFactory propertyFactory; 073 private final ValueFactories valueFactories; 074 private final NamespaceRegistry namespaceRegistry; 075 private final MimeTypeDetector mimeTypeDetector; 076 077 /** 078 * Create an instance of an execution context that uses the {@link AccessController#getContext() current JAAS calling context} 079 * , with default implementations for all other components (including default namespaces in the 080 * {@link #getNamespaceRegistry() namespace registry}. 081 */ 082 public ExecutionContext() { 083 this(null, null, null, null, null, null, null); 084 initializeDefaultNamespaces(this.getNamespaceRegistry()); 085 } 086 087 /** 088 * Create a copy of the supplied execution context. 089 * 090 * @param original the original 091 * @throws IllegalArgumentException if the original is null 092 */ 093 protected ExecutionContext( ExecutionContext original ) { 094 CheckArg.isNotNull(original, "original"); 095 this.loginContext = original.getLoginContext(); 096 this.accessControlContext = original.getAccessControlContext(); 097 this.subject = original.getSubject(); 098 this.namespaceRegistry = original.getNamespaceRegistry(); 099 this.valueFactories = original.getValueFactories(); 100 this.propertyFactory = original.getPropertyFactory(); 101 this.classLoaderFactory = original.getClassLoaderFactory(); 102 this.mimeTypeDetector = original.getMimeTypeDetector(); 103 } 104 105 /** 106 * Create a copy of the supplied execution context, but use the supplied {@link AccessControlContext} instead. 107 * 108 * @param original the original 109 * @param accessControlContext the access control context 110 * @throws IllegalArgumentException if the original or access control context are is null 111 */ 112 protected ExecutionContext( ExecutionContext original, 113 AccessControlContext accessControlContext ) { 114 CheckArg.isNotNull(original, "original"); 115 CheckArg.isNotNull(accessControlContext, "accessControlContext"); 116 this.loginContext = null; 117 this.accessControlContext = accessControlContext; 118 this.subject = Subject.getSubject(this.accessControlContext); 119 this.namespaceRegistry = original.getNamespaceRegistry(); 120 this.valueFactories = original.getValueFactories(); 121 this.propertyFactory = original.getPropertyFactory(); 122 this.classLoaderFactory = original.getClassLoaderFactory(); 123 this.mimeTypeDetector = original.getMimeTypeDetector(); 124 } 125 126 /** 127 * Create a copy of the supplied execution context, but use the supplied {@link LoginContext} instead. 128 * 129 * @param original the original 130 * @param loginContext the login context 131 * @throws IllegalArgumentException if the original or login context are is null 132 */ 133 protected ExecutionContext( ExecutionContext original, 134 LoginContext loginContext ) { 135 CheckArg.isNotNull(original, "original"); 136 CheckArg.isNotNull(loginContext, "loginContext"); 137 this.loginContext = loginContext; 138 this.accessControlContext = null; 139 this.subject = this.loginContext.getSubject(); 140 this.namespaceRegistry = original.getNamespaceRegistry(); 141 this.valueFactories = original.getValueFactories(); 142 this.propertyFactory = original.getPropertyFactory(); 143 this.classLoaderFactory = original.getClassLoaderFactory(); 144 this.mimeTypeDetector = original.getMimeTypeDetector(); 145 } 146 147 /** 148 * Create an instance of the execution context by supplying all parameters. 149 * 150 * @param loginContext the login context, or null if the {@link #getSubject() subject} is to be retrieved from the 151 * {@link AccessController#getContext() current calling context}. 152 * @param accessControlContext the access control context, or null if a {@link LoginContext} is provided or if the 153 * {@link AccessController#getContext() current calling context} should be used 154 * @param namespaceRegistry the namespace registry implementation, or null if a thread-safe version of 155 * {@link SimpleNamespaceRegistry} instance should be used 156 * @param valueFactories the {@link ValueFactories} implementation, or null if a {@link StandardValueFactories} instance 157 * should be used 158 * @param propertyFactory the {@link PropertyFactory} implementation, or null if a {@link BasicPropertyFactory} instance 159 * should be used 160 * @param mimeTypeDetector the {@link MimeTypeDetector} implementation, or null if an {@link ExtensionBasedMimeTypeDetector} 161 * instance should be used 162 * @param classLoaderFactory the {@link ClassLoaderFactory} implementation, or null if a {@link StandardClassLoaderFactory} 163 * instance should be used 164 */ 165 protected ExecutionContext( LoginContext loginContext, 166 AccessControlContext accessControlContext, 167 NamespaceRegistry namespaceRegistry, 168 ValueFactories valueFactories, 169 PropertyFactory propertyFactory, 170 MimeTypeDetector mimeTypeDetector, 171 ClassLoaderFactory classLoaderFactory ) { 172 this.loginContext = loginContext; 173 this.accessControlContext = accessControlContext; 174 if (loginContext == null) { 175 this.subject = Subject.getSubject(accessControlContext == null ? AccessController.getContext() : accessControlContext); 176 } else { 177 this.subject = loginContext.getSubject(); 178 } 179 this.namespaceRegistry = namespaceRegistry != null ? namespaceRegistry : new ThreadSafeNamespaceRegistry( 180 new SimpleNamespaceRegistry()); 181 this.valueFactories = valueFactories == null ? new StandardValueFactories(this.namespaceRegistry) : valueFactories; 182 this.propertyFactory = propertyFactory == null ? new BasicPropertyFactory(this.valueFactories) : propertyFactory; 183 this.classLoaderFactory = classLoaderFactory == null ? new StandardClassLoaderFactory() : classLoaderFactory; 184 this.mimeTypeDetector = mimeTypeDetector != null ? mimeTypeDetector : new ExtensionBasedMimeTypeDetector(); 185 } 186 187 /** 188 * Get the class loader factory used by this context. 189 * 190 * @return the class loader factory implementation; never null 191 */ 192 protected ClassLoaderFactory getClassLoaderFactory() { 193 return classLoaderFactory; 194 } 195 196 /** 197 * Return a logger associated with this context. This logger records only those activities within the context and provide a 198 * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log 199 * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}. 200 * 201 * @param clazz the class that is doing the logging 202 * @return the logger, named after <code>clazz</code>; never null 203 * @see #getLogger(String) 204 */ 205 public Logger getLogger( Class<?> clazz ) { 206 return Logger.getLogger(clazz); 207 } 208 209 /** 210 * Return a logger associated with this context. This logger records only those activities within the context and provide a 211 * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log 212 * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}. 213 * 214 * @param name the name for the logger 215 * @return the logger, named after <code>clazz</code>; never null 216 * @see #getLogger(Class) 217 */ 218 public Logger getLogger( String name ) { 219 return Logger.getLogger(name); 220 } 221 222 /** 223 * Return an object that can be used to determine the MIME type of some content, such as the content of a file. 224 * 225 * @return the detector; never null 226 */ 227 public MimeTypeDetector getMimeTypeDetector() { 228 return this.mimeTypeDetector; 229 } 230 231 /** 232 * Get the {@link AccessControlContext JAAS access control context} for this context. 233 * 234 * @return the access control context; may be <code>null</code> 235 */ 236 public AccessControlContext getAccessControlContext() { 237 return this.accessControlContext; 238 } 239 240 /** 241 * Get the {@link LoginContext JAAS login context} for this context. 242 * 243 * @return the login context; may be <code>null</code> 244 */ 245 public LoginContext getLoginContext() { 246 return this.loginContext; 247 } 248 249 /** 250 * Get the (mutable) namespace registry for this context. 251 * 252 * @return the namespace registry; never <code>null</code> 253 */ 254 public NamespaceRegistry getNamespaceRegistry() { 255 return this.namespaceRegistry; 256 } 257 258 /** 259 * Get the factory for creating {@link Property} objects. 260 * 261 * @return the property factory; never <code>null</code> 262 */ 263 public PropertyFactory getPropertyFactory() { 264 return this.propertyFactory; 265 } 266 267 /** 268 * Get the JAAS subject for which this context was created. 269 * 270 * @return the subject; should never be null if JAAS is used, but will be null if there is no 271 * {@link #getAccessControlContext() access control context} or {@link #getLoginContext() login context}. 272 */ 273 public Subject getSubject() { 274 return this.subject; 275 } 276 277 /** 278 * Get the factories that should be used to create values for {@link Property properties}. 279 * 280 * @return the property value factory; never null 281 */ 282 public ValueFactories getValueFactories() { 283 return this.valueFactories; 284 } 285 286 /** 287 * {@inheritDoc} 288 * 289 * @see org.jboss.dna.common.component.ClassLoaderFactory#getClassLoader(java.lang.String[]) 290 */ 291 public ClassLoader getClassLoader( String... classpath ) { 292 return this.classLoaderFactory.getClassLoader(classpath); 293 } 294 295 /** 296 * Create a new execution context that mirrors this context but that uses the supplied namespace registry. The resulting 297 * context's {@link #getValueFactories() value factories} and {@link #getPropertyFactory() property factory} all make use of 298 * the new namespace registry. 299 * 300 * @param namespaceRegistry the new namespace registry implementation, or null if the default implementation should be used 301 * @return the execution context that is identical with this execution context, but which uses the supplied registry; never 302 * null 303 */ 304 public ExecutionContext with( NamespaceRegistry namespaceRegistry ) { 305 // Don't supply the value factories or property factories, since they'll have to be recreated 306 // to reference the supplied namespace registry ... 307 return new ExecutionContext(this.getLoginContext(), this.getAccessControlContext(), namespaceRegistry, null, null, 308 this.getMimeTypeDetector(), this.getClassLoaderFactory()); 309 } 310 311 /** 312 * Create a new execution context that is the same as this context, but which uses the supplied {@link MimeTypeDetector MIME 313 * type detector}. 314 * 315 * @param mimeTypeDetector the new MIME type detector implementation, or null if the default implementation should be used 316 * @return the execution context that is identical with this execution context, but which uses the supplied detector 317 * implementation; never null 318 */ 319 public ExecutionContext with( MimeTypeDetector mimeTypeDetector ) { 320 // Don't supply the value factories or property factories, since they'll have to be recreated 321 // to reference the supplied namespace registry ... 322 return new ExecutionContext(getLoginContext(), getAccessControlContext(), getNamespaceRegistry(), getValueFactories(), 323 getPropertyFactory(), mimeTypeDetector, getClassLoaderFactory()); 324 } 325 326 /** 327 * Create a new execution context that mirrors this context but that uses the supplied {@link ClassLoaderFactory class loader 328 * factory}. 329 * 330 * @param classLoaderFactory the new class loader factory implementation, or null if the default implementation should be used 331 * @return the execution context that is identical with this execution context, but which uses the supplied class loader 332 * factory implementation; never null 333 */ 334 public ExecutionContext with( ClassLoaderFactory classLoaderFactory ) { 335 // Don't supply the value factories or property factories, since they'll have to be recreated 336 // to reference the supplied namespace registry ... 337 return new ExecutionContext(getLoginContext(), getAccessControlContext(), getNamespaceRegistry(), getValueFactories(), 338 getPropertyFactory(), getMimeTypeDetector(), classLoaderFactory); 339 } 340 341 /** 342 * Creates an {@link ExecutionContext} that is the same as this context, but which uses the supplied 343 * {@link AccessControlContext access control context}. 344 * 345 * @param accessControlContext the JAAS access control context that should be used 346 * @return the execution context that is identical with this execution context, but with a different security context; never 347 * null 348 * @throws IllegalArgumentException if <code>accessControlContext</code> is <code>null</code>. 349 */ 350 public ExecutionContext create( AccessControlContext accessControlContext ) { 351 return new ExecutionContext(this, accessControlContext); 352 } 353 354 /** 355 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied {@link LoginContext}. A 356 * LoginContext has a variety of constructors, including contructors that take combinations of 357 * {@link Configuration#getAppConfigurationEntry(String) application configuration name}, {@link Subject subject}, 358 * {@link CallbackHandler callback handlers}, and a {@link Configuration JAAS configuration}. 359 * 360 * @param loginContext the JAAS login context 361 * @return the execution context that is identical with this execution context, but with a different security context; never 362 * null 363 * @throws IllegalArgumentException if the <code>loginContext</code> is null 364 */ 365 public ExecutionContext create( LoginContext loginContext ) { 366 return new ExecutionContext(this, loginContext); 367 } 368 369 /** 370 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied 371 * {@link Configuration#getAppConfigurationEntry(String) application configuration name}. 372 * 373 * @param name the name of the {@link Configuration#getAppConfigurationEntry(String) JAAS application configuration name} 374 * @return the execution context that is identical with this execution context, but with a different security context; never 375 * null 376 * @throws IllegalArgumentException if the <code>name</code> is null 377 * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), or if the 378 * default callback handler JAAS property was not set or could not be loaded 379 */ 380 public ExecutionContext with( String name ) throws LoginException { 381 return new ExecutionContext(this, new LoginContext(name)); 382 } 383 384 /** 385 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied 386 * {@link Configuration#getAppConfigurationEntry(String) application configuration name} and a {@link Subject JAAS subject}. 387 * 388 * @param name the name of the {@link Configuration#getAppConfigurationEntry(String) JAAS application configuration name} 389 * @param subject the subject to authenticate 390 * @return the execution context that is identical with this execution context, but with a different security context; never 391 * null 392 * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), if the default 393 * callback handler JAAS property was not set or could not be loaded, or if the <code>subject</code> is null or 394 * unknown 395 */ 396 public ExecutionContext with( String name, 397 Subject subject ) throws LoginException { 398 return new ExecutionContext(this, new LoginContext(name, subject)); 399 } 400 401 /** 402 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied 403 * {@link Configuration#getAppConfigurationEntry(String) application configuration name} and a {@link CallbackHandler JAAS 404 * callback handler} (used to handle authentication callbacks). 405 * 406 * @param name the name of the {@link Configuration#getAppConfigurationEntry(String) JAAS application configuration name} 407 * @param callbackHandler the callback handler that will be used by {@link LoginModule}s to communicate with the user to 408 * authenticate 409 * @return the execution context that is identical with this execution context, but with a different security context; never 410 * null 411 * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), or if the 412 * <code>callbackHandler</code> is null 413 */ 414 public ExecutionContext with( String name, 415 CallbackHandler callbackHandler ) throws LoginException { 416 return new ExecutionContext(this, new LoginContext(name, callbackHandler)); 417 } 418 419 /** 420 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied 421 * {@link Configuration#getAppConfigurationEntry(String) application configuration name}, a {@link Subject JAAS subject}, and 422 * a {@link CallbackHandler JAAS callback handler} (used to handle authentication callbacks). 423 * 424 * @param name the name of the {@link Configuration#getAppConfigurationEntry(String) JAAS application configuration name} 425 * @param subject the subject to authenticate 426 * @param callbackHandler the callback handler that will be used by {@link LoginModule}s to communicate with the user to 427 * authenticate 428 * @return the execution context that is identical with this execution context, but with a different security context; never 429 * null 430 * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), if the default 431 * callback handler JAAS property was not set or could not be loaded, if the <code>subject</code> is null or unknown, 432 * or if the <code>callbackHandler</code> is null 433 */ 434 public ExecutionContext with( String name, 435 Subject subject, 436 CallbackHandler callbackHandler ) throws LoginException { 437 return new ExecutionContext(this, new LoginContext(name, subject, callbackHandler)); 438 } 439 440 /** 441 * {@inheritDoc} 442 * 443 * @see java.lang.Object#clone() 444 */ 445 @Override 446 public ExecutionContext clone() { 447 return new ExecutionContext(this); 448 } 449 450 /** 451 * {@inheritDoc} 452 * 453 * @see java.lang.Object#toString() 454 */ 455 @Override 456 public String toString() { 457 return "Execution context for " + getSubject(); 458 } 459 460 /** 461 * Method that initializes the default namespaces for namespace registries. 462 * 463 * @param namespaceRegistry the namespace registry 464 */ 465 protected void initializeDefaultNamespaces( NamespaceRegistry namespaceRegistry ) { 466 if (namespaceRegistry == null) return; 467 namespaceRegistry.register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI); 468 namespaceRegistry.register(JcrMixLexicon.Namespace.PREFIX, JcrMixLexicon.Namespace.URI); 469 namespaceRegistry.register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI); 470 namespaceRegistry.register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI); 471 // namespaceRegistry.register("dnadtd", "http://www.jboss.org/dna/dtd/1.0"); 472 // namespaceRegistry.register("dnaxml", "http://www.jboss.org/dna/xml/1.0"); 473 } 474 }