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.login.LoginException;
029    import net.jcip.annotations.Immutable;
030    import org.jboss.dna.common.component.ClassLoaderFactory;
031    import org.jboss.dna.common.component.StandardClassLoaderFactory;
032    import org.jboss.dna.common.util.CheckArg;
033    import org.jboss.dna.common.util.Logger;
034    import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
035    import org.jboss.dna.graph.mimetype.MimeTypeDetector;
036    import org.jboss.dna.graph.property.NamespaceRegistry;
037    import org.jboss.dna.graph.property.Property;
038    import org.jboss.dna.graph.property.PropertyFactory;
039    import org.jboss.dna.graph.property.ValueFactories;
040    import org.jboss.dna.graph.property.basic.BasicPropertyFactory;
041    import org.jboss.dna.graph.property.basic.SimpleNamespaceRegistry;
042    import org.jboss.dna.graph.property.basic.StandardValueFactories;
043    import org.jboss.dna.graph.property.basic.ThreadSafeNamespaceRegistry;
044    
045    /**
046     * An ExecutionContext is a representation of the environment or context in which a component or operation is operating. Some
047     * components require this context to be passed into individual methods, allowing the context to vary with each method invocation.
048     * Other components require the context to be provided before it's used, and will use that context for all its operations (until
049     * it is given a different one).
050     * <p>
051     * ExecutionContext instances are {@link Immutable immutable}, so components may hold onto references to them without concern of
052     * those contexts changing. Contexts may be used to create other contexts that vary the environment and/or security context. For
053     * example, an ExecutionContext could be used to create another context that references the same {@link #getNamespaceRegistry()
054     * namespace registry} but which has a different {@link #getSecurityContext() security context}.
055     * </p>
056     * 
057     * @author Randall Hauch
058     * @author John Verhaeg
059     */
060    @Immutable
061    public class ExecutionContext implements ClassLoaderFactory, Cloneable {
062    
063        private final ClassLoaderFactory classLoaderFactory;
064        private final PropertyFactory propertyFactory;
065        private final ValueFactories valueFactories;
066        private final NamespaceRegistry namespaceRegistry;
067        private final MimeTypeDetector mimeTypeDetector;
068        private final SecurityContext securityContext;
069    
070        /**
071         * Create an instance of an execution context that uses the {@link AccessController#getContext() current JAAS calling context}
072         * , with default implementations for all other components (including default namespaces in the
073         * {@link #getNamespaceRegistry() namespace registry}.
074         */
075        @SuppressWarnings( "synthetic-access" )
076        public ExecutionContext() {
077            this(new NullSecurityContext(), null, null, null, null, null);
078            initializeDefaultNamespaces(this.getNamespaceRegistry());
079            assert securityContext != null;
080    
081        }
082    
083        /**
084         * Create a copy of the supplied execution context.
085         * 
086         * @param original the original
087         * @throws IllegalArgumentException if the original is null
088         */
089        protected ExecutionContext( ExecutionContext original ) {
090            CheckArg.isNotNull(original, "original");
091            this.securityContext = original.getSecurityContext();
092            this.namespaceRegistry = original.getNamespaceRegistry();
093            this.valueFactories = original.getValueFactories();
094            this.propertyFactory = original.getPropertyFactory();
095            this.classLoaderFactory = original.getClassLoaderFactory();
096            this.mimeTypeDetector = original.getMimeTypeDetector();
097        }
098    
099        /**
100         * Create a copy of the supplied execution context, but use the supplied {@link AccessControlContext} instead.
101         * 
102         * @param original the original
103         * @param securityContext the security context
104         * @throws IllegalArgumentException if the original or access control context are is null
105         */
106        protected ExecutionContext( ExecutionContext original,
107                                    SecurityContext securityContext ) {
108            CheckArg.isNotNull(original, "original");
109            CheckArg.isNotNull(securityContext, "securityContext");
110            this.securityContext = securityContext;
111            this.namespaceRegistry = original.getNamespaceRegistry();
112            this.valueFactories = original.getValueFactories();
113            this.propertyFactory = original.getPropertyFactory();
114            this.classLoaderFactory = original.getClassLoaderFactory();
115            this.mimeTypeDetector = original.getMimeTypeDetector();
116        }
117    
118        /**
119         * Create an instance of the execution context by supplying all parameters.
120         * 
121         * @param securityContext the security context, or null if there is no associated authenticated user
122         * @param namespaceRegistry the namespace registry implementation, or null if a thread-safe version of
123         *        {@link SimpleNamespaceRegistry} instance should be used
124         * @param valueFactories the {@link ValueFactories} implementation, or null if a {@link StandardValueFactories} instance
125         *        should be used
126         * @param propertyFactory the {@link PropertyFactory} implementation, or null if a {@link BasicPropertyFactory} instance
127         *        should be used
128         * @param mimeTypeDetector the {@link MimeTypeDetector} implementation, or null if an {@link ExtensionBasedMimeTypeDetector}
129         *        instance should be used
130         * @param classLoaderFactory the {@link ClassLoaderFactory} implementation, or null if a {@link StandardClassLoaderFactory}
131         *        instance should be used
132         */
133        protected ExecutionContext( SecurityContext securityContext,
134                                    NamespaceRegistry namespaceRegistry,
135                                    ValueFactories valueFactories,
136                                    PropertyFactory propertyFactory,
137                                    MimeTypeDetector mimeTypeDetector,
138                                    ClassLoaderFactory classLoaderFactory ) {
139            assert securityContext != null;
140            this.securityContext = securityContext;
141            this.namespaceRegistry = namespaceRegistry != null ? namespaceRegistry : new ThreadSafeNamespaceRegistry(
142                                                                                                                     new SimpleNamespaceRegistry());
143            this.valueFactories = valueFactories == null ? new StandardValueFactories(this.namespaceRegistry) : valueFactories;
144            this.propertyFactory = propertyFactory == null ? new BasicPropertyFactory(this.valueFactories) : propertyFactory;
145            this.classLoaderFactory = classLoaderFactory == null ? new StandardClassLoaderFactory() : classLoaderFactory;
146            this.mimeTypeDetector = mimeTypeDetector != null ? mimeTypeDetector : new ExtensionBasedMimeTypeDetector();
147        }
148    
149        /**
150         * Get the class loader factory used by this context.
151         * 
152         * @return the class loader factory implementation; never null
153         */
154        protected ClassLoaderFactory getClassLoaderFactory() {
155            return classLoaderFactory;
156        }
157    
158        /**
159         * Return a logger associated with this context. This logger records only those activities within the context and provide a
160         * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log
161         * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}.
162         * 
163         * @param clazz the class that is doing the logging
164         * @return the logger, named after <code>clazz</code>; never null
165         * @see #getLogger(String)
166         */
167        public Logger getLogger( Class<?> clazz ) {
168            return Logger.getLogger(clazz);
169        }
170    
171        /**
172         * Return a logger associated with this context. This logger records only those activities within the context and provide a
173         * way to capture the context-specific activities. All log messages are also sent to the system logger, so classes that log
174         * via this mechanism should <i>not</i> also {@link Logger#getLogger(Class) obtain a system logger}.
175         * 
176         * @param name the name for the logger
177         * @return the logger, named after <code>clazz</code>; never null
178         * @see #getLogger(Class)
179         */
180        public Logger getLogger( String name ) {
181            return Logger.getLogger(name);
182        }
183    
184        /**
185         * Return an object that can be used to determine the MIME type of some content, such as the content of a file.
186         * 
187         * @return the detector; never null
188         */
189        public MimeTypeDetector getMimeTypeDetector() {
190            return this.mimeTypeDetector;
191        }
192    
193        /**
194         * Get the {@link SecurityContext security context} for this context.
195         * 
196         * @return the security context; never <code>null</code>
197         */
198        public SecurityContext getSecurityContext() {
199            return this.securityContext;
200        }
201    
202        /**
203         * Get the (mutable) namespace registry for this context.
204         * 
205         * @return the namespace registry; never <code>null</code>
206         */
207        public NamespaceRegistry getNamespaceRegistry() {
208            return this.namespaceRegistry;
209        }
210    
211        /**
212         * Get the factory for creating {@link Property} objects.
213         * 
214         * @return the property factory; never <code>null</code>
215         */
216        public PropertyFactory getPropertyFactory() {
217            return this.propertyFactory;
218        }
219    
220        /**
221         * Get the factories that should be used to create values for {@link Property properties}.
222         * 
223         * @return the property value factory; never null
224         */
225        public ValueFactories getValueFactories() {
226            return this.valueFactories;
227        }
228    
229        /**
230         * {@inheritDoc}
231         * 
232         * @see org.jboss.dna.common.component.ClassLoaderFactory#getClassLoader(java.lang.String[])
233         */
234        public ClassLoader getClassLoader( String... classpath ) {
235            return this.classLoaderFactory.getClassLoader(classpath);
236        }
237    
238        /**
239         * Create a new execution context that mirrors this context but that uses the supplied namespace registry. The resulting
240         * context's {@link #getValueFactories() value factories} and {@link #getPropertyFactory() property factory} all make use of
241         * the new namespace registry.
242         * 
243         * @param namespaceRegistry the new namespace registry implementation, or null if the default implementation should be used
244         * @return the execution context that is identical with this execution context, but which uses the supplied registry; never
245         *         null
246         */
247        public ExecutionContext with( NamespaceRegistry namespaceRegistry ) {
248            // Don't supply the value factories or property factories, since they'll have to be recreated
249            // to reference the supplied namespace registry ...
250            return new ExecutionContext(this.getSecurityContext(), namespaceRegistry, null, null, this.getMimeTypeDetector(),
251                                        this.getClassLoaderFactory());
252        }
253    
254        /**
255         * Create a new execution context that is the same as this context, but which uses the supplied {@link MimeTypeDetector MIME
256         * type detector}.
257         * 
258         * @param mimeTypeDetector the new MIME type detector implementation, or null if the default implementation should be used
259         * @return the execution context that is identical with this execution context, but which uses the supplied detector
260         *         implementation; never null
261         */
262        public ExecutionContext with( MimeTypeDetector mimeTypeDetector ) {
263            // Don't supply the value factories or property factories, since they'll have to be recreated
264            // to reference the supplied namespace registry ...
265            return new ExecutionContext(this.getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(),
266                                        mimeTypeDetector, getClassLoaderFactory());
267        }
268    
269        /**
270         * Create a new execution context that mirrors this context but that uses the supplied {@link ClassLoaderFactory class loader
271         * factory}.
272         * 
273         * @param classLoaderFactory the new class loader factory implementation, or null if the default implementation should be used
274         * @return the execution context that is identical with this execution context, but which uses the supplied class loader
275         *         factory implementation; never null
276         */
277        public ExecutionContext with( ClassLoaderFactory classLoaderFactory ) {
278            // Don't supply the value factories or property factories, since they'll have to be recreated
279            // to reference the supplied namespace registry ...
280            return new ExecutionContext(this.getSecurityContext(), getNamespaceRegistry(), getValueFactories(), getPropertyFactory(),
281                                        getMimeTypeDetector(), classLoaderFactory);
282        }
283    
284        /**
285         * Create an {@link ExecutionContext} that is the same as this context, but which uses the supplied {@link SecurityContext
286         * security context}.
287         * 
288         * @param securityContext the new security context to use; may be null
289         * @return the execution context that is identical with this execution context, but with a different security context; never
290         *         null
291         * @throws IllegalArgumentException if the <code>name</code> is null
292         * @throws LoginException if there <code>name</code> is invalid (or there is no login context named "other"), or if the
293         *         default callback handler JAAS property was not set or could not be loaded
294         */
295        public ExecutionContext with( SecurityContext securityContext ) throws LoginException {
296            return new ExecutionContext(this, securityContext);
297        }
298    
299        /**
300         * {@inheritDoc}
301         * 
302         * @see java.lang.Object#clone()
303         */
304        @Override
305        public ExecutionContext clone() {
306            return new ExecutionContext(this);
307        }
308    
309        /**
310         * {@inheritDoc}
311         * 
312         * @see java.lang.Object#toString()
313         */
314        @Override
315        public String toString() {
316            return "Execution context for " + getSecurityContext() == null ? "null" : getSecurityContext().getUserName();
317        }
318    
319        /**
320         * Method that initializes the default namespaces for namespace registries.
321         * 
322         * @param namespaceRegistry the namespace registry
323         */
324        protected void initializeDefaultNamespaces( NamespaceRegistry namespaceRegistry ) {
325            if (namespaceRegistry == null) return;
326            namespaceRegistry.register(JcrLexicon.Namespace.PREFIX, JcrLexicon.Namespace.URI);
327            namespaceRegistry.register(JcrMixLexicon.Namespace.PREFIX, JcrMixLexicon.Namespace.URI);
328            namespaceRegistry.register(JcrNtLexicon.Namespace.PREFIX, JcrNtLexicon.Namespace.URI);
329            namespaceRegistry.register(DnaLexicon.Namespace.PREFIX, DnaLexicon.Namespace.URI);
330            // namespaceRegistry.register("dnadtd", "http://www.jboss.org/dna/dtd/1.0");
331            // namespaceRegistry.register("dnaxml", "http://www.jboss.org/dna/xml/1.0");
332        }
333    
334        /**
335         * Default security context that confers no roles.
336         */
337        private static class NullSecurityContext implements SecurityContext {
338    
339            public String getUserName() {
340                return null;
341            }
342    
343            public boolean hasRole( String roleName ) {
344                return false;
345            }
346    
347            public void logout() {
348            }
349    
350        }
351    }