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    }