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 | } |