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 1276 2010-01-10 23:22:52Z 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.Marshaller;
51  import javax.xml.bind.Unmarshaller;
52  
53  /**
54   * Object management and configuration model bootstrap context interface.
55   *
56   * @author <a href="mailto:cs@jomc.org">Christian Schulte</a>
57   * @version $Id: BootstrapContext.java 1276 2010-01-10 23:22:52Z schulte2005 $
58   * @see #createBootstrapContext(java.lang.ClassLoader)
59   */
60  public abstract class BootstrapContext
61  {
62  
63      /** Class name of the {@code BootstrapContext} implementation. */
64      private static volatile String bootstrapContextClassName;
65  
66      /** The class loader of the context. */
67      private ClassLoader classLoader;
68  
69      /**
70       * Creates a new {@code BootstrapContext} instance taking a class loader.
71       *
72       * @param classLoader The class loader of the context.
73       */
74      protected BootstrapContext( final ClassLoader classLoader )
75      {
76          super();
77          this.classLoader = classLoader;
78      }
79  
80      /**
81       * Gets the class loader of the context.
82       *
83       * @return The class loader of the context.
84       */
85      protected ClassLoader getClassLoader()
86      {
87          if ( this.classLoader == null )
88          {
89              this.classLoader = new ClassLoader( null )
90              {
91  
92                  @Override
93                  public String toString()
94                  {
95                      return BootstrapContext.class.getName() + ".BootstrapClassLoader@" +
96                             System.identityHashCode( this );
97  
98                  }
99  
100             };
101 
102         }
103 
104         return this.classLoader;
105     }
106 
107     /**
108      * Gets the name of the class providing the {@code BootstrapContext} implementation.
109      * <p>The name of the class providing the {@code BootstrapContext} implementation returned by method
110      * {@link #createBootstrapContext(java.lang.ClassLoader)} is controlled by system property
111      * {@code org.jomc.model.bootstrap.BootstrapContext.className}. If that property is not set, the name of the
112      * {@link DefaultBootstrapContext} class is returned.</p>
113      *
114      * @return The name of the class providing the {@code BootstrapContext} implementation.
115      *
116      * @see #setBootstrapContextClassName(java.lang.String)
117      */
118     public static String getBootstrapContextClassName()
119     {
120         if ( bootstrapContextClassName == null )
121         {
122             bootstrapContextClassName = System.getProperty( "org.jomc.model.bootstrap.BootstrapContext.className",
123                                                             DefaultBootstrapContext.class.getName() );
124 
125         }
126 
127         return bootstrapContextClassName;
128     }
129 
130     /**
131      * Sets the name of the class providing the {@code BootstrapContext} implementation.
132      *
133      * @param value The new name of the class providing the {@code BootstrapContext} implementation or {@code null}.
134      *
135      * @see #getBootstrapContextClassName()
136      */
137     public static void setBootstrapContextClassName( final String value )
138     {
139         bootstrapContextClassName = value;
140     }
141 
142     /**
143      * Searches the context for a class with a given name.
144      *
145      * @param name The name of the class to return.
146      *
147      * @return A Class object of the class with name {@code name} or {@code null} if no such class is found.
148      *
149      * @throws NullPointerException if {@code name} is {@code null}.
150      * @throws BootstrapException if searching fails.
151      */
152     public Class findClass( final String name ) throws BootstrapException
153     {
154         if ( name == null )
155         {
156             throw new NullPointerException( "name" );
157         }
158 
159         try
160         {
161             return Class.forName( name, true, this.getClassLoader() );
162         }
163         catch ( final ClassNotFoundException e )
164         {
165             return null;
166         }
167     }
168 
169     /**
170      * Searches the context for a resource with a given name.
171      *
172      * @param name The name of the resource to return.
173      *
174      * @return An URL object for reading the resource or {@code null} if no such resource is found.
175      *
176      * @throws NullPointerException if {@code name} is {@code null}.
177      * @throws BootstrapException if searching fails.
178      */
179     public URL findResource( final String name ) throws BootstrapException
180     {
181         if ( name == null )
182         {
183             throw new NullPointerException( "name" );
184         }
185 
186         return this.getClassLoader().getResource( name );
187     }
188 
189     /**
190      * Searches the context for resources with a given name.
191      *
192      * @param name The name of the resources to return.
193      *
194      * @return An enumeration of URL objects for reading the resources. If no resources are found, the enumeration will
195      * be empty.
196      *
197      * @throws NullPointerException if {@code name} is {@code null}.
198      * @throws BootstrapException if searching fails.
199      */
200     public Enumeration<URL> findResources( final String name ) throws BootstrapException
201     {
202         if ( name == null )
203         {
204             throw new NullPointerException( "name" );
205         }
206 
207         try
208         {
209             return this.getClassLoader().getResources( name );
210         }
211         catch ( final IOException e )
212         {
213             throw new BootstrapException( e );
214         }
215     }
216 
217     /**
218      * Searches the context for schemas.
219      * <p>This method loads {@code SchemaProvider} classes setup via
220      * {@code META-INF/services/org.jomc.model.bootstrap.SchemaProvider} resources and returns a list of provided
221      * schemas.</p>
222      *
223      * @return The schemas found in the context.
224      *
225      * @throws BootstrapException if searching schemas fails.
226      *
227      * @see SchemaProvider#findSchemas(org.jomc.model.bootstrap.BootstrapContext)
228      */
229     public Schemas findSchemas() throws BootstrapException
230     {
231         try
232         {
233             final Schemas schemas = new Schemas();
234 
235             final Collection<Class<SchemaProvider>> providers = this.loadProviders( SchemaProvider.class );
236             for ( Class<SchemaProvider> provider : providers )
237             {
238                 final SchemaProvider schemaProvider = provider.newInstance();
239                 final Schemas provided = schemaProvider.findSchemas( this );
240                 if ( provided != null )
241                 {
242                     schemas.getSchema().addAll( provided.getSchema() );
243                 }
244             }
245 
246             return schemas;
247         }
248         catch ( final InstantiationException e )
249         {
250             throw new BootstrapException( e );
251         }
252         catch ( final IllegalAccessException e )
253         {
254             throw new BootstrapException( e );
255         }
256     }
257 
258     /**
259      * Creates a new object management and configuration model {@code BootstrapContext} instance.
260      *
261      * @param classLoader The class loader to create a new object management and configuration model bootstrap context
262      * instance with or {@code null} to create a new bootstrap context using the platform's bootstrap class loader.
263      *
264      * @return A new {@code BootstrapContext} instance.
265      *
266      * @throws BootstrapException if creating a new object management and configuration model bootstrap context instance
267      * fails.
268      *
269      * @see #getBootstrapContextClassName()
270      */
271     public static BootstrapContext createBootstrapContext( final ClassLoader classLoader ) throws BootstrapException
272     {
273         if ( DefaultBootstrapContext.class.getName().equals( getBootstrapContextClassName() ) )
274         {
275             return new DefaultBootstrapContext( classLoader );
276         }
277 
278         try
279         {
280             final Class clazz = Class.forName( getBootstrapContextClassName(), true, classLoader );
281             final Constructor ctor = clazz.getConstructor( ClassLoader.class );
282             return (BootstrapContext) ctor.newInstance( classLoader );
283         }
284         catch ( final ClassNotFoundException e )
285         {
286             throw new BootstrapException( e );
287         }
288         catch ( final NoSuchMethodException e )
289         {
290             throw new BootstrapException( e );
291         }
292         catch ( final InstantiationException e )
293         {
294             throw new BootstrapException( e );
295         }
296         catch ( final IllegalAccessException e )
297         {
298             throw new BootstrapException( e );
299         }
300         catch ( final InvocationTargetException e )
301         {
302             throw new BootstrapException( e );
303         }
304         catch ( final ClassCastException e )
305         {
306             throw new BootstrapException( e );
307         }
308     }
309 
310     /**
311      * Creates a new object management and configuration model bootstrap JAXP schema instance.
312      *
313      * @return A new object management and configuration model bootstrap JAXP schema instance.
314      *
315      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXP schema
316      * instance fails.
317      */
318     public abstract javax.xml.validation.Schema createSchema() throws BootstrapException;
319 
320     /**
321      * Creates a new object management and configuration model bootstrap JAXB context instance.
322      *
323      * @return A new object management and configuration model bootstrap JAXB context instance.
324      *
325      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB context
326      * instance fails.
327      */
328     public abstract JAXBContext createContext() throws BootstrapException;
329 
330     /**
331      * Creates a new object management and configuration model bootstrap JAXB marshaller instance.
332      *
333      * @return A new object management and configuration model bootstrap JAXB marshaller instance.
334      *
335      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB marshaller
336      * instance fails.
337      */
338     public abstract Marshaller createMarshaller() throws BootstrapException;
339 
340     /**
341      * Creates a new object management and configuration model bootstrap JAXB unmarshaller instance.
342      *
343      * @return A new object management and configuration model bootstrap JAXB unmarshaller instance.
344      *
345      * @throws BootstrapException if creating a new object management and configuration model bootstrap JAXB
346      * unmarshaller instance fails.
347      */
348     public abstract Unmarshaller createUnmarshaller() throws BootstrapException;
349 
350     private <T> Collection<Class<T>> loadProviders( final Class<T> providerClass ) throws BootstrapException
351     {
352         try
353         {
354             final String providerNamePrefix = providerClass.getName() + ".";
355             final Map<String, Class<T>> providers = new TreeMap<String, Class<T>>( new Comparator<String>()
356             {
357 
358                 public int compare( final String key1, final String key2 )
359                 {
360                     return key1.compareTo( key2 );
361                 }
362 
363             } );
364 
365             final File platformProviders = new File( new StringBuilder().append( System.getProperty( "java.home" ) ).
366                 append( File.separator ).append( "jre" ).append( File.separator ).append( "lib" ).
367                 append( File.separator ).append( "jomc.properties" ).toString() );
368 
369             if ( platformProviders.exists() )
370             {
371                 InputStream in = null;
372                 final java.util.Properties p = new java.util.Properties();
373 
374                 try
375                 {
376                     in = new FileInputStream( platformProviders );
377                     p.load( in );
378                 }
379                 finally
380                 {
381                     if ( in != null )
382                     {
383                         in.close();
384                     }
385                 }
386 
387                 for ( Map.Entry e : p.entrySet() )
388                 {
389                     if ( e.getKey().toString().startsWith( providerNamePrefix ) )
390                     {
391                         final Class<T> provider = this.findClass( e.getValue().toString() );
392                         if ( provider != null )
393                         {
394                             providers.put( e.getKey().toString(), provider );
395                         }
396                     }
397                 }
398             }
399 
400             final Enumeration<URL> serviceProviders =
401                 this.findResources( "META-INF/services/" + providerClass.getName() );
402 
403             while ( serviceProviders.hasMoreElements() )
404             {
405                 final URL url = serviceProviders.nextElement();
406                 final BufferedReader reader = new BufferedReader( new InputStreamReader( url.openStream(), "UTF-8" ) );
407 
408                 String line = null;
409                 while ( ( line = reader.readLine() ) != null )
410                 {
411                     if ( line.contains( "#" ) )
412                     {
413                         continue;
414                     }
415 
416                     final Class<T> provider = this.findClass( line );
417                     if ( provider != null )
418                     {
419                         providers.put( providerNamePrefix + providers.size(), provider );
420                     }
421                 }
422 
423                 reader.close();
424             }
425 
426             return providers.values();
427         }
428         catch ( final IOException e )
429         {
430             throw new BootstrapException( e );
431         }
432     }
433 
434 }