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