1 /* 2 * ModeShape (http://www.modeshape.org) 3 * See the COPYRIGHT.txt file distributed with this work for information 4 * regarding copyright ownership. Some portions may be licensed 5 * to Red Hat, Inc. under one or more contributor license agreements. 6 * See the AUTHORS.txt file in the distribution for a full listing of 7 * individual contributors. 8 * 9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape 10 * is licensed to you under the terms of the GNU Lesser General Public License as 11 * published by the Free Software Foundation; either version 2.1 of 12 * the License, or (at your option) any later version. 13 * 14 * ModeShape is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this software; if not, write to the Free 21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 */ 24 package org.modeshape.graph; 25 26 import java.security.AccessControlContext; 27 import java.security.AccessController; 28 import java.util.Collections; 29 import java.util.HashMap; 30 import java.util.Map; 31 import java.util.UUID; 32 import net.jcip.annotations.Immutable; 33 import org.modeshape.common.component.ClassLoaderFactory; 34 import org.modeshape.common.component.StandardClassLoaderFactory; 35 import org.modeshape.common.util.CheckArg; 36 import org.modeshape.common.util.Logger; 37 import org.modeshape.graph.mimetype.ExtensionBasedMimeTypeDetector; 38 import org.modeshape.graph.mimetype.MimeTypeDetector; 39 import org.modeshape.graph.mimetype.MimeTypeDetectors; 40 import org.modeshape.graph.property.NamespaceRegistry; 41 import org.modeshape.graph.property.Property; 42 import org.modeshape.graph.property.PropertyFactory; 43 import org.modeshape.graph.property.ValueFactories; 44 import org.modeshape.graph.property.basic.BasicPropertyFactory; 45 import org.modeshape.graph.property.basic.SimpleNamespaceRegistry; 46 import org.modeshape.graph.property.basic.StandardValueFactories; 47 import org.modeshape.graph.property.basic.ThreadSafeNamespaceRegistry; 48 49 /** 50 * An ExecutionContext is a representation of the environment or context in which a component or operation is operating. Some 51 * components require this context to be passed into individual methods, allowing the context to vary with each method invocation. 52 * Other components require the context to be provided before it's used, and will use that context for all its operations (until 53 * it is given a different one). 54 * <p> 55 * ExecutionContext instances are {@link Immutable immutable}, so components may hold onto references to them without concern of 56 * those contexts changing. Contexts may be used to create other contexts that vary the environment and/or security context. For 57 * example, an ExecutionContext could be used to create another context that references the same {@link #getNamespaceRegistry() 58 * namespace registry} but which has a different {@link #getSecurityContext() security context}. 59 * </p> 60 */ 61 @Immutable 62 public class ExecutionContext implements ClassLoaderFactory, Cloneable { 63 64 public static final ExecutionContext DEFAULT_CONTEXT = new ExecutionContext(); 65 66 private final ClassLoaderFactory classLoaderFactory; 67 private final PropertyFactory propertyFactory; 68 private final ValueFactories valueFactories; 69 private final NamespaceRegistry namespaceRegistry; 70 private final MimeTypeDetector mimeTypeDetector; 71 private final SecurityContext securityContext; 72 /** The unique ID string, which is always generated so that it can be final and not volatile. */ 73 private final String id = UUID.randomUUID().toString(); 74 private final String processId; 75 private final Map<String, String> data; 76 77 /** 78 * Create an instance of an execution context that uses the {@link AccessController#getContext() current JAAS calling context} 79 * , with default implementations for all other components (including default namespaces in the 80 * {@link #getNamespaceRegistry() namespace registry}. 81 */ 82 @SuppressWarnings( "synthetic-access" ) 83 public ExecutionContext() { 84 this(new NullSecurityContext(), null, null, null, null, null, null, null); 85 initializeDefaultNamespaces(this.getNamespaceRegistry()); 86 assert securityContext != null; 87 88 } 89 90 /** 91 * Create a copy of the supplied execution context. 92 * 93 * @param original the original 94 * @throws IllegalArgumentException if the original is null 95 */ 96 protected ExecutionContext( ExecutionContext original ) { 97 CheckArg.isNotNull(original, "original"); 98 this.securityContext = original.getSecurityContext(); 99 this.namespaceRegistry = original.getNamespaceRegistry(); 100 this.valueFactories = original.getValueFactories(); 101 this.propertyFactory = original.getPropertyFactory(); 102 this.classLoaderFactory = original.getClassLoaderFactory(); 103 this.mimeTypeDetector = original.getMimeTypeDetector(); 104 this.data = original.getData(); 105 this.processId = original.getProcessId(); 106 } 107 108 /** 109 * Create a copy of the supplied execution context, but use the supplied {@link AccessControlContext} instead. 110 * 111 * @param original the original 112 * @param securityContext the security context 113 * @throws IllegalArgumentException if the original or access control context are is null 114 */ 115 protected ExecutionContext( ExecutionContext original, 116 SecurityContext securityContext ) { 117 CheckArg.isNotNull(original, "original"); 118 CheckArg.isNotNull(securityContext, "securityContext"); 119 this.securityContext = securityContext; 120 this.namespaceRegistry = original.getNamespaceRegistry(); 121 this.valueFactories = original.getValueFactories(); 122 this.propertyFactory = original.getPropertyFactory(); 123 this.classLoaderFactory = original.getClassLoaderFactory(); 124 this.mimeTypeDetector = original.getMimeTypeDetector(); 125 this.data = original.getData(); 126 this.processId = original.getProcessId(); 127 } 128 129 /** 130 * Create an instance of the execution context by supplying all parameters. 131 * 132 * @param securityContext the security context, or null if there is no associated authenticated user 133 * @param namespaceRegistry the namespace registry implementation, or null if a thread-safe version of 134 * {@link SimpleNamespaceRegistry} instance should be used 135 * @param valueFactories the {@link ValueFactories} implementation, or null if a {@link StandardValueFactories} instance 136 * should be used 137 * @param propertyFactory the {@link PropertyFactory} implementation, or null if a {@link BasicPropertyFactory} instance 138 * should be used 139 * @param mimeTypeDetector the {@link MimeTypeDetector} implementation, or null if the context should use a 140 * {@link MimeTypeDetectors} instance with an {@link ExtensionBasedMimeTypeDetector} 141 * @param classLoaderFactory the {@link ClassLoaderFactory} implementation, or null if a {@link StandardClassLoaderFactory} 142 * instance should be used 143 * @param data the custom data for this context, or null if there is no such data 144 * @param processId the unique identifier of the process in which this context exists, or null if it should be generated 145 */ 146 protected ExecutionContext( SecurityContext securityContext, 147 NamespaceRegistry namespaceRegistry, 148 ValueFactories valueFactories, 149 PropertyFactory propertyFactory, 150 MimeTypeDetector mimeTypeDetector, 151 ClassLoaderFactory classLoaderFactory, 152 Map<String, String> data, 153 String processId ) { 154 assert securityContext != null; 155 this.securityContext = securityContext; 156 this.namespaceRegistry = namespaceRegistry != null ? namespaceRegistry : new ThreadSafeNamespaceRegistry( 157 new SimpleNamespaceRegistry()); 158 this.valueFactories = valueFactories == null ? new StandardValueFactories(this.namespaceRegistry) : valueFactories; 159 this.propertyFactory = propertyFactory == null ? new BasicPropertyFactory(this.valueFactories) : propertyFactory; 160 this.classLoaderFactory = classLoaderFactory == null ? new StandardClassLoaderFactory() : classLoaderFactory; 161 this.mimeTypeDetector = mimeTypeDetector != null ? mimeTypeDetector : createDefaultMimeTypeDetector(); 162 this.data = data != null ? data : Collections.<String, String>emptyMap(); 163 this.processId = processId != null ? processId : UUID.randomUUID().toString(); 164 } 165 166 private MimeTypeDetector createDefaultMimeTypeDetector() { 167 MimeTypeDetectors detectors = new MimeTypeDetectors(); 168 detectors.addDetector(ExtensionBasedMimeTypeDetector.CONFIGURATION); 169 return detectors; 170 } 171 172 /** 173 * Get the class loader factory used by this context. 174 * 175 * @return the class loader factory implementation; never null 176 */ 177 protected ClassLoaderFactory getClassLoaderFactory() { 178 return classLoaderFactory; 179 } 180 181 /** 182 * Return a logger associated with this context. This logger records only those activities within the context and provide a 183 * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log 184 * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}. 185 * 186 * @param clazz the class that is doing the logging 187 * @return the logger, named after <code>clazz</code>; never null 188 * @see #getLogger(String) 189 */ 190 public Logger getLogger( Class<?> clazz ) { 191 return Logger.getLogger(clazz); 192 } 193 194 /** 195 * Return a logger associated with this context. This logger records only those activities within the context and provide a 196 * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log 197 * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}. 198 * 199 * @param name the name for the logger 200 * @return the logger, named after <code>clazz</code>; never null 201 * @see #getLogger(Class) 202 */ 203 public Logger getLogger( String name ) { 204 return Logger.getLogger(name); 205 } 206 207 /** 208 * Return an object that can be used to determine the MIME type of some content, such as the content of a file. 209 * 210 * @return the detector; never null 211 */ 212 public MimeTypeDetector getMimeTypeDetector() { 213 return this.mimeTypeDetector; 214 } 215 216 /** 217 * Get the {@link SecurityContext security context} for this context. 218 * 219 * @return the security context; never <code>null</code> 220 */ 221 public SecurityContext getSecurityContext() { 222 return this.securityContext; 223 } 224 225 /** 226 * Get the (mutable) namespace registry for this context. 227 * 228 * @return the namespace registry; never <code>null</code> 229 */ 230 public NamespaceRegistry getNamespaceRegistry() { 231 return this.namespaceRegistry; 232 } 233 234 /** 235 * Get the factory for creating {@link Property} objects. 236 * 237 * @return the property factory; never <code>null</code> 238 */ 239 public final PropertyFactory getPropertyFactory() { 240 return this.propertyFactory; 241 } 242 243 /** 244 * Get the factories that should be used to create values for {@link Property properties}. 245 * 246 * @return the property value factory; never null 247 */ 248 public ValueFactories getValueFactories() { 249 return this.valueFactories; 250 } 251 252 /** 253 * {@inheritDoc} 254 * 255 * @see org.modeshape.common.component.ClassLoaderFactory#getClassLoader(java.lang.String[]) 256 */ 257 public ClassLoader getClassLoader( String... classpath ) { 258 return this.classLoaderFactory.getClassLoader(classpath); 259 } 260 261 /** 262 * Get the unique identifier for this context. Each context will always have a unique identifier. 263 * 264 * @return the unique identifier string; never null and never empty 265 */ 266 public String getId() { 267 return id; 268 } 269 270 /** 271 * Get the identifier for the process in which this context exists. Multiple contexts running in the same "process" will all 272 * have the same identifier. 273 * 274 * @return the identifier for the process; never null and never empty 275 */ 276 public String getProcessId() { 277 return processId; 278 } 279 280 /** 281 * Get the immutable map of custom data that is affiliated with this context. 282 * 283 * @return the custom data; never null but possibly empty 284 */ 285 public Map<String, String> getData() { 286 return data; 287 } 288 289 /** 290 * Create a new execution context that mirrors this context but that uses the supplied namespace registry. The resulting 291 * context's {@link #getValueFactories() value factories} and {@link #getPropertyFactory() property factory} all make use of 292 * the new namespace registry. 293 * 294 * @param namespaceRegistry the new namespace registry implementation, or null if the default implementation should be used 295 * @return the execution context that is identical with this execution context, but which uses the supplied registry; never 296 * null 297 */ 298 public ExecutionContext with( NamespaceRegistry namespaceRegistry ) { 299 // Don't supply the value factories or property factories, since they'll have to be recreated 300 // to reference the supplied namespace registry ... 301 return new ExecutionContext(this.getSecurityContext(), namespaceRegistry, null, null, this.getMimeTypeDetector(), 302 this.getClassLoaderFactory(), this.getData(), getProcessId()); 303 } 304 305 /** 306 * Create a new execution context that is the same as this context, but which uses the supplied {@link MimeTypeDetector MIME 307 * type detector}. 308 * 309 * @param mimeTypeDetector the new MIME type detector implementation, or null if the context should use a 310 * {@link MimeTypeDetectors} instance with an {@link ExtensionBasedMimeTypeDetector} 311 * @return the execution context that is identical with this execution context, but which uses the supplied detector 312 * implementation; never null 313 */ 314 public ExecutionContext with( MimeTypeDetector mimeTypeDetector ) { 315 // Don't supply the value factories or property factories, since they'll have to be recreated 316 // to reference the supplied namespace registry ... 317 return new ExecutionContext(this.getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(), 318 mimeTypeDetector, getClassLoaderFactory(), this.getData(), getProcessId()); 319 } 320 321 /** 322 * Create a new execution context that mirrors this context but that uses the supplied {@link ClassLoaderFactory class loader 323 * factory}. 324 * 325 * @param classLoaderFactory the new class loader factory implementation, or null if the default implementation should be used 326 * @return the execution context that is identical with this execution context, but which uses the supplied class loader 327 * factory implementation; never null 328 */ 329 public ExecutionContext with( ClassLoaderFactory classLoaderFactory ) { 330 // Don't supply the value factories or property factories, since they'll have to be recreated 331 // to reference the supplied namespace registry ... 332 return new ExecutionContext(this.getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(), 333 getMimeTypeDetector(), classLoaderFactory, this.getData(), getProcessId()); 334 } 335 336 /** 337 * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied {@link SecurityContext 338 * security context}. 339 * 340 * @param securityContext the new security context to use; may be null 341 * @return the execution context that is identical with this execution context, but with a different security context; never 342 * null 343 * @throws IllegalArgumentException if the <code>name</code> is null 344 */ 345 public ExecutionContext with( SecurityContext securityContext ) { 346 return new ExecutionContext(this, securityContext); 347 } 348 349 /** 350 * Create a new execution context that mirrors this context but that contains the supplied data. Note that the supplied map is 351 * always copied to ensure that it is immutable. 352 * 353 * @param data the data that is to be affiliated with the resulting context or null if the resulting context should have no 354 * data 355 * @return the execution context that is identical with this execution context, but which uses the supplied data; never null 356 * @since 2.0 357 */ 358 public ExecutionContext with( Map<String, String> data ) { 359 Map<String, String> newData = data; 360 if (data == null) { 361 if (this.data.isEmpty()) return this; 362 } else { 363 // Copy the data in the map ... 364 newData = Collections.unmodifiableMap(new HashMap<String, String>(data)); 365 } 366 return new ExecutionContext(this.getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(), 367 getMimeTypeDetector(), getClassLoaderFactory(), newData, getProcessId()); 368 } 369 370 /** 371 * Create a new execution context that mirrors this context but that contains the supplied key-value pair in the new context's 372 * data. 373 * 374 * @param key the key for the new data that is to be affiliated with the resulting context 375 * @param value the data value to be affiliated with the supplied key in the resulting context, or null if an existing data 376 * affiliated with the key should be removed in the resulting context 377 * @return the execution context that is identical with this execution context, but which uses the supplied data; never null 378 * @since 2.0 379 */ 380 public ExecutionContext with( String key, 381 String value ) { 382 Map<String, String> newData = data; 383 if (value == null) { 384 // Remove the value with the key ... 385 if (this.data.isEmpty() || !this.data.containsKey(key)) { 386 // nothing to remove 387 return this; 388 } 389 newData = new HashMap<String, String>(data); 390 newData.remove(key); 391 newData = Collections.unmodifiableMap(newData); 392 } else { 393 // We are to add the value ... 394 newData = new HashMap<String, String>(data); 395 newData.put(key, value); 396 newData = Collections.unmodifiableMap(newData); 397 } 398 return new ExecutionContext(getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(), 399 getMimeTypeDetector(), getClassLoaderFactory(), newData, getProcessId()); 400 } 401 402 /** 403 * Create a new execution context that mirrors this context but that contains the supplied process identifier. 404 * 405 * @param processId the identifier of the process 406 * @return the execution context that is identical with this execution context, but which uses the supplied process 407 * identifier; never null 408 * @since 2.1 409 */ 410 public ExecutionContext with( String processId ) { 411 return new ExecutionContext(getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(), 412 getMimeTypeDetector(), getClassLoaderFactory(), getData(), processId); 413 } 414 415 /** 416 * {@inheritDoc} 417 * 418 * @see java.lang.Object#clone() 419 */ 420 @Override 421 public ExecutionContext clone() { 422 return new ExecutionContext(this); 423 } 424 425 /** 426 * {@inheritDoc} 427 * 428 * @see java.lang.Object#toString() 429 */ 430 @Override 431 public String toString() { 432 StringBuilder sb = new StringBuilder("Execution context for "); 433 if (getSecurityContext() == null) sb.append("null"); 434 else sb.append(getSecurityContext().getUserName()); 435 sb.append(" (").append(id).append(')'); 436 return sb.toString(); 437 } 438 439 /** 440 * Method that initializes the default namespaces for namespace registries. 441 * 442 * @param namespaceRegistry the namespace registry 443 */ 444 protected void initializeDefaultNamespaces( NamespaceRegistry namespaceRegistry ) { 445 if (namespaceRegistry == null) return; 446 namespaceRegistry.register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI); 447 namespaceRegistry.register(JcrMixLexicon.Namespace.PREFIX, JcrMixLexicon.Namespace.URI); 448 namespaceRegistry.register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI); 449 namespaceRegistry.register(ModeShapeLexicon.Namespace.PREFIX, ModeShapeLexicon.Namespace.URI); 450 namespaceRegistry.register(ModeShapeIntLexicon.Namespace.PREFIX, ModeShapeIntLexicon.Namespace.URI); 451 // namespaceRegistry.register("dnadtd", "http://www.modeshape.org/dtd/1.0"); 452 // namespaceRegistry.register("dnaxml", "http://www.modeshape.org/xml/1.0"); 453 } 454 455 /** 456 * Default security context that confers no roles. 457 */ 458 private static class NullSecurityContext implements SecurityContext { 459 460 public String getUserName() { 461 return null; 462 } 463 464 public boolean hasRole( String roleName ) { 465 return false; 466 } 467 468 public void logout() { 469 } 470 471 } 472 }