View Javadoc

1   /*
2    *   Copyright (c) 2009 The JOMC Project
3    *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
4    *   All rights reserved.
5    *
6    *   Redistribution and use in source and binary forms, with or without
7    *   modification, are permitted provided that the following conditions
8    *   are met:
9    *
10   *     o Redistributions of source code must retain the above copyright
11   *       notice, this list of conditions and the following disclaimer.
12   *
13   *     o Redistributions in binary form must reproduce the above copyright
14   *       notice, this list of conditions and the following disclaimer in
15   *       the documentation and/or other materials provided with the
16   *       distribution.
17   *
18   *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
19   *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20   *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21   *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
22   *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23   *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24   *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25   *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26   *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
27   *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28   *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29   *
30   *   $Id: BootstrapContext.java 1393 2010-01-27 18:34:30Z schulte2005 $
31   *
32   */
33  package org.jomc.model.bootstrap;
34  
35  import java.io.BufferedReader;
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.io.InputStreamReader;
41  import java.lang.reflect.Constructor;
42  import java.lang.reflect.InvocationTargetException;
43  import java.net.URL;
44  import java.util.Collection;
45  import java.util.Comparator;
46  import java.util.Enumeration;
47  import java.util.Map;
48  import java.util.TreeMap;
49  import javax.xml.bind.JAXBContext;
50  import javax.xml.bind.JAXBException;
51  import javax.xml.bind.Marshaller;
52  import javax.xml.bind.Unmarshaller;
53  import javax.xml.bind.util.JAXBSource;
54  import javax.xml.validation.Validator;
55  import org.xml.sax.SAXException;
56  
57  /**
58   * Object management and configuration model bootstrap context interface.
59   *
60   * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
61   * @version $Id: BootstrapContext.java 1393 2010-01-27 18:34:30Z schulte2005 $
62   * @see #createBootstrapContext(java.lang.ClassLoader)
63   */
64  public abstract class BootstrapContext
65  {
66  
67      /** Class name of the {@code BootstrapContext} implementation. */
68      private static volatile String bootstrapContextClassName;
69  
70      /** The class loader of the context. */
71      private ClassLoader classLoader;
72  
73      /**
74       * Creates a new {@code BootstrapContext} instance taking a class loader.
75       *
76       * @param classLoader The class loader of the context.
77       */
78      public BootstrapContext( final ClassLoader classLoader )
79      {
80          super();
81          this.classLoader = classLoader;
82      }
83  
84      /**
85       * Gets the class loader of the context.
86       *
87       * @return The class loader of the context.
88       */
89      public ClassLoader getClassLoader()
90      {
91          if ( this.classLoader == null )
92          {
93              this.classLoader = new ClassLoader( null )
94              {
95  
96                  @Override
97                  public String toString()
98                  {
99                      return BootstrapContext.class.getName() + ".BootstrapClassLoader@" +
100                            System.identityHashCode( this );
101 
102                 }
103 
104             };
105 
106         }
107 
108         return this.classLoader;
109     }
110 
111     /**
112      * Gets the name of the class providing the {@code BootstrapContext} implementation.
113      * <p>The name of the class providing the {@code BootstrapContext} implementation returned by method
114      * {@link #createBootstrapContext(java.lang.ClassLoader)} is controlled by system property
115      * {@code org.jomc.model.bootstrap.BootstrapContext.className}. If that property is not set, the name of the
116      * {@link DefaultBootstrapContext} class is returned.</p>
117      *
118      * @return The name of the class providing the {@code BootstrapContext} implementation.
119      *
120      * @see #setBootstrapContextClassName(java.lang.String)
121      */
122     public static String getBootstrapContextClassName()
123     {
124         if ( bootstrapContextClassName == null )
125         {
126             bootstrapContextClassName = System.getProperty( "org.jomc.model.bootstrap.BootstrapContext.className",
127                                                             DefaultBootstrapContext.class.getName() );
128 
129         }
130 
131         return bootstrapContextClassName;
132     }
133 
134     /**
135      * Sets the name of the class providing the {@code BootstrapContext} implementation.
136      *
137      * @param value The new name of the class providing the {@code BootstrapContext} implementation or {@code null}.
138      *
139      * @see #getBootstrapContextClassName()
140      */
141     public static void setBootstrapContextClassName( final String value )
142     {
143         bootstrapContextClassName = value;
144     }
145 
146     /**
147      * Searches the context for a class with a given name.
148      *
149      * @param name The name of the class to return.
150      *
151      * @return A Class object of the class with name {@code name} or {@code null} if no such class is found.
152      *
153      * @throws NullPointerException if {@code name} is {@code null}.
154      * @throws BootstrapException if searching fails.
155      */
156     public Class findClass( final String name ) throws BootstrapException
157     {
158         if ( name == null )
159         {
160             throw new NullPointerException( "name" );
161         }
162 
163         try
164         {
165             return Class.forName( name, true, this.getClassLoader() );
166         }
167         catch ( final ClassNotFoundException e )
168         {
169             return null;
170         }
171     }
172 
173     /**
174      * Searches the context for a resource with a given name.
175      *
176      * @param name The name of the resource to return.
177      *
178      * @return An URL object for reading the resource or {@code null} if no such resource is found.
179      *
180      * @throws NullPointerException if {@code name} is {@code null}.
181      * @throws BootstrapException if searching fails.
182      */
183     public URL findResource( final String name ) throws BootstrapException
184     {
185         if ( name == null )
186         {
187             throw new NullPointerException( "name" );
188         }
189 
190         return this.getClassLoader().getResource( name );
191     }
192 
193     /**
194      * Searches the context for resources with a given name.
195      *
196      * @param name The name of the resources to return.
197      *
198      * @return An enumeration of URL objects for reading the resources. If no resources are found, the enumeration will
199      * be empty.
200      *
201      * @throws NullPointerException if {@code name} is {@code null}.
202      * @throws BootstrapException if searching fails.
203      */
204     public Enumeration<URL> findResources( final String name ) throws BootstrapException
205     {
206         if ( name == null )
207         {
208             throw new NullPointerException( "name" );
209         }
210 
211         try
212         {
213             return this.getClassLoader().getResources( name );
214         }
215         catch ( final IOException e )
216         {
217             throw new BootstrapException( e );
218         }
219     }
220 
221     /**
222      * Searches the context for schemas.
223      * <p>This method loads {@code SchemaProvider} classes setup via
224      * {@code META-INF/services/org.jomc.model.bootstrap.SchemaProvider} resources and returns a list of provided
225      * schemas.</p>
226      *
227      * @return The schemas found in the context.
228      *
229      * @throws BootstrapException if searching schemas fails.
230      *
231      * @see SchemaProvider#findSchemas(org.jomc.model.bootstrap.BootstrapContext)
232      */
233     public Schemas findSchemas() throws BootstrapException
234     {
235         try
236         {
237             final Schemas schemas = new Schemas();
238 
239             final Collection<Class<SchemaProvider>> providers = this.loadProviders( SchemaProvider.class );
240             for ( Class<SchemaProvider> provider : providers )
241             {
242                 final SchemaProvider schemaProvider = provider.newInstance();
243                 final Schemas provided = schemaProvider.findSchemas( this );
244                 if ( provided != null )
245                 {
246                     schemas.getSchema().addAll( provided.getSchema() );
247                 }
248             }
249 
250             final javax.xml.validation.Schema bootstrapSchema = this.createSchema();
251             final Validator validator = bootstrapSchema.newValidator();
252             validator.validate( new JAXBSource( this.createContext(), new ObjectFactory().createSchemas( schemas ) ) );
253 
254             return schemas;
255         }
256         catch ( final InstantiationException e )
257         {
258             throw new BootstrapException( e );
259         }
260         catch ( final IllegalAccessException e )
261         {
262             throw new BootstrapException( e );
263         }
264         catch ( final JAXBException e )
265         {
266             throw new BootstrapException( e );
267         }
268         catch ( final SAXException e )
269         {
270             throw new BootstrapException( e );
271         }
272         catch ( final IOException e )
273         {
274             throw new BootstrapException( e );
275         }
276     }
277 
278     /**
279      * Searches the context for services.
280      * <p>This method loads {@code ServiceProvider} classes setup via
281      * {@code META-INF/services/org.jomc.model.bootstrap.ServiceProvider} resources and returns a list of provided
282      * services.</p>
283      *
284      * @return The services found in the context.
285      *
286      * @throws BootstrapException if searching services fails.
287      *
288      * @see ServiceProvider#findServices(org.jomc.model.bootstrap.BootstrapContext)
289      */
290     public Services findServices() throws BootstrapException
291     {
292         try
293         {
294             final Services services = new Services();
295 
296             final Collection<Class<ServiceProvider>> providers = this.loadProviders( ServiceProvider.class );
297             for ( Class<ServiceProvider> provider : providers )
298             {
299                 final ServiceProvider serviceProvider = provider.newInstance();
300                 final Services provided = serviceProvider.findServices( this );
301                 if ( provided != null )
302                 {
303                     services.getService().addAll( provided.getService() );
304                 }
305             }
306 
307             final javax.xml.validation.Schema bootstrapSchema = this.createSchema();
308             final Validator validator = bootstrapSchema.newValidator();
309             validator.validate( new JAXBSource( this.createContext(), new ObjectFactory().createServices( services ) ) );
310 
311             return services;
312         }
313         catch ( final InstantiationException e )
314         {
315             throw new BootstrapException( e );
316         }
317         catch ( final IllegalAccessException e )
318         {
319             throw new BootstrapException( e );
320         }
321         catch ( final JAXBException e )
322         {
323             throw new BootstrapException( e );
324         }
325         catch ( final SAXException e )
326         {
327             throw new BootstrapException( e );
328         }
329         catch ( final IOException e )
330         {
331             throw new BootstrapException( e );
332         }
333     }
334 
335     /**
336      * Creates a new object management and configuration model {@code BootstrapContext} instance.
337      *
338      * @param classLoader The class loader to create a new object management and configuration model bootstrap context
339      * instance with or {@code null} to create a new bootstrap context using the platform's bootstrap class loader.
340      *
341      * @return A new {@code BootstrapContext} instance.
342      *
343      * @throws BootstrapException if creating a new object management and configuration model bootstrap context instance
344      * fails.
345      *
346      * @see #getBootstrapContextClassName()
347      */
348     public static BootstrapContext createBootstrapContext( final ClassLoader classLoader ) throws BootstrapException
349     {
350         if ( DefaultBootstrapContext.class.getName().equals( getBootstrapContextClassName() ) )
351         {
352             return new DefaultBootstrapContext( classLoader );
353         }
354 
355         try
356         {
357             final Class clazz = Class.forName( getBootstrapContextClassName(), true, classLoader );
358             final Constructor ctor = clazz.getConstructor( ClassLoader.class );
359             return (BootstrapContext) ctor.newInstance( classLoader );
360         }
361         catch ( final ClassNotFoundException e )
362         {
363             throw new BootstrapException( e );
364         }
365         catch ( final NoSuchMethodException e )
366         {
367             throw new BootstrapException( e );
368         }
369         catch ( final InstantiationException e )
370         {
371             throw new BootstrapException( e );
372         }
373         catch ( final IllegalAccessException e )
374         {
375             throw new BootstrapException( e );
376         }
377         catch ( final InvocationTargetException e )
378         {
379             throw new BootstrapException( e );
380         }
381         catch ( final ClassCastException e )
382         {
383             throw new BootstrapException( e );
384         }
385     }
386 
387     /**
388      * Creates a new object management and configuration model bootstrap JAXP schema instance.
389      *
390      * @return A new object management and configuration model bootstrap JAXP schema instance.
391      *
392      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXP schema
393      * instance fails.
394      */
395     public abstract javax.xml.validation.Schema createSchema() throws BootstrapException;
396 
397     /**
398      * Creates a new object management and configuration model bootstrap JAXB context instance.
399      *
400      * @return A new object management and configuration model bootstrap JAXB context instance.
401      *
402      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB context
403      * instance fails.
404      */
405     public abstract JAXBContext createContext() throws BootstrapException;
406 
407     /**
408      * Creates a new object management and configuration model bootstrap JAXB marshaller instance.
409      *
410      * @return A new object management and configuration model bootstrap JAXB marshaller instance.
411      *
412      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB marshaller
413      * instance fails.
414      */
415     public abstract Marshaller createMarshaller() throws BootstrapException;
416 
417     /**
418      * Creates a new object management and configuration model bootstrap JAXB unmarshaller instance.
419      *
420      * @return A new object management and configuration model bootstrap JAXB unmarshaller instance.
421      *
422      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB
423      * unmarshaller instance fails.
424      */
425     public abstract Unmarshaller createUnmarshaller() throws BootstrapException;
426 
427     private <T> Collection<Class<T>> loadProviders( final Class<T> providerClass ) throws BootstrapException
428     {
429         try
430         {
431             final String providerNamePrefix = providerClass.getName() + ".";
432             final Map<String, Class<T>> providers = new TreeMap<String, Class<T>>( new Comparator<String>()
433             {
434 
435                 public int compare( final String key1, final String key2 )
436                 {
437                     return key1.compareTo( key2 );
438                 }
439 
440             } );
441 
442             final File platformProviders = new File( new StringBuilder().append( System.getProperty( "java.home" ) ).
443                 append( File.separator ).append( "jre" ).append( File.separator ).append( "lib" ).
444                 append( File.separator ).append( "jomc.properties" ).toString() );
445 
446             if ( platformProviders.exists() )
447             {
448                 InputStream in = null;
449                 final java.util.Properties p = new java.util.Properties();
450 
451                 try
452                 {
453                     in = new FileInputStream( platformProviders );
454                     p.load( in );
455                 }
456                 finally
457                 {
458                     if ( in != null )
459                     {
460                         in.close();
461                     }
462                 }
463 
464                 for ( Map.Entry e : p.entrySet() )
465                 {
466                     if ( e.getKey().toString().startsWith( providerNamePrefix ) )
467                     {
468                         final Class<T> provider = this.findClass( e.getValue().toString() );
469                         if ( provider != null )
470                         {
471                             providers.put( e.getKey().toString(), provider );
472                         }
473                     }
474                 }
475             }
476 
477             final Enumeration<URL> serviceProviders =
478                 this.findResources( "META-INF/services/" + providerClass.getName() );
479 
480             while ( serviceProviders.hasMoreElements() )
481             {
482                 final URL url = serviceProviders.nextElement();
483                 final BufferedReader reader = new BufferedReader( new InputStreamReader( url.openStream(), "UTF-8" ) );
484 
485                 String line = null;
486                 while ( ( line = reader.readLine() ) != null )
487                 {
488                     if ( line.contains( "#" ) )
489                     {
490                         continue;
491                     }
492 
493                     final Class<T> provider = this.findClass( line );
494                     if ( provider != null )
495                     {
496                         providers.put( providerNamePrefix + providers.size(), provider );
497                     }
498                 }
499 
500                 reader.close();
501             }
502 
503             return providers.values();
504         }
505         catch ( final IOException e )
506         {
507             throw new BootstrapException( e );
508         }
509     }
510 
511 }