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