View Javadoc

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 }