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 }