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: ModelContext.java 1336 2010-01-19 04:00:00Z schulte2005 $ |
31 | * |
32 | */ |
33 | package org.jomc.model; |
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.io.StringReader; |
42 | import java.io.StringWriter; |
43 | import java.lang.reflect.Constructor; |
44 | import java.lang.reflect.InvocationTargetException; |
45 | import java.net.URL; |
46 | import java.text.MessageFormat; |
47 | import java.util.Collection; |
48 | import java.util.Comparator; |
49 | import java.util.Enumeration; |
50 | import java.util.LinkedList; |
51 | import java.util.List; |
52 | import java.util.Locale; |
53 | import java.util.Map; |
54 | import java.util.ResourceBundle; |
55 | import java.util.TreeMap; |
56 | import java.util.logging.Level; |
57 | import javax.xml.bind.JAXBContext; |
58 | import javax.xml.bind.JAXBException; |
59 | import javax.xml.bind.Marshaller; |
60 | import javax.xml.bind.Unmarshaller; |
61 | import javax.xml.transform.Source; |
62 | import javax.xml.validation.Schema; |
63 | import javax.xml.validation.Validator; |
64 | import org.w3c.dom.ls.LSResourceResolver; |
65 | import org.xml.sax.EntityResolver; |
66 | import org.xml.sax.SAXException; |
67 | import org.xml.sax.SAXParseException; |
68 | import org.xml.sax.helpers.DefaultHandler; |
69 | |
70 | /** |
71 | * Object management and configuration model context interface. |
72 | * |
73 | * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> |
74 | * @version $Id: ModelContext.java 1336 2010-01-19 04:00:00Z schulte2005 $ |
75 | * @see #createModelContext(java.lang.ClassLoader) |
76 | */ |
77 | public abstract class ModelContext |
78 | { |
79 | |
80 | /** Listener interface. */ |
81 | public abstract static class Listener |
82 | { |
83 | |
84 | /** |
85 | * Get called on logging. |
86 | * |
87 | * @param level The level of the event. |
88 | * @param message The message of the event or {@code null}. |
89 | * @param t The throwable of the event or {@code null}. |
90 | * |
91 | * @throws NullPointerException if {@code level} is {@code null}. |
92 | */ |
93 | public abstract void onLog( Level level, String message, Throwable t ); |
94 | |
95 | } |
96 | |
97 | /** |
98 | * Log level events are logged at by default. |
99 | * @see #getDefaultLogLevel() |
100 | */ |
101 | private static final Level DEFAULT_LOG_LEVEL = Level.WARNING; |
102 | |
103 | /** Default log level. */ |
104 | private static volatile Level defaultLogLevel; |
105 | |
106 | /** Class name of the {@code ModelContext} implementation. */ |
107 | private static volatile String modelContextClassName; |
108 | |
109 | /** The listeners of the instance. */ |
110 | private List<Listener> listeners; |
111 | |
112 | /** Log level of the instance. */ |
113 | private Level logLevel; |
114 | |
115 | /** The class loader of the context. */ |
116 | private ClassLoader classLoader; |
117 | |
118 | /** |
119 | * Creates a new {@code ModelContext} instance taking a class loader. |
120 | * |
121 | * @param classLoader The class loader of the context. |
122 | */ |
123 | protected ModelContext( final ClassLoader classLoader ) |
124 | { |
125 | super(); |
126 | this.classLoader = classLoader; |
127 | } |
128 | |
129 | /** |
130 | * Gets the class loader of the context. |
131 | * |
132 | * @return The class loader of the context. |
133 | */ |
134 | public ClassLoader getClassLoader() |
135 | { |
136 | if ( this.classLoader == null ) |
137 | { |
138 | this.classLoader = new ClassLoader( null ) |
139 | { |
140 | |
141 | @Override |
142 | public String toString() |
143 | { |
144 | return ModelContext.class.getName() + ".BootstrapClassLoader@" + System.identityHashCode( this ); |
145 | } |
146 | |
147 | }; |
148 | |
149 | } |
150 | |
151 | return this.classLoader; |
152 | } |
153 | |
154 | /** |
155 | * Gets the list of registered listeners. |
156 | * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make |
157 | * to the returned list will be present inside the object. This is why there is no {@code set} method for the |
158 | * listeners property.</p> |
159 | * |
160 | * @return The list of registered listeners. |
161 | * |
162 | * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable) |
163 | */ |
164 | public List<Listener> getListeners() |
165 | { |
166 | if ( this.listeners == null ) |
167 | { |
168 | this.listeners = new LinkedList<Listener>(); |
169 | } |
170 | |
171 | return this.listeners; |
172 | } |
173 | |
174 | /** |
175 | * Gets the default log level events are logged at. |
176 | * <p>The default log level is controlled by system property |
177 | * {@code org.jomc.model.ModelContext.defaultLogLevel} holding the log level to log events at by default. |
178 | * If that property is not set, the {@code WARNING} default is returned.</p> |
179 | * |
180 | * @return The log level events are logged at by default. |
181 | * |
182 | * @see #getLogLevel() |
183 | * @see Level#parse(java.lang.String) |
184 | */ |
185 | public static Level getDefaultLogLevel() |
186 | { |
187 | if ( defaultLogLevel == null ) |
188 | { |
189 | defaultLogLevel = Level.parse( System.getProperty( |
190 | "org.jomc.model.ModelContext.defaultLogLevel", DEFAULT_LOG_LEVEL.getName() ) ); |
191 | |
192 | } |
193 | |
194 | return defaultLogLevel; |
195 | } |
196 | |
197 | /** |
198 | * Sets the default log level events are logged at. |
199 | * |
200 | * @param value The new default level events are logged at or {@code null}. |
201 | * |
202 | * @see #getDefaultLogLevel() |
203 | */ |
204 | public static void setDefaultLogLevel( final Level value ) |
205 | { |
206 | defaultLogLevel = value; |
207 | } |
208 | |
209 | /** |
210 | * Gets the log level of the instance. |
211 | * |
212 | * @return The log level of the instance. |
213 | * |
214 | * @see #getDefaultLogLevel() |
215 | * @see #setLogLevel(java.util.logging.Level) |
216 | * @see #isLoggable(java.util.logging.Level) |
217 | */ |
218 | public Level getLogLevel() |
219 | { |
220 | if ( this.logLevel == null ) |
221 | { |
222 | this.logLevel = getDefaultLogLevel(); |
223 | this.log( Level.CONFIG, this.getMessage( "defaultLogLevelInfo", new Object[] |
224 | { |
225 | this.getClass().getCanonicalName(), this.logLevel.getLocalizedName() |
226 | } ), null ); |
227 | |
228 | } |
229 | |
230 | return this.logLevel; |
231 | } |
232 | |
233 | /** |
234 | * Sets the log level of the instance. |
235 | * |
236 | * @param value The new log level of the instance or {@code null}. |
237 | * |
238 | * @see #getLogLevel() |
239 | * @see #isLoggable(java.util.logging.Level) |
240 | */ |
241 | public void setLogLevel( final Level value ) |
242 | { |
243 | this.logLevel = value; |
244 | } |
245 | |
246 | /** |
247 | * Checks if a message at a given level is provided to the listeners of the instance. |
248 | * |
249 | * @param level The level to test. |
250 | * |
251 | * @return {@code true} if messages at {@code level} are provided to the listeners of the instance; |
252 | * {@code false} if messages at {@code level} are not provided to the listeners of the instance. |
253 | * |
254 | * @throws NullPointerException if {@code level} is {@code null}. |
255 | * |
256 | * @see #getLogLevel() |
257 | * @see #setLogLevel(java.util.logging.Level) |
258 | */ |
259 | public boolean isLoggable( final Level level ) |
260 | { |
261 | if ( level == null ) |
262 | { |
263 | throw new NullPointerException( "level" ); |
264 | } |
265 | |
266 | return level.intValue() >= this.getLogLevel().intValue(); |
267 | } |
268 | |
269 | /** |
270 | * Notifies registered listeners. |
271 | * |
272 | * @param level The level of the event. |
273 | * @param message The message of the event or {@code null}. |
274 | * @param throwable The throwable of the event {@code null}. |
275 | * |
276 | * @throws NullPointerException if {@code level} is {@code null}. |
277 | * |
278 | * @see #getListeners() |
279 | * @see #isLoggable(java.util.logging.Level) |
280 | */ |
281 | public void log( final Level level, final String message, final Throwable throwable ) |
282 | { |
283 | if ( level == null ) |
284 | { |
285 | throw new NullPointerException( "level" ); |
286 | } |
287 | |
288 | if ( this.isLoggable( level ) ) |
289 | { |
290 | for ( Listener l : this.getListeners() ) |
291 | { |
292 | l.onLog( level, message, throwable ); |
293 | } |
294 | } |
295 | } |
296 | |
297 | /** |
298 | * Gets the name of the class providing the {@code ModelContext} implementation. |
299 | * <p>The name of the class providing the {@code ModelContext} implementation returned by method |
300 | * {@link #createModelContext(java.lang.ClassLoader)} is controlled by system property |
301 | * {@code org.jomc.model.ModelContext.className}. If that property is not set, the name of the |
302 | * {@link DefaultModelContext} class is returned.</p> |
303 | * |
304 | * @return The name of the class providing the {@code ModelContext} implementation. |
305 | * |
306 | * @see #setModelContextClassName(java.lang.String) |
307 | */ |
308 | public static String getModelContextClassName() |
309 | { |
310 | if ( modelContextClassName == null ) |
311 | { |
312 | modelContextClassName = System.getProperty( "org.jomc.model.ModelContext.className", |
313 | DefaultModelContext.class.getName() ); |
314 | |
315 | } |
316 | |
317 | return modelContextClassName; |
318 | } |
319 | |
320 | /** |
321 | * Sets the name of the class providing the ModelContext implementation. |
322 | * |
323 | * @param value The new name of the class providing the ModelContext implementation or {@code null}. |
324 | * |
325 | * @see #getModelContextClassName() |
326 | */ |
327 | public static void setModelContextClassName( final String value ) |
328 | { |
329 | modelContextClassName = value; |
330 | } |
331 | |
332 | /** |
333 | * Searches the context for a class with a given name. |
334 | * |
335 | * @param name The name of the class to return. |
336 | * |
337 | * @return A Class object of the class with name {@code name} or {@code null} if no such class is found. |
338 | * |
339 | * @throws NullPointerException if {@code name} is {@code null}. |
340 | * @throws ModelException if searching fails. |
341 | */ |
342 | public Class findClass( final String name ) throws ModelException |
343 | { |
344 | if ( name == null ) |
345 | { |
346 | throw new NullPointerException( "name" ); |
347 | } |
348 | |
349 | try |
350 | { |
351 | return Class.forName( name, true, this.getClassLoader() ); |
352 | } |
353 | catch ( final ClassNotFoundException e ) |
354 | { |
355 | if ( this.isLoggable( Level.FINE ) ) |
356 | { |
357 | this.log( Level.FINE, this.getMessage( "classNotFound", new Object[] |
358 | { |
359 | name |
360 | } ), e ); |
361 | |
362 | } |
363 | |
364 | return null; |
365 | } |
366 | } |
367 | |
368 | /** |
369 | * Searches the context for a resource with a given name. |
370 | * |
371 | * @param name The name of the resource to return. |
372 | * |
373 | * @return An URL object for reading the resource or {@code null} if no such resource is found. |
374 | * |
375 | * @throws NullPointerException if {@code name} is {@code null}. |
376 | * @throws ModelException if searching fails. |
377 | */ |
378 | public URL findResource( final String name ) throws ModelException |
379 | { |
380 | if ( name == null ) |
381 | { |
382 | throw new NullPointerException( "name" ); |
383 | } |
384 | |
385 | return this.getClassLoader().getResource( name ); |
386 | } |
387 | |
388 | /** |
389 | * Searches the context for resources with a given name. |
390 | * |
391 | * @param name The name of the resources to return. |
392 | * |
393 | * @return An enumeration of URL objects for reading the resources. If no resources are found, the enumeration will |
394 | * be empty. |
395 | * |
396 | * @throws NullPointerException if {@code name} is {@code null}. |
397 | * @throws ModelException if searching fails. |
398 | */ |
399 | public Enumeration<URL> findResources( final String name ) throws ModelException |
400 | { |
401 | if ( name == null ) |
402 | { |
403 | throw new NullPointerException( "name" ); |
404 | } |
405 | |
406 | try |
407 | { |
408 | return this.getClassLoader().getResources( name ); |
409 | } |
410 | catch ( final IOException e ) |
411 | { |
412 | throw new ModelException( e ); |
413 | } |
414 | } |
415 | |
416 | /** |
417 | * Searches the context for modules. |
418 | * <p>This method loads {@code ModelProvider} classes setup via |
419 | * {@code META-INF/services/org.jomc.model.ModelProvider} resources and returns a list of provided modules.</p> |
420 | * |
421 | * @return The modules found in the context. |
422 | * |
423 | * @throws ModelException if searching modules fails. |
424 | * |
425 | * @see ModelProvider#findModules(org.jomc.model.ModelContext) |
426 | */ |
427 | public Modules findModules() throws ModelException |
428 | { |
429 | try |
430 | { |
431 | final Text text = new Text(); |
432 | text.setLanguage( "en" ); |
433 | text.setValue( this.getMessage( "contextModulesInfo", null ) ); |
434 | |
435 | final Modules modules = new Modules(); |
436 | modules.setDocumentation( new Texts() ); |
437 | modules.getDocumentation().setDefaultLanguage( "en" ); |
438 | modules.getDocumentation().getText().add( text ); |
439 | |
440 | final Collection<Class<ModelProvider>> providers = this.loadProviders( ModelProvider.class ); |
441 | for ( Class<ModelProvider> provider : providers ) |
442 | { |
443 | if ( this.isLoggable( Level.CONFIG ) ) |
444 | { |
445 | this.log( Level.CONFIG, this.getMessage( "modelProviderInfo", new Object[] |
446 | { |
447 | provider.getName() |
448 | } ), null ); |
449 | |
450 | } |
451 | |
452 | final ModelProvider modelProvider = provider.newInstance(); |
453 | final Modules provided = modelProvider.findModules( this ); |
454 | if ( provided != null ) |
455 | { |
456 | modules.getModule().addAll( provided.getModule() ); |
457 | } |
458 | } |
459 | |
460 | if ( this.isLoggable( Level.FINEST ) ) |
461 | { |
462 | final StringWriter stringWriter = new StringWriter(); |
463 | final Marshaller m = this.createMarshaller(); |
464 | m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); |
465 | m.marshal( new ObjectFactory().createModules( modules ), stringWriter ); |
466 | stringWriter.close(); |
467 | |
468 | this.log( Level.FINEST, this.getMessage( "foundModules", null ), null ); |
469 | |
470 | final BufferedReader reader = new BufferedReader( new StringReader( stringWriter.toString() ) ); |
471 | String line; |
472 | |
473 | while ( ( line = reader.readLine() ) != null ) |
474 | { |
475 | this.log( Level.FINEST, "\t" + line, null ); |
476 | } |
477 | |
478 | reader.close(); |
479 | } |
480 | |
481 | return modules; |
482 | } |
483 | catch ( final InstantiationException e ) |
484 | { |
485 | throw new ModelException( e ); |
486 | } |
487 | catch ( final IllegalAccessException e ) |
488 | { |
489 | throw new ModelException( e ); |
490 | } |
491 | catch ( final IOException e ) |
492 | { |
493 | throw new ModelException( e ); |
494 | } |
495 | catch ( final JAXBException e ) |
496 | { |
497 | throw new ModelException( e ); |
498 | } |
499 | } |
500 | |
501 | /** |
502 | * Processes modules. |
503 | * <p>This method loads {@code ModelProcessor} classes setup via |
504 | * {@code META-INF/services/org.jomc.model.ModelProcessor} resources and returns a list of processed modules.</p> |
505 | * |
506 | * @param modules The modules to process. |
507 | * |
508 | * @return The processed modules. |
509 | * |
510 | * @throws NullPointerException if {@code modules} is {@code null}. |
511 | * @throws ModelException if processing modules fails. |
512 | * |
513 | * @see ModelProcessor#processModules(org.jomc.model.ModelContext, org.jomc.model.Modules) |
514 | */ |
515 | public Modules processModules( final Modules modules ) throws ModelException |
516 | { |
517 | if ( modules == null ) |
518 | { |
519 | throw new NullPointerException( "modules" ); |
520 | } |
521 | |
522 | try |
523 | { |
524 | Modules processed = modules; |
525 | final Collection<Class<ModelProcessor>> processors = this.loadProviders( ModelProcessor.class ); |
526 | |
527 | for ( Class<ModelProcessor> processor : processors ) |
528 | { |
529 | if ( this.isLoggable( Level.CONFIG ) ) |
530 | { |
531 | this.log( Level.CONFIG, this.getMessage( "modelProcessorInfo", new Object[] |
532 | { |
533 | processor.getName() |
534 | } ), null ); |
535 | |
536 | } |
537 | |
538 | final ModelProcessor modelProcessor = processor.newInstance(); |
539 | final Modules current = modelProcessor.processModules( this, processed ); |
540 | if ( current != null ) |
541 | { |
542 | processed = current; |
543 | } |
544 | } |
545 | |
546 | if ( this.isLoggable( Level.FINEST ) ) |
547 | { |
548 | final StringWriter stringWriter = new StringWriter(); |
549 | final Marshaller m = this.createMarshaller(); |
550 | m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE ); |
551 | m.marshal( new ObjectFactory().createModules( processed ), stringWriter ); |
552 | stringWriter.close(); |
553 | |
554 | this.log( Level.FINEST, this.getMessage( "processedModules", null ), null ); |
555 | |
556 | final BufferedReader reader = new BufferedReader( new StringReader( stringWriter.toString() ) ); |
557 | String line; |
558 | |
559 | while ( ( line = reader.readLine() ) != null ) |
560 | { |
561 | this.log( Level.FINEST, "\t" + line, null ); |
562 | } |
563 | |
564 | reader.close(); |
565 | } |
566 | |
567 | return processed; |
568 | } |
569 | catch ( final InstantiationException e ) |
570 | { |
571 | throw new ModelException( e ); |
572 | } |
573 | catch ( final IllegalAccessException e ) |
574 | { |
575 | throw new ModelException( e ); |
576 | } |
577 | catch ( final IOException e ) |
578 | { |
579 | throw new ModelException( e ); |
580 | } |
581 | catch ( final JAXBException e ) |
582 | { |
583 | throw new ModelException( e ); |
584 | } |
585 | } |
586 | |
587 | /** |
588 | * Validates a given model. |
589 | * |
590 | * @param model A source providing the model to validate. |
591 | * |
592 | * @return Validation report. |
593 | * |
594 | * @throws NullPointerException if {@code model} is {@code null}. |
595 | * @throws ModelException if validating the model fails. |
596 | */ |
597 | public ModelValidationReport validateModel( final Source model ) throws ModelException |
598 | { |
599 | if ( model == null ) |
600 | { |
601 | throw new NullPointerException( "model" ); |
602 | } |
603 | |
604 | final Schema schema = this.createSchema(); |
605 | final Validator validator = schema.newValidator(); |
606 | final ModelErrorHandler modelErrorHandler = new ModelErrorHandler(); |
607 | validator.setErrorHandler( modelErrorHandler ); |
608 | |
609 | try |
610 | { |
611 | validator.validate( model ); |
612 | } |
613 | catch ( final SAXException e ) |
614 | { |
615 | if ( modelErrorHandler.getReport().isModelValid() ) |
616 | { |
617 | throw new ModelException( e ); |
618 | } |
619 | } |
620 | catch ( final IOException e ) |
621 | { |
622 | throw new ModelException( e ); |
623 | } |
624 | |
625 | return modelErrorHandler.getReport(); |
626 | } |
627 | |
628 | /** |
629 | * Validates a given list of modules. |
630 | * <p>This method loads {@code ModelValidator} classes setup via |
631 | * {@code META-INF/services/org.jomc.model.ModelValidator} resources and returns an aggregated validation report.</p> |
632 | * |
633 | * @param modules The list of modules to validate. |
634 | * |
635 | * @return Validation report. |
636 | * |
637 | * @throws NullPointerException if {@code modules} is {@code null}. |
638 | * @throws ModelException if validating the modules fails. |
639 | * |
640 | * @see ModelValidator#validateModel(org.jomc.model.ModelContext, org.jomc.model.Modules) |
641 | */ |
642 | public ModelValidationReport validateModel( final Modules modules ) throws ModelException |
643 | { |
644 | if ( modules == null ) |
645 | { |
646 | throw new NullPointerException( "modules" ); |
647 | } |
648 | |
649 | try |
650 | { |
651 | final Collection<Class<ModelValidator>> validators = this.loadProviders( ModelValidator.class ); |
652 | final ModelValidationReport report = new ModelValidationReport(); |
653 | |
654 | for ( Class<ModelValidator> validator : validators ) |
655 | { |
656 | if ( this.isLoggable( Level.CONFIG ) ) |
657 | { |
658 | this.log( Level.CONFIG, this.getMessage( "modelValidatorInfo", new Object[] |
659 | { |
660 | validator.getName() |
661 | } ), null ); |
662 | |
663 | } |
664 | |
665 | final ModelValidator modelValidator = validator.newInstance(); |
666 | final ModelValidationReport current = modelValidator.validateModel( this, modules ); |
667 | if ( current != null ) |
668 | { |
669 | report.getDetails().addAll( current.getDetails() ); |
670 | } |
671 | } |
672 | |
673 | return report; |
674 | } |
675 | catch ( final InstantiationException e ) |
676 | { |
677 | throw new ModelException( e ); |
678 | } |
679 | catch ( final IllegalAccessException e ) |
680 | { |
681 | throw new ModelException( e ); |
682 | } |
683 | } |
684 | |
685 | /** |
686 | * Creates a new object management and configuration model {@code ModelContext} instance. |
687 | * |
688 | * @param classLoader The class loader to create a new object management and configuration model context instance |
689 | * with or {@code null} to create a new context using the platform's bootstrap class loader. |
690 | * |
691 | * @return A new {@code ModelContext} instance. |
692 | * |
693 | * @throws ModelException if creating a new object management and configuration model context instance fails. |
694 | * |
695 | * @see #getModelContextClassName() |
696 | */ |
697 | public static ModelContext createModelContext( final ClassLoader classLoader ) throws ModelException |
698 | { |
699 | if ( DefaultModelContext.class.getName().equals( getModelContextClassName() ) ) |
700 | { |
701 | return new DefaultModelContext( classLoader ); |
702 | } |
703 | |
704 | try |
705 | { |
706 | final Class clazz = Class.forName( getModelContextClassName(), true, classLoader ); |
707 | final Constructor ctor = clazz.getConstructor( ClassLoader.class ); |
708 | return (ModelContext) ctor.newInstance( classLoader ); |
709 | } |
710 | catch ( final ClassNotFoundException e ) |
711 | { |
712 | throw new ModelException( e ); |
713 | } |
714 | catch ( final NoSuchMethodException e ) |
715 | { |
716 | throw new ModelException( e ); |
717 | } |
718 | catch ( final InstantiationException e ) |
719 | { |
720 | throw new ModelException( e ); |
721 | } |
722 | catch ( final IllegalAccessException e ) |
723 | { |
724 | throw new ModelException( e ); |
725 | } |
726 | catch ( final InvocationTargetException e ) |
727 | { |
728 | throw new ModelException( e ); |
729 | } |
730 | catch ( final ClassCastException e ) |
731 | { |
732 | throw new ModelException( e ); |
733 | } |
734 | } |
735 | |
736 | /** |
737 | * Creates a new object management and configuration model SAX entity resolver instance. |
738 | * |
739 | * @return A new object management and configuration model SAX entity resolver instance. |
740 | * |
741 | * @throws ModelException if creating a new object management and configuration model SAX entity resolver instance |
742 | * fails. |
743 | */ |
744 | public abstract EntityResolver createEntityResolver() throws ModelException; |
745 | |
746 | /** |
747 | * Creates a new object management and configuration model L/S resource resolver instance. |
748 | * |
749 | * @return A new object management and configuration model L/S resource resolver instance. |
750 | * |
751 | * @throws ModelException if creating a new object management and configuration model L/S resource resolver instance |
752 | * fails. |
753 | */ |
754 | public abstract LSResourceResolver createResourceResolver() throws ModelException; |
755 | |
756 | /** |
757 | * Creates a new object management and configuration model JAXP schema instance. |
758 | * |
759 | * @return A new object management and configuration model JAXP schema instance. |
760 | * |
761 | * @throws ModelException if creating a new object management and configuration model JAXP schema instance fails. |
762 | */ |
763 | public abstract Schema createSchema() throws ModelException; |
764 | |
765 | /** |
766 | * Creates a new object management and configuration model JAXB context instance. |
767 | * |
768 | * @return A new object management and configuration model JAXB context instance. |
769 | * |
770 | * @throws ModelException if creating a new object management and configuration model JAXB context instance fails. |
771 | */ |
772 | public abstract JAXBContext createContext() throws ModelException; |
773 | |
774 | /** |
775 | * Creates a new object management and configuration model JAXB marshaller instance. |
776 | * |
777 | * @return A new object management and configuration model JAXB marshaller instance. |
778 | * |
779 | * @throws ModelException if creating a new object management and configuration model JAXB marshaller instance |
780 | * fails. |
781 | */ |
782 | public abstract Marshaller createMarshaller() throws ModelException; |
783 | |
784 | /** |
785 | * Creates a new object management and configuration model JAXB unmarshaller instance. |
786 | * |
787 | * @return A new object management and configuration model JAXB unmarshaller instance. |
788 | * |
789 | * @throws ModelException if creating a new object management and configuration model JAXB unmarshaller instance |
790 | * fails. |
791 | */ |
792 | public abstract Unmarshaller createUnmarshaller() throws ModelException; |
793 | |
794 | private <T> Collection<Class<T>> loadProviders( final Class<T> providerClass ) throws ModelException |
795 | { |
796 | try |
797 | { |
798 | final String providerNamePrefix = providerClass.getName() + "."; |
799 | final Map<String, Class<T>> providers = new TreeMap<String, Class<T>>( new Comparator<String>() |
800 | { |
801 | |
802 | public int compare( final String key1, final String key2 ) |
803 | { |
804 | return key1.compareTo( key2 ); |
805 | } |
806 | |
807 | } ); |
808 | |
809 | final File platformProviders = new File( new StringBuilder().append( System.getProperty( "java.home" ) ). |
810 | append( File.separator ).append( "jre" ).append( File.separator ).append( "lib" ). |
811 | append( File.separator ).append( "jomc.properties" ).toString() ); |
812 | |
813 | if ( platformProviders.exists() ) |
814 | { |
815 | if ( this.isLoggable( Level.CONFIG ) ) |
816 | { |
817 | this.log( Level.CONFIG, this.getMessage( "processing", new Object[] |
818 | { |
819 | platformProviders.getAbsolutePath() |
820 | } ), null ); |
821 | |
822 | } |
823 | |
824 | InputStream in = null; |
825 | final java.util.Properties p = new java.util.Properties(); |
826 | |
827 | try |
828 | { |
829 | in = new FileInputStream( platformProviders ); |
830 | p.load( in ); |
831 | } |
832 | finally |
833 | { |
834 | if ( in != null ) |
835 | { |
836 | in.close(); |
837 | } |
838 | } |
839 | |
840 | for ( Map.Entry e : p.entrySet() ) |
841 | { |
842 | if ( e.getKey().toString().startsWith( providerNamePrefix ) ) |
843 | { |
844 | final Class<T> provider = this.findClass( e.getValue().toString() ); |
845 | if ( provider != null ) |
846 | { |
847 | providers.put( e.getKey().toString(), provider ); |
848 | } |
849 | } |
850 | } |
851 | } |
852 | |
853 | final Enumeration<URL> serviceProviders = |
854 | this.findResources( "META-INF/services/" + providerClass.getName() ); |
855 | |
856 | while ( serviceProviders.hasMoreElements() ) |
857 | { |
858 | final URL url = serviceProviders.nextElement(); |
859 | final BufferedReader reader = new BufferedReader( new InputStreamReader( url.openStream(), "UTF-8" ) ); |
860 | |
861 | String line = null; |
862 | while ( ( line = reader.readLine() ) != null ) |
863 | { |
864 | if ( line.contains( "#" ) ) |
865 | { |
866 | continue; |
867 | } |
868 | |
869 | final Class<T> provider = this.findClass( line ); |
870 | if ( provider != null ) |
871 | { |
872 | providers.put( providerNamePrefix + providers.size(), provider ); |
873 | } |
874 | } |
875 | |
876 | reader.close(); |
877 | } |
878 | |
879 | return providers.values(); |
880 | } |
881 | catch ( final IOException e ) |
882 | { |
883 | throw new ModelException( e ); |
884 | } |
885 | } |
886 | |
887 | private String getMessage( final String key, final Object args ) |
888 | { |
889 | return new MessageFormat( ResourceBundle.getBundle( ModelContext.class.getName().replace( '.', '/' ), |
890 | Locale.getDefault() ).getString( key ) ).format( args ); |
891 | |
892 | } |
893 | |
894 | } |
895 | |
896 | /** |
897 | * {@code ErrorHandler} collecting {@code ModelValidationReport} details. |
898 | * |
899 | * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> |
900 | * @version $Id: ModelContext.java 1336 2010-01-19 04:00:00Z schulte2005 $ |
901 | */ |
902 | class ModelErrorHandler extends DefaultHandler |
903 | { |
904 | |
905 | /** The report of the instance. */ |
906 | private ModelValidationReport report; |
907 | |
908 | /** Creates a new {@code ModelErrorHandler} instance. */ |
909 | public ModelErrorHandler() |
910 | { |
911 | this( null ); |
912 | } |
913 | |
914 | /** |
915 | * Creates a new {@code ModelErrorHandler} instance taking a report to use for collecting validation events. |
916 | * |
917 | * @param report A report to use for collecting validation events. |
918 | */ |
919 | public ModelErrorHandler( final ModelValidationReport report ) |
920 | { |
921 | super(); |
922 | this.report = report; |
923 | } |
924 | |
925 | /** |
926 | * Gets the report of the instance. |
927 | * |
928 | * @return The report of the instance. |
929 | */ |
930 | public ModelValidationReport getReport() |
931 | { |
932 | if ( this.report == null ) |
933 | { |
934 | this.report = new ModelValidationReport(); |
935 | } |
936 | |
937 | return this.report; |
938 | } |
939 | |
940 | @Override |
941 | public void warning( final SAXParseException exception ) throws SAXException |
942 | { |
943 | this.getReport().getDetails().add( new ModelValidationReport.Detail( |
944 | "W3C XML 1.0 Recommendation - Warning condition", Level.WARNING, exception.getMessage(), null ) ); |
945 | |
946 | } |
947 | |
948 | @Override |
949 | public void error( final SAXParseException exception ) throws SAXException |
950 | { |
951 | this.getReport().getDetails().add( new ModelValidationReport.Detail( |
952 | "W3C XML 1.0 Recommendation - Section 1.2 - Error", Level.SEVERE, exception.getMessage(), null ) ); |
953 | |
954 | } |
955 | |
956 | @Override |
957 | public void fatalError( final SAXParseException exception ) throws SAXException |
958 | { |
959 | this.getReport().getDetails().add( new ModelValidationReport.Detail( |
960 | "W3C XML 1.0 Recommendation - Section 1.2 - Fatal Error", Level.SEVERE, exception.getMessage(), null ) ); |
961 | |
962 | } |
963 | |
964 | } |