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