1 /*
2 * Copyright (C) Christian Schulte, 2005-206
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $JOMC: ModelContext.java 4987 2014-11-15 19:51:47Z schulte $
29 *
30 */
31 package org.jomc.modlet;
32
33 import java.io.IOException;
34 import java.lang.reflect.Constructor;
35 import java.lang.reflect.InvocationTargetException;
36 import java.net.URI;
37 import java.net.URL;
38 import java.text.MessageFormat;
39 import java.util.Collections;
40 import java.util.Enumeration;
41 import java.util.HashMap;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Locale;
45 import java.util.Map;
46 import java.util.ResourceBundle;
47 import java.util.Set;
48 import java.util.logging.Level;
49 import javax.xml.bind.JAXBContext;
50 import javax.xml.bind.JAXBException;
51 import javax.xml.bind.Marshaller;
52 import javax.xml.bind.Unmarshaller;
53 import javax.xml.bind.util.JAXBSource;
54 import javax.xml.transform.Source;
55 import javax.xml.validation.Validator;
56 import org.w3c.dom.ls.LSResourceResolver;
57 import org.xml.sax.EntityResolver;
58 import org.xml.sax.SAXException;
59
60 /**
61 * Model context interface.
62 * <p>
63 * <b>Use Cases:</b><br/><ul>
64 * <li>{@link #createContext(java.lang.String) }</li>
65 * <li>{@link #createEntityResolver(java.lang.String) }</li>
66 * <li>{@link #createMarshaller(java.lang.String) }</li>
67 * <li>{@link #createResourceResolver(java.lang.String) }</li>
68 * <li>{@link #createSchema(java.lang.String) }</li>
69 * <li>{@link #createUnmarshaller(java.lang.String) }</li>
70 * <li>{@link #findModel(java.lang.String) }</li>
71 * <li>{@link #findModel(org.jomc.modlet.Model) }</li>
72 * <li>{@link #processModel(org.jomc.modlet.Model) }</li>
73 * <li>{@link #validateModel(org.jomc.modlet.Model) }</li>
74 * <li>{@link #validateModel(java.lang.String, javax.xml.transform.Source) }</li>
75 * </ul>
76 *
77 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
78 * @version $JOMC: ModelContext.java 4987 2014-11-15 19:51:47Z schulte $
79 *
80 * @see ModelContextFactory
81 */
82 public abstract class ModelContext
83 {
84
85 /** Listener interface. */
86 public abstract static class Listener
87 {
88
89 /** Creates a new {@code Listener} instance. */
90 public Listener()
91 {
92 super();
93 }
94
95 /**
96 * Gets called on logging.
97 *
98 * @param level The level of the event.
99 * @param message The message of the event or {@code null}.
100 * @param t The throwable of the event or {@code null}.
101 *
102 * @throws NullPointerException if {@code level} is {@code null}.
103 */
104 public void onLog( final Level level, final String message, final Throwable t )
105 {
106 if ( level == null )
107 {
108 throw new NullPointerException( "level" );
109 }
110 }
111
112 }
113
114 /**
115 * Default {@code http://jomc.org/modlet} namespace schema system id.
116 * @see #getDefaultModletSchemaSystemId()
117 */
118 private static final String DEFAULT_MODLET_SCHEMA_SYSTEM_ID =
119 "http://xml.jomc.org/modlet/jomc-modlet-1.8.xsd";
120
121 /**
122 * Log level events are logged at by default.
123 * @see #getDefaultLogLevel()
124 */
125 private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
126
127 /** Default log level. */
128 private static volatile Level defaultLogLevel;
129
130 /** Default {@code http://jomc.org/model/modlet} namespace schema system id. */
131 private static volatile String defaultModletSchemaSystemId;
132
133 /** Class name of the {@code ModelContext} implementation. */
134 @Deprecated
135 private static volatile String modelContextClassName;
136
137 /** The attributes of the instance. */
138 private final Map<String, Object> attributes = new HashMap<String, Object>();
139
140 /** The class loader of the instance. */
141 private ClassLoader classLoader;
142
143 /**
144 * Flag indicating the {@code classLoader} field is initialized.
145 * @since 1.2
146 */
147 private boolean classLoaderSet;
148
149 /** The listeners of the instance. */
150 private List<Listener> listeners;
151
152 /** Log level of the instance. */
153 private Level logLevel;
154
155 /** The {@code Modlets} of the instance. */
156 private Modlets modlets;
157
158 /** Modlet namespace schema system id of the instance. */
159 private String modletSchemaSystemId;
160
161 /**
162 * Creates a new {@code ModelContext} instance.
163 * @since 1.2
164 */
165 public ModelContext()
166 {
167 super();
168 this.classLoader = null;
169 this.classLoaderSet = false;
170 }
171
172 /**
173 * Creates a new {@code ModelContext} instance taking a class loader.
174 *
175 * @param classLoader The class loader of the context.
176 *
177 * @see #getClassLoader()
178 */
179 public ModelContext( final ClassLoader classLoader )
180 {
181 super();
182 this.classLoader = classLoader;
183 this.classLoaderSet = true;
184 }
185
186 /**
187 * Gets a set holding the names of all attributes of the context.
188 *
189 * @return An unmodifiable set holding the names of all attributes of the context.
190 *
191 * @see #clearAttribute(java.lang.String)
192 * @see #getAttribute(java.lang.String)
193 * @see #getAttribute(java.lang.String, java.lang.Object)
194 * @see #setAttribute(java.lang.String, java.lang.Object)
195 * @since 1.2
196 */
197 public Set<String> getAttributeNames()
198 {
199 return Collections.unmodifiableSet( this.attributes.keySet() );
200 }
201
202 /**
203 * Gets an attribute of the context.
204 *
205 * @param name The name of the attribute to get.
206 *
207 * @return The value of the attribute with name {@code name}; {@code null} if no attribute matching {@code name} is
208 * found.
209 *
210 * @throws NullPointerException if {@code name} is {@code null}.
211 *
212 * @see #getAttribute(java.lang.String, java.lang.Object)
213 * @see #setAttribute(java.lang.String, java.lang.Object)
214 * @see #clearAttribute(java.lang.String)
215 */
216 public Object getAttribute( final String name )
217 {
218 if ( name == null )
219 {
220 throw new NullPointerException( "name" );
221 }
222
223 return this.attributes.get( name );
224 }
225
226 /**
227 * Gets an attribute of the context.
228 *
229 * @param name The name of the attribute to get.
230 * @param def The value to return if no attribute matching {@code name} is found.
231 *
232 * @return The value of the attribute with name {@code name}; {@code def} if no such attribute is found.
233 *
234 * @throws NullPointerException if {@code name} is {@code null}.
235 *
236 * @see #getAttribute(java.lang.String)
237 * @see #setAttribute(java.lang.String, java.lang.Object)
238 * @see #clearAttribute(java.lang.String)
239 */
240 public Object getAttribute( final String name, final Object def )
241 {
242 if ( name == null )
243 {
244 throw new NullPointerException( "name" );
245 }
246
247 Object value = this.getAttribute( name );
248
249 if ( value == null )
250 {
251 value = def;
252 }
253
254 return value;
255 }
256
257 /**
258 * Sets an attribute in the context.
259 *
260 * @param name The name of the attribute to set.
261 * @param value The value of the attribute to set.
262 *
263 * @return The previous value of the attribute with name {@code name}; {@code null} if no such value is found.
264 *
265 * @throws NullPointerException if {@code name} or {@code value} is {@code null}.
266 *
267 * @see #getAttribute(java.lang.String)
268 * @see #getAttribute(java.lang.String, java.lang.Object)
269 * @see #clearAttribute(java.lang.String)
270 */
271 public Object setAttribute( final String name, final Object value )
272 {
273 if ( name == null )
274 {
275 throw new NullPointerException( "name" );
276 }
277 if ( value == null )
278 {
279 throw new NullPointerException( "value" );
280 }
281
282 return this.attributes.put( name, value );
283 }
284
285 /**
286 * Removes an attribute from the context.
287 *
288 * @param name The name of the attribute to remove.
289 *
290 * @throws NullPointerException if {@code name} is {@code null}.
291 *
292 * @see #getAttribute(java.lang.String)
293 * @see #getAttribute(java.lang.String, java.lang.Object)
294 * @see #setAttribute(java.lang.String, java.lang.Object)
295 */
296 public void clearAttribute( final String name )
297 {
298 if ( name == null )
299 {
300 throw new NullPointerException( "name" );
301 }
302
303 this.attributes.remove( name );
304 }
305
306 /**
307 * Gets the class loader of the context.
308 *
309 * @return The class loader of the context or {@code null}, indicating the bootstrap class loader.
310 *
311 * @see #findClass(java.lang.String)
312 * @see #findResource(java.lang.String)
313 * @see #findResources(java.lang.String)
314 */
315 public ClassLoader getClassLoader()
316 {
317 if ( !this.classLoaderSet )
318 {
319 this.classLoader = this.getClass().getClassLoader();
320 this.classLoaderSet = true;
321 }
322
323 return this.classLoader;
324 }
325
326 /**
327 * Gets the listeners of the context.
328 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
329 * to the returned list will be present inside the object. This is why there is no {@code set} method for the
330 * listeners property.</p>
331 *
332 * @return The list of listeners of the context.
333 *
334 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
335 */
336 public List<Listener> getListeners()
337 {
338 if ( this.listeners == null )
339 {
340 this.listeners = new LinkedList<Listener>();
341 }
342
343 return this.listeners;
344 }
345
346 /**
347 * Gets the default {@code http://jomc.org/modlet} namespace schema system id.
348 * <p>The default {@code http://jomc.org/modlet} namespace schema system id is controlled by system property
349 * {@code org.jomc.modlet.ModelContext.defaultModletSchemaSystemId} holding a system id URI.
350 * If that property is not set, the {@code http://xml.jomc.org/modlet/jomc-modlet-1.8.xsd} default is
351 * returned.</p>
352 *
353 * @return The default system id of the {@code http://jomc.org/modlet} namespace schema.
354 *
355 * @see #setDefaultModletSchemaSystemId(java.lang.String)
356 */
357 public static String getDefaultModletSchemaSystemId()
358 {
359 if ( defaultModletSchemaSystemId == null )
360 {
361 defaultModletSchemaSystemId = System.getProperty(
362 "org.jomc.modlet.ModelContext.defaultModletSchemaSystemId", DEFAULT_MODLET_SCHEMA_SYSTEM_ID );
363
364 }
365
366 return defaultModletSchemaSystemId;
367 }
368
369 /**
370 * Sets the default {@code http://jomc.org/modlet} namespace schema system id.
371 *
372 * @param value The new default {@code http://jomc.org/modlet} namespace schema system id or {@code null}.
373 *
374 * @see #getDefaultModletSchemaSystemId()
375 */
376 public static void setDefaultModletSchemaSystemId( final String value )
377 {
378 defaultModletSchemaSystemId = value;
379 }
380
381 /**
382 * Gets the {@code http://jomc.org/modlet} namespace schema system id of the context.
383 *
384 * @return The {@code http://jomc.org/modlet} namespace schema system id of the context.
385 *
386 * @see #getDefaultModletSchemaSystemId()
387 * @see #setModletSchemaSystemId(java.lang.String)
388 */
389 public final String getModletSchemaSystemId()
390 {
391 if ( this.modletSchemaSystemId == null )
392 {
393 this.modletSchemaSystemId = getDefaultModletSchemaSystemId();
394
395 if ( this.isLoggable( Level.CONFIG ) )
396 {
397 this.log( Level.CONFIG,
398 getMessage( "defaultModletSchemaSystemIdInfo", this.modletSchemaSystemId ), null );
399
400 }
401 }
402
403 return this.modletSchemaSystemId;
404 }
405
406 /**
407 * Sets the {@code http://jomc.org/modlet} namespace schema system id of the context.
408 *
409 * @param value The new {@code http://jomc.org/modlet} namespace schema system id or {@code null}.
410 *
411 * @see #getModletSchemaSystemId()
412 */
413 public final void setModletSchemaSystemId( final String value )
414 {
415 final String oldModletSchemaSystemId = this.getModletSchemaSystemId();
416 this.modletSchemaSystemId = value;
417
418 if ( this.modlets != null )
419 {
420 for ( int i = 0, s0 = this.modlets.getModlet().size(); i < s0; i++ )
421 {
422 final Modlet m = this.modlets.getModlet().get( i );
423
424 if ( m.getSchemas() != null )
425 {
426 final Schema s = m.getSchemas().getSchemaBySystemId( oldModletSchemaSystemId );
427
428 if ( s != null )
429 {
430 s.setSystemId( value );
431 }
432 }
433 }
434 }
435 }
436
437 /**
438 * Gets the default log level events are logged at.
439 * <p>The default log level is controlled by system property
440 * {@code org.jomc.modlet.ModelContext.defaultLogLevel} holding the log level to log events at by default.
441 * If that property is not set, the {@code WARNING} default is returned.</p>
442 *
443 * @return The log level events are logged at by default.
444 *
445 * @see #getLogLevel()
446 * @see Level#parse(java.lang.String)
447 */
448 public static Level getDefaultLogLevel()
449 {
450 if ( defaultLogLevel == null )
451 {
452 defaultLogLevel = Level.parse( System.getProperty(
453 "org.jomc.modlet.ModelContext.defaultLogLevel", DEFAULT_LOG_LEVEL.getName() ) );
454
455 }
456
457 return defaultLogLevel;
458 }
459
460 /**
461 * Sets the default log level events are logged at.
462 *
463 * @param value The new default level events are logged at or {@code null}.
464 *
465 * @see #getDefaultLogLevel()
466 */
467 public static void setDefaultLogLevel( final Level value )
468 {
469 defaultLogLevel = value;
470 }
471
472 /**
473 * Gets the log level of the context.
474 *
475 * @return The log level of the context.
476 *
477 * @see #getDefaultLogLevel()
478 * @see #setLogLevel(java.util.logging.Level)
479 * @see #isLoggable(java.util.logging.Level)
480 */
481 public final Level getLogLevel()
482 {
483 if ( this.logLevel == null )
484 {
485 this.logLevel = getDefaultLogLevel();
486
487 if ( this.isLoggable( Level.CONFIG ) )
488 {
489 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null );
490 }
491 }
492
493 return this.logLevel;
494 }
495
496 /**
497 * Sets the log level of the context.
498 *
499 * @param value The new log level of the context or {@code null}.
500 *
501 * @see #getLogLevel()
502 * @see #isLoggable(java.util.logging.Level)
503 */
504 public final void setLogLevel( final Level value )
505 {
506 this.logLevel = value;
507 }
508
509 /**
510 * Checks if a message at a given level is provided to the listeners of the context.
511 *
512 * @param level The level to test.
513 *
514 * @return {@code true}, if messages at {@code level} are provided to the listeners of the context; {@code false},
515 * if messages at {@code level} are not provided to the listeners of the context.
516 *
517 * @throws NullPointerException if {@code level} is {@code null}.
518 *
519 * @see #getLogLevel()
520 * @see #setLogLevel(java.util.logging.Level)
521 */
522 public boolean isLoggable( final Level level )
523 {
524 if ( level == null )
525 {
526 throw new NullPointerException( "level" );
527 }
528
529 return level.intValue() >= this.getLogLevel().intValue();
530 }
531
532 /**
533 * Notifies all listeners of the context.
534 *
535 * @param level The level of the event.
536 * @param message The message of the event or {@code null}.
537 * @param throwable The throwable of the event {@code null}.
538 *
539 * @throws NullPointerException if {@code level} is {@code null}.
540 *
541 * @see #getListeners()
542 * @see #isLoggable(java.util.logging.Level)
543 */
544 public void log( final Level level, final String message, final Throwable throwable )
545 {
546 if ( level == null )
547 {
548 throw new NullPointerException( "level" );
549 }
550
551 if ( this.isLoggable( level ) )
552 {
553 for ( Listener l : this.getListeners() )
554 {
555 l.onLog( level, message, throwable );
556 }
557 }
558 }
559
560 /**
561 * Gets the {@code Modlets} of the context.
562 * <p>If no {@code Modlets} have been set using the {@code setModlets} method, this method calls the
563 * {@code findModlets} method and the {@code processModlets} method to initialize the {@code Modlets} of the
564 * context.</p>
565 * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
566 * to the returned list will be present inside the object.</p>
567 *
568 * @return The {@code Modlets} of the context.
569 *
570 * @throws ModelException if getting the {@code Modlets} of the context fails.
571 *
572 * @see #setModlets(org.jomc.modlet.Modlets)
573 * @see #findModlets(org.jomc.modlet.Modlets)
574 * @see #processModlets(org.jomc.modlet.Modlets)
575 */
576 public final Modlets getModlets() throws ModelException
577 {
578 try
579 {
580 if ( this.modlets == null )
581 {
582 final Modlet modlet = new Modlet();
583 modlet.setModel( ModletObject.MODEL_PUBLIC_ID );
584 modlet.setName( getMessage( "projectName" ) );
585 modlet.setVendor( getMessage( "projectVendor" ) );
586 modlet.setVersion( getMessage( "projectVersion" ) );
587 modlet.setSchemas( new Schemas() );
588
589 final Schema schema = new Schema();
590 schema.setPublicId( ModletObject.MODEL_PUBLIC_ID );
591 schema.setSystemId( this.getModletSchemaSystemId() );
592 schema.setContextId( ModletObject.class.getPackage().getName() );
593 schema.setClasspathId( ModletObject.class.getPackage().getName().replace( '.', '/' )
594 + "/jomc-modlet-1.8.xsd" );
595
596 modlet.getSchemas().getSchema().add( schema );
597
598 this.modlets = new Modlets();
599 this.modlets.getModlet().add( modlet );
600
601 long t0 = System.currentTimeMillis();
602 final Modlets provided = this.findModlets( this.modlets );
603
604 if ( this.isLoggable( Level.FINE ) )
605 {
606 this.log( Level.FINE, getMessage( "findModletsReport",
607 provided != null ? provided.getModlet().size() : 0,
608 System.currentTimeMillis() - t0 ), null );
609
610 }
611
612 if ( provided != null )
613 {
614 this.modlets = provided;
615 }
616
617 t0 = System.currentTimeMillis();
618 final Modlets processed = this.processModlets( this.modlets );
619
620 if ( this.isLoggable( Level.FINE ) )
621 {
622 this.log( Level.FINE, getMessage( "processModletsReport",
623 processed != null ? processed.getModlet().size() : 0,
624 System.currentTimeMillis() - t0 ), null );
625 }
626
627 if ( processed != null )
628 {
629 this.modlets = processed;
630 }
631
632 t0 = System.currentTimeMillis();
633 final javax.xml.validation.Schema modletSchema = this.createSchema( ModletObject.MODEL_PUBLIC_ID );
634 final Validator validator = modletSchema.newValidator();
635 validator.validate( new JAXBSource( this.createContext( ModletObject.MODEL_PUBLIC_ID ),
636 new ObjectFactory().createModlets( this.modlets ) ) );
637
638 if ( this.isLoggable( Level.FINE ) )
639 {
640 this.log( Level.FINE, getMessage( "validateModletsReport",
641 this.modlets.getModlet().size(),
642 System.currentTimeMillis() - t0 ), null );
643 }
644 }
645
646 return this.modlets;
647 }
648 catch ( final IOException e )
649 {
650 this.modlets = null;
651 throw new ModelException( getMessage( e ), e );
652 }
653 catch ( final JAXBException e )
654 {
655 this.modlets = null;
656 String message = getMessage( e );
657 if ( message == null && e.getLinkedException() != null )
658 {
659 message = getMessage( e.getLinkedException() );
660 }
661
662 throw new ModelException( message, e );
663 }
664 catch ( final SAXException e )
665 {
666 this.modlets = null;
667 String message = getMessage( e );
668 if ( message == null && e.getException() != null )
669 {
670 message = getMessage( e.getException() );
671 }
672
673 throw new ModelException( message, e );
674 }
675 }
676
677 /**
678 * Sets the {@code Modlets} of the context.
679 *
680 * @param value The new {@code Modlets} of the context or {@code null}.
681 *
682 * @see #getModlets()
683 */
684 public final void setModlets( final Modlets value )
685 {
686 this.modlets = value;
687 }
688
689 /**
690 * Searches the context for a class with a given name.
691 *
692 * @param name The name of the class to search.
693 *
694 * @return A class object of the class with name {@code name} or {@code null}, if no such class is found.
695 *
696 * @throws NullPointerException if {@code name} is {@code null}.
697 * @throws ModelException if searching fails.
698 *
699 * @see #getClassLoader()
700 */
701 public Class<?> findClass( final String name ) throws ModelException
702 {
703 if ( name == null )
704 {
705 throw new NullPointerException( "name" );
706 }
707
708 try
709 {
710 return Class.forName( name, false, this.getClassLoader() );
711 }
712 catch ( final ClassNotFoundException e )
713 {
714 if ( this.isLoggable( Level.FINE ) )
715 {
716 this.log( Level.FINE, getMessage( e ), e );
717 }
718
719 return null;
720 }
721 }
722
723 /**
724 * Searches the context for a resource with a given name.
725 *
726 * @param name The name of the resource to search.
727 *
728 * @return An URL object for reading the resource or {@code null}, if no such resource is found.
729 *
730 * @throws NullPointerException if {@code name} is {@code null}.
731 * @throws ModelException if searching fails.
732 *
733 * @see #getClassLoader()
734 */
735 public URL findResource( final String name ) throws ModelException
736 {
737 if ( name == null )
738 {
739 throw new NullPointerException( "name" );
740 }
741
742 final long t0 = System.currentTimeMillis();
743 final URL resource = this.getClassLoader() == null
744 ? ClassLoader.getSystemResource( name )
745 : this.getClassLoader().getResource( name );
746
747 if ( this.isLoggable( Level.FINE ) )
748 {
749 this.log( Level.FINE, getMessage( "resourcesReport", name, System.currentTimeMillis() - t0 ), null );
750 }
751
752 return resource;
753 }
754
755 /**
756 * Searches the context for resources with a given name.
757 *
758 * @param name The name of the resources to search.
759 *
760 * @return An enumeration of URL objects for reading the resources. If no resources are found, the enumeration will
761 * be empty.
762 *
763 * @throws NullPointerException if {@code name} is {@code null}.
764 * @throws ModelException if searching fails.
765 *
766 * @see #getClassLoader()
767 */
768 public Enumeration<URL> findResources( final String name ) throws ModelException
769 {
770 if ( name == null )
771 {
772 throw new NullPointerException( "name" );
773 }
774
775 try
776 {
777 final long t0 = System.currentTimeMillis();
778 final Enumeration<URL> resources = this.getClassLoader() == null
779 ? ClassLoader.getSystemResources( name )
780 : this.getClassLoader().getResources( name );
781
782 if ( this.isLoggable( Level.FINE ) )
783 {
784 this.log( Level.FINE, getMessage( "resourcesReport", name, System.currentTimeMillis() - t0 ), null );
785 }
786
787 return resources;
788 }
789 catch ( final IOException e )
790 {
791 throw new ModelException( getMessage( e ), e );
792 }
793 }
794
795 /**
796 * Searches the context for {@code Modlets}.
797 *
798 * @return The {@code Modlets} found in the context or {@code null}.
799 *
800 * @throws ModelException if searching {@code Modlets} fails.
801 *
802 * @see ModletProvider META-INF/services/org.jomc.modlet.ModletProvider
803 * @see #getModlets()
804 * @deprecated As of JOMC 1.6, replaced by {@link #findModlets(org.jomc.modlet.Modlets)}. This method will be
805 * removed in JOMC 2.0.
806 */
807 @Deprecated
808 public abstract Modlets findModlets() throws ModelException;
809
810 /**
811 * Searches the context for {@code Modlets}.
812 *
813 * @param modlets The {@code Modlets} currently being searched.
814 *
815 * @return The {@code Modlets} found in the context or {@code null}.
816 *
817 * @throws NullPointerException if {@code modlets} is {@code null}.
818 * @throws ModelException if searching {@code Modlets} fails.
819 *
820 * @see ModletProvider META-INF/services/org.jomc.modlet.ModletProvider
821 * @see #getModlets()
822 * @since 1.6
823 */
824 public abstract Modlets findModlets( Modlets modlets ) throws ModelException;
825
826 /**
827 * Processes a list of {@code Modlet}s.
828 *
829 * @param modlets The {@code Modlets} currently being processed.
830 *
831 * @return The processed {@code Modlets} or {@code null}.
832 *
833 * @throws NullPointerException if {@code modlets} is {@code null}.
834 * @throws ModelException if processing {@code Modlets} fails.
835 *
836 * @see ModletProcessor META-INF/services/org.jomc.modlet.ModletProcessor
837 * @see #getModlets()
838 * @since 1.6
839 */
840 public abstract Modlets processModlets( Modlets modlets ) throws ModelException;
841
842 /**
843 * Creates a new {@code Model} instance.
844 *
845 * @param model The identifier of the {@code Model} to create.
846 *
847 * @return A new instance of the {@code Model} identified by {@code model}.
848 *
849 * @throws NullPointerException if {@code model} is {@code null}.
850 * @throws ModelException if creating a new {@code Model} instance fails.
851 *
852 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) createServiceObject( <i>service</i>, ModelProvider.class )
853 * @see ModletObject#MODEL_PUBLIC_ID
854 */
855 public abstract Model findModel( String model ) throws ModelException;
856
857 /**
858 * Populates a given {@code Model} instance.
859 *
860 * @param model The {@code Model} to populate.
861 *
862 * @return The populated model.
863 *
864 * @throws NullPointerException if {@code model} is {@code null}.
865 * @throws ModelException if populating {@code model} fails.
866 *
867 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) createServiceObject( <i>service</i>, ModelProvider.class )
868 *
869 * @since 1.2
870 */
871 public abstract Model findModel( Model model ) throws ModelException;
872
873 /**
874 * Gets the name of the class providing the default {@code ModelContext} implementation.
875 * <p>The name of the class providing the default {@code ModelContext} implementation returned by method
876 * {@link #createModelContext(java.lang.ClassLoader)} is controlled by system property
877 * {@code org.jomc.modlet.ModelContext.className}. If that property is not set, the name of the
878 * {@link org.jomc.modlet.DefaultModelContext} class is returned.</p>
879 *
880 * @return The name of the class providing the default {@code ModelContext} implementation.
881 *
882 * @see #setModelContextClassName(java.lang.String)
883 *
884 * @deprecated As of JOMC 1.2, replaced by class {@link ModelContextFactory}. This method will be removed in version
885 * 2.0.
886 */
887 @Deprecated
888 public static String getModelContextClassName()
889 {
890 if ( modelContextClassName == null )
891 {
892 modelContextClassName = System.getProperty( "org.jomc.modlet.ModelContext.className",
893 DefaultModelContext.class.getName() );
894
895 }
896
897 return modelContextClassName;
898 }
899
900 /**
901 * Sets the name of the class providing the default {@code ModelContext} implementation.
902 *
903 * @param value The new name of the class providing the default {@code ModelContext} implementation or {@code null}.
904 *
905 * @see #getModelContextClassName()
906 *
907 * @deprecated As of JOMC 1.2, replaced by class {@link ModelContextFactory}. This method will be removed in version
908 * 2.0.
909 */
910 @Deprecated
911 public static void setModelContextClassName( final String value )
912 {
913 modelContextClassName = value;
914 }
915
916 /**
917 * Creates a new default {@code ModelContext} instance.
918 *
919 * @param classLoader The class loader to create a new default {@code ModelContext} instance with or {@code null},
920 * to create a new context using the platform's bootstrap class loader.
921 *
922 * @return A new {@code ModelContext} instance.
923 *
924 * @throws ModelException if creating a new {@code ModelContext} instance fails.
925 *
926 * @see #getModelContextClassName()
927 *
928 * @deprecated As of JOMC 1.2, replaced by method {@link ModelContextFactory#newModelContext(java.lang.ClassLoader)}.
929 * This method will be removed in version 2.0.
930 */
931 public static ModelContext createModelContext( final ClassLoader classLoader ) throws ModelException
932 {
933 if ( getModelContextClassName().equals( DefaultModelContext.class.getName() ) )
934 {
935 return new DefaultModelContext( classLoader );
936 }
937
938 try
939 {
940 final Class<?> clazz = Class.forName( getModelContextClassName(), false, classLoader );
941
942 if ( !ModelContext.class.isAssignableFrom( clazz ) )
943 {
944 throw new ModelException( getMessage( "illegalContextImplementation", getModelContextClassName(),
945 ModelContext.class.getName() ) );
946
947 }
948
949 final Constructor<? extends ModelContext> ctor =
950 clazz.asSubclass( ModelContext.class ).getDeclaredConstructor( ClassLoader.class );
951
952 return ctor.newInstance( classLoader );
953 }
954 catch ( final ClassNotFoundException e )
955 {
956 throw new ModelException( getMessage( "contextClassNotFound", getModelContextClassName() ), e );
957 }
958 catch ( final NoSuchMethodException e )
959 {
960 throw new ModelException( getMessage( "contextConstructorNotFound", getModelContextClassName() ), e );
961 }
962 catch ( final InstantiationException e )
963 {
964 final String message = getMessage( e );
965 throw new ModelException( getMessage( "contextInstantiationException", getModelContextClassName(),
966 message != null ? " " + message : "" ), e );
967
968 }
969 catch ( final IllegalAccessException e )
970 {
971 final String message = getMessage( e );
972 throw new ModelException( getMessage( "contextConstructorAccessDenied", getModelContextClassName(),
973 message != null ? " " + message : "" ), e );
974
975 }
976 catch ( final InvocationTargetException e )
977 {
978 String message = getMessage( e );
979 if ( message == null && e.getTargetException() != null )
980 {
981 message = getMessage( e.getTargetException() );
982 }
983
984 throw new ModelException( getMessage( "contextConstructorException", getModelContextClassName(),
985 message != null ? " " + message : "" ), e );
986
987 }
988 }
989
990 /**
991 * Creates a new service object.
992 *
993 * @param <T> The type of the service.
994 * @param service The service to create a new object of.
995 * @param type The class of the type of the service.
996 *
997 * @return An new service object for {@code service}.
998 *
999 * @throws NullPointerException if {@code service} or {@code type} is {@code null}.
1000 * @throws ModelException if creating the service object fails.
1001 *
1002 * @see ModelProvider
1003 * @see ModelProcessor
1004 * @see ModelValidator
1005 *
1006 * @since 1.2
1007 */
1008 public abstract <T> T createServiceObject( final Service service, final Class<T> type ) throws ModelException;
1009
1010 /**
1011 * Creates a new SAX entity resolver instance of a given model.
1012 *
1013 * @param model The identifier of the model to create a new SAX entity resolver of.
1014 *
1015 * @return A new SAX entity resolver instance of the model identified by {@code model}.
1016 *
1017 * @throws NullPointerException if {@code model} is {@code null}.
1018 * @throws ModelException if creating a new SAX entity resolver instance fails.
1019 *
1020 * @see ModletObject#MODEL_PUBLIC_ID
1021 */
1022 public abstract EntityResolver createEntityResolver( String model ) throws ModelException;
1023
1024 /**
1025 * Creates a new SAX entity resolver instance for a given public identifier URI.
1026 *
1027 * @param publicId The public identifier URI to create a new SAX entity resolver for.
1028 *
1029 * @return A new SAX entity resolver instance for the public identifier URI {@code publicId}.
1030 *
1031 * @throws NullPointerException if {@code publicId} is {@code null}.
1032 * @throws ModelException if creating a new SAX entity resolver instance fails.
1033 *
1034 * @see ModletObject#PUBLIC_ID
1035 * @since 1.2
1036 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1037 */
1038 @Deprecated
1039 public abstract EntityResolver createEntityResolver( URI publicId ) throws ModelException;
1040
1041 /**
1042 * Creates a new L/S resource resolver instance of a given model.
1043 *
1044 * @param model The identifier of the model to create a new L/S resource resolver of.
1045 *
1046 * @return A new L/S resource resolver instance of the model identified by {@code model}.
1047 *
1048 * @throws NullPointerException if {@code model} is {@code null}.
1049 * @throws ModelException if creating a new L/S resource resolver instance fails.
1050 *
1051 * @see ModletObject#MODEL_PUBLIC_ID
1052 */
1053 public abstract LSResourceResolver createResourceResolver( String model ) throws ModelException;
1054
1055 /**
1056 * Creates a new L/S resource resolver instance for a given public identifier URI.
1057 *
1058 * @param publicId The public identifier URI to create a new L/S resource resolver for.
1059 *
1060 * @return A new L/S resource resolver instance for the public identifier URI {@code publicId}.
1061 *
1062 * @throws NullPointerException if {@code publicId} is {@code null}.
1063 * @throws ModelException if creating a new L/S resource resolver instance fails.
1064 *
1065 * @see ModletObject#PUBLIC_ID
1066 * @since 1.2
1067 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1068 */
1069 @Deprecated
1070 public abstract LSResourceResolver createResourceResolver( URI publicId ) throws ModelException;
1071
1072 /**
1073 * Creates a new JAXP schema instance of a given model.
1074 *
1075 * @param model The identifier of the model to create a new JAXP schema instance of.
1076 *
1077 * @return A new JAXP schema instance of the model identified by {@code model}.
1078 *
1079 * @throws NullPointerException if {@code model} is {@code null}.
1080 * @throws ModelException if creating a new JAXP schema instance fails.
1081 *
1082 * @see ModletObject#MODEL_PUBLIC_ID
1083 */
1084 public abstract javax.xml.validation.Schema createSchema( String model ) throws ModelException;
1085
1086 /**
1087 * Creates a new JAXP schema instance for a given public identifier URI.
1088 *
1089 * @param publicId The public identifier URI to create a new JAXP schema instance for.
1090 *
1091 * @return A new JAXP schema instance for the public identifier URI {@code publicId}.
1092 *
1093 * @throws NullPointerException if {@code publicId} is {@code null}.
1094 * @throws ModelException if creating a new JAXP schema instance fails.
1095 *
1096 * @see ModletObject#PUBLIC_ID
1097 * @since 1.2
1098 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1099 */
1100 @Deprecated
1101 public abstract javax.xml.validation.Schema createSchema( URI publicId ) throws ModelException;
1102
1103 /**
1104 * Creates a new JAXB context instance of a given model.
1105 *
1106 * @param model The identifier of the model to create a new JAXB context instance of.
1107 *
1108 * @return A new JAXB context instance of the model identified by {@code model}.
1109 *
1110 * @throws NullPointerException if {@code model} is {@code null}.
1111 * @throws ModelException if creating a new JAXB context instance fails.
1112 *
1113 * @see ModletObject#MODEL_PUBLIC_ID
1114 */
1115 public abstract JAXBContext createContext( String model ) throws ModelException;
1116
1117 /**
1118 * Creates a new JAXB context instance for a given public identifier URI.
1119 *
1120 * @param publicId The public identifier URI to create a new JAXB context instance for.
1121 *
1122 * @return A new JAXB context instance for the public identifier URI {@code publicId}.
1123 *
1124 * @throws NullPointerException if {@code publicId} is {@code null}.
1125 * @throws ModelException if creating a new JAXB context instance fails.
1126 *
1127 * @see ModletObject#PUBLIC_ID
1128 * @since 1.2
1129 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1130 */
1131 @Deprecated
1132 public abstract JAXBContext createContext( URI publicId ) throws ModelException;
1133
1134 /**
1135 * Creates a new JAXB marshaller instance of a given model.
1136 *
1137 * @param model The identifier of the model to create a new JAXB marshaller instance of.
1138 *
1139 * @return A new JAXB marshaller instance of the model identified by {@code model}.
1140 *
1141 * @throws NullPointerException if {@code model} is {@code null}.
1142 * @throws ModelException if creating a new JAXB marshaller instance fails.
1143 *
1144 * @see ModletObject#MODEL_PUBLIC_ID
1145 */
1146 public abstract Marshaller createMarshaller( String model ) throws ModelException;
1147
1148 /**
1149 * Creates a new JAXB marshaller instance for a given public identifier URI.
1150 *
1151 * @param publicId The public identifier URI to create a new JAXB marshaller instance for.
1152 *
1153 * @return A new JAXB marshaller instance for the public identifier URI {@code publicId}.
1154 *
1155 * @throws NullPointerException if {@code publicId} is {@code null}.
1156 * @throws ModelException if creating a new JAXB marshaller instance fails.
1157 *
1158 * @see ModletObject#PUBLIC_ID
1159 * @since 1.2
1160 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1161 */
1162 @Deprecated
1163 public abstract Marshaller createMarshaller( URI publicId ) throws ModelException;
1164
1165 /**
1166 * Creates a new JAXB unmarshaller instance of a given model.
1167 *
1168 * @param model The identifier of the model to create a new JAXB unmarshaller instance of.
1169 *
1170 * @return A new JAXB unmarshaller instance of the model identified by {@code model}.
1171 *
1172 * @throws NullPointerException if {@code model} is {@code null}.
1173 * @throws ModelException if creating a new JAXB unmarshaller instance fails.
1174 *
1175 * @see ModletObject#MODEL_PUBLIC_ID
1176 */
1177 public abstract Unmarshaller createUnmarshaller( String model ) throws ModelException;
1178
1179 /**
1180 * Creates a new JAXB unmarshaller instance for a given given public identifier URI.
1181 *
1182 * @param publicId The public identifier URI to create a new JAXB unmarshaller instance for.
1183 *
1184 * @return A new JAXB unmarshaller instance for the public identifier URI {@code publicId}.
1185 *
1186 * @throws NullPointerException if {@code publicId} is {@code null}.
1187 * @throws ModelException if creating a new JAXB unmarshaller instance fails.
1188 *
1189 * @see ModletObject#PUBLIC_ID
1190 * @since 1.2
1191 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0.
1192 */
1193 @Deprecated
1194 public abstract Unmarshaller createUnmarshaller( URI publicId ) throws ModelException;
1195
1196 /**
1197 * Processes a {@code Model}.
1198 *
1199 * @param model The {@code Model} to process.
1200 *
1201 * @return The processed {@code Model}.
1202 *
1203 * @throws NullPointerException if {@code model} is {@code null}.
1204 * @throws ModelException if processing {@code model} fails.
1205 *
1206 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) createServiceObject( <i>service</i>, ModelProcessor.class )
1207 */
1208 public abstract Model processModel( Model model ) throws ModelException;
1209
1210 /**
1211 * Validates a given {@code Model}.
1212 *
1213 * @param model The {@code Model} to validate.
1214 *
1215 * @return Validation report.
1216 *
1217 * @throws NullPointerException if {@code model} is {@code null}.
1218 * @throws ModelException if validating the modules fails.
1219 *
1220 * @see #createServiceObject(org.jomc.modlet.Service, java.lang.Class) createServiceObject( <i>service</i>, ModelValidator.class )
1221 * @see ModelValidationReport#isModelValid()
1222 */
1223 public abstract ModelValidationReport validateModel( Model model ) throws ModelException;
1224
1225 /**
1226 * Validates a given model.
1227 *
1228 * @param model The identifier of the {@code Model} to use for validating {@code source}.
1229 * @param source A source providing the model to validate.
1230 *
1231 * @return Validation report.
1232 *
1233 * @throws NullPointerException if {@code model} or {@code source} is {@code null}.
1234 * @throws ModelException if validating the model fails.
1235 *
1236 * @see #createSchema(java.lang.String)
1237 * @see ModelValidationReport#isModelValid()
1238 * @see ModletObject#MODEL_PUBLIC_ID
1239 */
1240 public abstract ModelValidationReport validateModel( String model, Source source ) throws ModelException;
1241
1242 private static String getMessage( final String key, final Object... args )
1243 {
1244 return MessageFormat.format( ResourceBundle.getBundle(
1245 ModelContext.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
1246
1247 }
1248
1249 private static String getMessage( final Throwable t )
1250 {
1251 return t != null
1252 ? t.getMessage() != null && t.getMessage().trim().length() > 0
1253 ? t.getMessage()
1254 : getMessage( t.getCause() )
1255 : null;
1256
1257 }
1258
1259 }