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