View Javadoc

1   // SECTION-START[License Header]
2   // <editor-fold defaultstate="collapsed" desc=" Generated License ">
3   /*
4    *   Copyright (c) 2010 The JOMC Project
5    *   Copyright (c) 2005 Christian Schulte <cs@jomc.org>
6    *   All rights reserved.
7    *
8    *   Redistribution and use in source and binary forms, with or without
9    *   modification, are permitted provided that the following conditions
10   *   are met:
11   *
12   *     o Redistributions of source code must retain the above copyright
13   *       notice, this list of conditions and the following disclaimer.
14   *
15   *     o Redistributions in binary form must reproduce the above copyright
16   *       notice, this list of conditions and the following disclaimer in
17   *       the documentation and/or other materials provided with the
18   *       distribution.
19   *
20   *   THIS SOFTWARE IS PROVIDED BY THE JOMC PROJECT AND CONTRIBUTORS "AS IS"
21   *   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22   *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23   *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE JOMC PROJECT OR
24   *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25   *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26   *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27   *   OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28   *   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29   *   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30   *   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31   *
32   *   $Id: DefaultObjectManager.java 1346 2010-01-19 12:16:43Z schulte2005 $
33   *
34   */
35  // </editor-fold>
36  // SECTION-END
37  package org.jomc.ri;
38  
39  import java.io.IOException;
40  import java.lang.reflect.Array;
41  import java.lang.reflect.Method;
42  import java.lang.reflect.Proxy;
43  import java.net.URI;
44  import java.text.MessageFormat;
45  import java.util.ArrayList;
46  import java.util.HashMap;
47  import java.util.HashSet;
48  import java.util.LinkedList;
49  import java.util.List;
50  import java.util.Locale;
51  import java.util.Map;
52  import java.util.ResourceBundle;
53  import java.util.Set;
54  import java.util.logging.Level;
55  import java.util.logging.LogRecord;
56  import org.jomc.ObjectManagementException;
57  import org.jomc.ObjectManager;
58  import org.jomc.ObjectManagerFactory;
59  import org.jomc.model.Dependency;
60  import org.jomc.model.Implementation;
61  import org.jomc.model.ImplementationReference;
62  import org.jomc.model.Implementations;
63  import org.jomc.model.Instance;
64  import org.jomc.model.Message;
65  import org.jomc.model.ModelContext;
66  import org.jomc.model.ModelException;
67  import org.jomc.model.ModelValidationReport;
68  import org.jomc.model.Module;
69  import org.jomc.model.Modules;
70  import org.jomc.model.Multiplicity;
71  import org.jomc.model.Property;
72  import org.jomc.model.Specification;
73  import org.jomc.model.SpecificationReference;
74  import org.jomc.spi.Invocation;
75  import org.jomc.spi.Invoker;
76  import org.jomc.spi.Listener;
77  import org.jomc.spi.Locator;
78  import org.jomc.spi.Scope;
79  import org.jomc.util.WeakIdentityHashMap;
80  
81  // SECTION-START[Documentation]
82  // <editor-fold defaultstate="collapsed" desc=" Generated Documentation ">
83  /**
84   * Object management and configuration reference implementation.
85   * <p><b>Specifications</b><ul>
86   * <li>{@code org.jomc.ObjectManager} {@code 1.0} {@code Singleton}</li>
87   * </ul></p>
88   *
89   * @author <a href="mailto:cs@jomc.org">Christian Schulte</a> 1.0
90   * @version $Id: DefaultObjectManager.java 1346 2010-01-19 12:16:43Z schulte2005 $
91   */
92  // </editor-fold>
93  // SECTION-END
94  // SECTION-START[Annotations]
95  // <editor-fold defaultstate="collapsed" desc=" Generated Annotations ">
96  @javax.annotation.Generated( value = "org.jomc.tools.JavaSources",
97                               comments = "See http://jomc.sourceforge.net/jomc/1.0-alpha-15/jomc-tools" )
98  // </editor-fold>
99  // SECTION-END
100 public class DefaultObjectManager implements ObjectManager
101 {
102     // SECTION-START[Constructors]
103     // <editor-fold defaultstate="collapsed" desc=" Generated Constructors ">
104 
105     /** Creates a new {@code DefaultObjectManager} instance. */
106     @javax.annotation.Generated( value = "org.jomc.tools.JavaSources",
107                                  comments = "See http://jomc.sourceforge.net/jomc/1.0-alpha-15/jomc-tools" )
108     public DefaultObjectManager()
109     {
110         // SECTION-START[Default Constructor]
111         super();
112         // SECTION-END
113     }
114     // </editor-fold>
115     // SECTION-END
116     // SECTION-START[ObjectManager]
117 
118     public Object getObject( final Class specification )
119     {
120         if ( specification == null )
121         {
122             throw new NullPointerException( "specification" );
123         }
124 
125         try
126         {
127             this.initialize();
128 
129             final ClassLoader classLoader = getClassLoader( specification );
130             final Modules model = this.getModules( classLoader );
131             final Specification s = model.getSpecification( specification );
132 
133             if ( s == null )
134             {
135                 if ( this.isLoggable( Level.WARNING ) )
136                 {
137                     this.log( Level.WARNING, this.getMissingSpecificationMessage(
138                         specification.getName() ), new Exception() );
139 
140                 }
141 
142                 return null;
143             }
144 
145             Scope scope = null;
146             if ( s.getScope() != null )
147             {
148                 scope = this.getScope( classLoader, s.getScope() );
149 
150                 if ( scope == null )
151                 {
152                     if ( this.isLoggable( Level.WARNING ) )
153                     {
154                         this.log( Level.WARNING, this.getMissingScopeMessage( s.getScope() ), null );
155                     }
156 
157                     return null;
158                 }
159             }
160 
161             final Implementations available = model.getImplementations( s.getIdentifier() );
162             if ( available == null || available.getImplementation().isEmpty() )
163             {
164                 if ( this.isLoggable( Level.WARNING ) )
165                 {
166                     this.log( Level.WARNING, this.getMissingImplementationsMessage(
167                         specification.getName() ), new Exception() );
168 
169                 }
170 
171                 return null;
172             }
173 
174             if ( s.getMultiplicity() == Multiplicity.ONE )
175             {
176                 final Implementation i = available.getImplementation().get( 0 );
177 
178                 if ( i.getLocation() != null )
179                 {
180                     if ( s.getClazz() == null )
181                     {
182                         if ( this.isLoggable( Level.WARNING ) )
183                         {
184                             this.log( Level.WARNING, this.getMissingSpecificationClassMessage( s ), new Exception() );
185                         }
186 
187                         return null;
188                     }
189 
190                     final Object object = this.getObject(
191                         Class.forName( s.getClazz(), true, classLoader ), i.getLocationUri(), classLoader );
192 
193                     if ( object == null )
194                     {
195                         if ( this.isLoggable( Level.WARNING ) )
196                         {
197                             this.log( Level.WARNING, this.getMissingObjectMessage(
198                                 i.getIdentifier(), i.getName() ), new Exception() );
199 
200                         }
201 
202                         return null;
203                     }
204 
205                     return object;
206                 }
207                 else if ( !i.isAbstract() )
208                 {
209                     final Instance instance = model.getInstance( i.getIdentifier() );
210                     if ( instance == null )
211                     {
212                         if ( this.isLoggable( Level.WARNING ) )
213                         {
214                             this.log( Level.WARNING, this.getMissingInstanceMessage(
215                                 i.getIdentifier(), i.getName() ), new Exception() );
216 
217                         }
218 
219                         return null;
220                     }
221 
222                     final Object object = this.getObject( scope, instance, classLoader );
223                     if ( object == null )
224                     {
225                         if ( this.isLoggable( Level.WARNING ) )
226                         {
227                             this.log( Level.WARNING, this.getMissingObjectMessage(
228                                 i.getIdentifier(), i.getName() ), new Exception() );
229 
230                         }
231 
232                         return null;
233                     }
234 
235                     return object;
236                 }
237             }
238             else if ( s.getMultiplicity() == Multiplicity.MANY )
239             {
240                 final List<Object> list = new ArrayList<Object>( available.getImplementation().size() );
241 
242                 for ( Implementation i : available.getImplementation() )
243                 {
244                     if ( i.getLocation() != null )
245                     {
246                         if ( s.getClazz() == null )
247                         {
248                             if ( this.isLoggable( Level.WARNING ) )
249                             {
250                                 this.log( Level.WARNING, this.getMissingSpecificationClassMessage( s ),
251                                           new Exception() );
252 
253                             }
254 
255                             return null;
256                         }
257 
258                         final Object o = this.getObject(
259                             Class.forName( s.getClazz(), true, classLoader ), i.getLocationUri(), classLoader );
260 
261                         if ( o == null )
262                         {
263                             if ( this.isLoggable( Level.WARNING ) )
264                             {
265                                 this.log( Level.WARNING, this.getMissingObjectMessage(
266                                     i.getIdentifier(), i.getName() ), new Exception() );
267 
268                             }
269                         }
270                         else
271                         {
272                             list.add( o );
273                         }
274                     }
275                     else if ( !i.isAbstract() )
276                     {
277                         final Instance instance = model.getInstance( i.getIdentifier() );
278                         if ( instance == null )
279                         {
280                             if ( this.isLoggable( Level.WARNING ) )
281                             {
282                                 this.log( Level.WARNING, this.getMissingInstanceMessage(
283                                     i.getIdentifier(), i.getName() ), new Exception() );
284 
285                             }
286 
287                             return null;
288                         }
289 
290                         final Object o = this.getObject( scope, instance, classLoader );
291                         if ( o == null )
292                         {
293                             if ( this.isLoggable( Level.WARNING ) )
294                             {
295                                 this.log( Level.WARNING, this.getMissingObjectMessage(
296                                     i.getIdentifier(), i.getName() ), new Exception() );
297 
298                             }
299                         }
300                         else
301                         {
302                             list.add( o );
303                         }
304                     }
305                 }
306 
307                 return list.isEmpty()
308                        ? null : list.toArray( (Object[]) Array.newInstance( specification, list.size() ) );
309 
310             }
311             else if ( this.isLoggable( Level.WARNING ) )
312             {
313                 this.log( Level.WARNING, this.getUnsupportedMultiplicityMessage(
314                     s.getMultiplicity() ), new Exception() );
315 
316             }
317 
318             return null;
319         }
320         catch ( final Exception e )
321         {
322             throw new ObjectManagementException( e.getMessage(), e );
323         }
324     }
325 
326     public Object getObject( final Class specification, final String implementationName )
327     {
328         if ( specification == null )
329         {
330             throw new NullPointerException( "specification" );
331         }
332         if ( implementationName == null )
333         {
334             throw new NullPointerException( "implementationName" );
335         }
336 
337         try
338         {
339             this.initialize();
340 
341             final ClassLoader classLoader = getClassLoader( specification );
342             final Modules model = this.getModules( classLoader );
343             final Specification s = model.getSpecification( specification );
344 
345             if ( s == null )
346             {
347                 if ( this.isLoggable( Level.WARNING ) )
348                 {
349                     this.log( Level.WARNING, this.getMissingSpecificationMessage(
350                         specification.getName() ), new Exception() );
351 
352                 }
353 
354                 return null;
355             }
356 
357             Scope scope = null;
358             if ( s.getScope() != null )
359             {
360                 scope = this.getScope( classLoader, s.getScope() );
361 
362                 if ( scope == null )
363                 {
364                     if ( this.isLoggable( Level.WARNING ) )
365                     {
366                         this.log( Level.WARNING, this.getMissingScopeMessage( s.getScope() ), null );
367                     }
368 
369                     return null;
370                 }
371             }
372 
373             final Implementations available = model.getImplementations( s.getIdentifier() );
374             if ( available == null || available.getImplementation().isEmpty() )
375             {
376                 if ( this.isLoggable( Level.WARNING ) )
377                 {
378                     this.log( Level.WARNING, this.getMissingImplementationsMessage(
379                         specification.getName() ), new Exception() );
380 
381                 }
382 
383                 return null;
384             }
385 
386             final Implementation i = available.getImplementationByName( implementationName );
387             if ( i == null )
388             {
389                 if ( this.isLoggable( Level.WARNING ) )
390                 {
391                     this.log( Level.WARNING, this.getMissingImplementationMessage(
392                         implementationName, s.getIdentifier() ), new Exception() );
393 
394                 }
395 
396                 return null;
397             }
398 
399             if ( i.getLocation() != null )
400             {
401                 if ( s.getClazz() == null )
402                 {
403                     if ( this.isLoggable( Level.WARNING ) )
404                     {
405                         this.log( Level.WARNING, this.getMissingSpecificationClassMessage( s ), new Exception() );
406                     }
407 
408                     return null;
409                 }
410 
411                 final Object object = this.getObject(
412                     Class.forName( s.getClazz(), true, classLoader ), i.getLocationUri(), classLoader );
413 
414                 if ( object == null )
415                 {
416                     if ( this.isLoggable( Level.WARNING ) )
417                     {
418                         this.log( Level.WARNING, this.getMissingObjectMessage(
419                             i.getIdentifier(), i.getName() ), new Exception() );
420 
421                     }
422 
423                     return null;
424                 }
425 
426                 return object;
427             }
428             else if ( !i.isAbstract() )
429             {
430                 final Instance instance = model.getInstance( i.getIdentifier() );
431                 if ( instance == null )
432                 {
433                     if ( this.isLoggable( Level.WARNING ) )
434                     {
435                         this.log( Level.WARNING, this.getMissingInstanceMessage(
436                             i.getIdentifier(), i.getName() ), new Exception() );
437 
438                     }
439 
440                     return null;
441                 }
442 
443                 final Object object = this.getObject( scope, instance, classLoader );
444                 if ( object == null )
445                 {
446                     if ( this.isLoggable( Level.WARNING ) )
447                     {
448                         this.log( Level.WARNING, this.getMissingObjectMessage(
449                             i.getIdentifier(), i.getName() ), new Exception() );
450 
451                     }
452 
453                     return null;
454                 }
455 
456                 return object;
457             }
458 
459             return null;
460         }
461         catch ( final Exception e )
462         {
463             throw new ObjectManagementException( e.getMessage(), e );
464         }
465     }
466 
467     public Object getDependency( final Object object, final String dependencyName )
468     {
469         if ( object == null )
470         {
471             throw new NullPointerException( "object" );
472         }
473         if ( dependencyName == null )
474         {
475             throw new NullPointerException( "dependencyName" );
476         }
477 
478         try
479         {
480             this.initialize();
481 
482             final ClassLoader classLoader = getClassLoader( object.getClass() );
483             final Modules model = this.getModules( classLoader );
484             final Instance instance = model.getInstance( object );
485 
486             if ( instance == null )
487             {
488                 if ( this.isLoggable( Level.WARNING ) )
489                 {
490                     this.log( Level.WARNING, this.getMissingObjectInstanceMessage( object ), new Exception() );
491                 }
492 
493                 return null;
494             }
495 
496             synchronized ( instance )
497             {
498                 final Dependency dependency = instance.getDependencies() != null
499                                               ? instance.getDependencies().getDependency( dependencyName ) : null;
500 
501                 if ( dependency == null )
502                 {
503                     if ( this.isLoggable( Level.WARNING ) )
504                     {
505                         this.log( Level.WARNING, this.getMissingDependencyMessage(
506                             dependencyName, instance.getIdentifier() ), new Exception() );
507 
508                     }
509 
510                     return null;
511                 }
512 
513                 Object o = instance.getDependencyObjects().get( dependencyName );
514                 if ( o == null )
515                 {
516                     final Specification ds = model.getSpecification( dependency.getIdentifier() );
517                     if ( ds == null )
518                     {
519                         if ( this.isLoggable( Level.WARNING ) )
520                         {
521                             this.log( Level.WARNING, this.getMissingSpecificationMessage(
522                                 dependency.getIdentifier() ), new Exception() );
523 
524                         }
525 
526                         return null;
527                     }
528 
529                     Scope scope = null;
530                     if ( ds.getScope() != null )
531                     {
532                         scope = this.getScope( classLoader, ds.getScope() );
533 
534                         if ( scope == null )
535                         {
536                             if ( this.isLoggable( Level.WARNING ) )
537                             {
538                                 this.log( Level.WARNING, this.getMissingScopeMessage( ds.getScope() ), null );
539                             }
540 
541                             return null;
542                         }
543                     }
544 
545                     final Implementations available = model.getImplementations( ds.getIdentifier() );
546                     if ( available == null || available.getImplementation().isEmpty() )
547                     {
548                         if ( !dependency.isOptional() && this.isLoggable( Level.WARNING ) )
549                         {
550                             this.log( Level.WARNING, this.getMissingImplementationsMessage(
551                                 dependency.getIdentifier() ), new Exception() );
552 
553                         }
554 
555                         return null;
556                     }
557 
558                     if ( dependency.getImplementationName() != null )
559                     {
560                         final Implementation i =
561                             available.getImplementationByName( dependency.getImplementationName() );
562 
563                         if ( i == null )
564                         {
565                             if ( !dependency.isOptional() && this.isLoggable( Level.WARNING ) )
566                             {
567                                 this.log( Level.WARNING, this.getMissingImplementationMessage(
568                                     dependency.getImplementationName(), dependency.getIdentifier() ), new Exception() );
569 
570                             }
571 
572                             return null;
573                         }
574 
575                         if ( i.getLocation() != null )
576                         {
577                             if ( ds.getClazz() == null )
578                             {
579                                 if ( this.isLoggable( Level.WARNING ) )
580                                 {
581                                     this.log( Level.WARNING, this.getMissingSpecificationClassMessage( ds ),
582                                               new Exception() );
583 
584                                 }
585 
586                                 return null;
587                             }
588 
589                             o = this.getObject(
590                                 Class.forName( ds.getClazz(), true, classLoader ), i.getLocationUri(), classLoader );
591 
592                             if ( o == null )
593                             {
594                                 if ( this.isLoggable( Level.WARNING ) )
595                                 {
596                                     this.log( Level.WARNING, this.getMissingObjectMessage(
597                                         i.getIdentifier(), i.getName() ), new Exception() );
598 
599                                 }
600 
601                                 return null;
602                             }
603                         }
604                         else if ( !i.isAbstract() )
605                         {
606                             final Instance di = model.getInstance( i.getIdentifier(), dependency );
607                             if ( di == null )
608                             {
609                                 if ( this.isLoggable( Level.WARNING ) )
610                                 {
611                                     this.log( Level.WARNING, this.getMissingInstanceMessage(
612                                         i.getIdentifier(), i.getName() ), new Exception() );
613 
614                                 }
615 
616                                 return null;
617                             }
618 
619                             o = this.getObject( scope, di, classLoader );
620                             if ( o == null )
621                             {
622                                 if ( this.isLoggable( Level.WARNING ) )
623                                 {
624                                     this.log( Level.WARNING, this.getMissingObjectMessage(
625                                         i.getIdentifier(), i.getName() ), new Exception() );
626 
627                                 }
628 
629                                 return null;
630                             }
631                         }
632                     }
633                     else if ( ds.getMultiplicity() == Multiplicity.ONE )
634                     {
635                         final Implementation ref = available.getImplementation().get( 0 );
636                         if ( ref.getLocation() != null )
637                         {
638                             if ( ds.getClazz() == null )
639                             {
640                                 if ( this.isLoggable( Level.WARNING ) )
641                                 {
642                                     this.log( Level.WARNING, this.getMissingSpecificationClassMessage( ds ),
643                                               new Exception() );
644                                 }
645 
646                                 return null;
647                             }
648 
649                             o = this.getObject(
650                                 Class.forName( ds.getClazz(), true, classLoader ), ref.getLocationUri(), classLoader );
651 
652                             if ( o == null )
653                             {
654                                 if ( this.isLoggable( Level.WARNING ) )
655                                 {
656                                     this.log( Level.WARNING, this.getMissingObjectMessage(
657                                         ref.getIdentifier(), ref.getName() ), new Exception() );
658 
659                                 }
660 
661                                 return null;
662                             }
663                         }
664                         else if ( !ref.isAbstract() )
665                         {
666                             final Instance di = model.getInstance( ref.getIdentifier(), dependency );
667                             if ( di == null )
668                             {
669                                 if ( this.isLoggable( Level.WARNING ) )
670                                 {
671                                     this.log( Level.WARNING, this.getMissingInstanceMessage(
672                                         ref.getIdentifier(), ref.getName() ), new Exception() );
673 
674                                 }
675 
676                                 return null;
677                             }
678 
679                             o = this.getObject( scope, di, classLoader );
680                             if ( o == null )
681                             {
682                                 if ( this.isLoggable( Level.WARNING ) )
683                                 {
684                                     this.log( Level.WARNING, this.getMissingObjectMessage(
685                                         ref.getIdentifier(), ref.getName() ), new Exception() );
686 
687                                 }
688 
689                                 return null;
690                             }
691                         }
692                     }
693                     else
694                     {
695                         final List<Object> list = new ArrayList<Object>( available.getImplementation().size() );
696 
697                         if ( !available.getImplementation().isEmpty() && ds.getClazz() == null )
698                         {
699                             if ( this.isLoggable( Level.WARNING ) )
700                             {
701                                 this.log( Level.WARNING, this.getMissingSpecificationClassMessage( ds ),
702                                           new Exception() );
703 
704                             }
705 
706                             return null;
707                         }
708 
709                         for ( Implementation a : available.getImplementation() )
710                         {
711                             if ( a.getLocation() != null )
712                             {
713                                 final Object o2 = this.getObject( Class.forName( ds.getClazz(), true, classLoader ),
714                                                                   a.getLocationUri(), classLoader );
715 
716                                 if ( o2 == null )
717                                 {
718                                     if ( this.isLoggable( Level.WARNING ) )
719                                     {
720                                         this.log( Level.WARNING, this.getMissingObjectMessage(
721                                             a.getIdentifier(), a.getName() ), new Exception() );
722 
723                                     }
724                                 }
725                                 else
726                                 {
727                                     list.add( o2 );
728                                 }
729                             }
730                             else if ( !a.isAbstract() )
731                             {
732                                 final Instance di = model.getInstance( a.getIdentifier(), dependency );
733                                 if ( di == null )
734                                 {
735                                     if ( this.isLoggable( Level.WARNING ) )
736                                     {
737                                         this.log( Level.WARNING, this.getMissingInstanceMessage(
738                                             a.getIdentifier(), a.getName() ), new Exception() );
739 
740                                     }
741 
742                                     return null;
743                                 }
744 
745                                 final Object o2 = this.getObject( scope, di, classLoader );
746                                 if ( o2 == null )
747                                 {
748                                     if ( this.isLoggable( Level.WARNING ) )
749                                     {
750                                         this.log( Level.WARNING, this.getMissingObjectMessage(
751                                             a.getIdentifier(), a.getName() ), new Exception() );
752 
753                                     }
754                                 }
755                                 else
756                                 {
757                                     list.add( o2 );
758                                 }
759                             }
760                         }
761 
762                         o = list.isEmpty() ? null : list.toArray( (Object[]) Array.newInstance( Class.forName(
763                             ds.getClazz(), true, classLoader ), list.size() ) );
764 
765                     }
766                 }
767 
768                 if ( o != null && dependency.isBound() )
769                 {
770                     instance.getDependencyObjects().put( dependencyName, o );
771                 }
772 
773                 return o;
774             }
775         }
776         catch ( final Exception e )
777         {
778             throw new ObjectManagementException( e.getMessage(), e );
779         }
780     }
781 
782     public Object getProperty( final Object object, final String propertyName )
783     {
784         if ( object == null )
785         {
786             throw new NullPointerException( "object" );
787         }
788         if ( propertyName == null )
789         {
790             throw new NullPointerException( "propertyName" );
791         }
792 
793         try
794         {
795             this.initialize();
796 
797             final ClassLoader classLoader = getClassLoader( object.getClass() );
798             final Modules model = this.getModules( classLoader );
799             final Instance instance = model.getInstance( object );
800 
801             if ( instance == null )
802             {
803                 if ( this.isLoggable( Level.WARNING ) )
804                 {
805                     this.log( Level.WARNING, this.getMissingObjectInstanceMessage( object ), new Exception() );
806                 }
807 
808                 return null;
809             }
810 
811             synchronized ( instance )
812             {
813                 Object value = instance.getPropertyObjects().get( propertyName );
814                 if ( value == null )
815                 {
816                     final Property property =
817                         instance.getProperties() != null ? instance.getProperties().getProperty( propertyName ) : null;
818 
819                     if ( property == null )
820                     {
821                         if ( this.isLoggable( Level.WARNING ) )
822                         {
823                             this.log( Level.WARNING, this.getMissingPropertyMessage(
824                                 propertyName, object.getClass().getName() ), new Exception() );
825 
826                         }
827 
828                         return null;
829                     }
830 
831                     value = property.getJavaValue( classLoader );
832                     if ( value != null )
833                     {
834                         instance.getPropertyObjects().put( propertyName, value );
835                     }
836                 }
837 
838                 return value;
839             }
840         }
841         catch ( final Exception e )
842         {
843             throw new ObjectManagementException( e.getMessage(), e );
844         }
845     }
846 
847     public String getMessage( final Object object, final String messageName, final Locale locale,
848                               final Object... arguments )
849     {
850         if ( object == null )
851         {
852             throw new NullPointerException( "object" );
853         }
854         if ( messageName == null )
855         {
856             throw new NullPointerException( "messageName" );
857         }
858         if ( locale == null )
859         {
860             throw new NullPointerException( "locale" );
861         }
862 
863         try
864         {
865             this.initialize();
866 
867             final ClassLoader classLoader = getClassLoader( object.getClass() );
868             final Modules model = this.getModules( classLoader );
869             final Instance instance = model.getInstance( object );
870 
871             if ( instance == null )
872             {
873                 if ( this.isLoggable( Level.WARNING ) )
874                 {
875                     this.log( Level.WARNING, this.getMissingObjectInstanceMessage( object ), new Exception() );
876                 }
877 
878                 return null;
879             }
880 
881             synchronized ( instance )
882             {
883                 final Message message =
884                     instance.getMessages() != null ? instance.getMessages().getMessage( messageName ) : null;
885 
886                 if ( message == null || message.getTemplate() == null )
887                 {
888                     if ( this.isLoggable( Level.WARNING ) )
889                     {
890                         this.log( Level.WARNING, this.getMissingMessageMessage(
891                             messageName, object.getClass().getName() ), new Exception() );
892 
893                     }
894 
895                     return null;
896                 }
897 
898                 return MessageFormat.format( message.getTemplate().getText(
899                     locale.getLanguage().toLowerCase( Locale.ENGLISH ) ).getValue(), arguments );
900 
901             }
902         }
903         catch ( final Exception e )
904         {
905             throw new ObjectManagementException( e.getMessage(), e );
906         }
907     }
908 
909     // SECTION-END
910     // SECTION-START[DefaultObjectManager]
911     /** Constant for the {@code Singleton} scope identifier. */
912     protected static final String SINGLETON_SCOPE_IDENTIFIER = "Singleton";
913 
914     /**
915      * Log level events are logged at by default.
916      * @see #getDefaultLogLevel()
917      */
918     private static final Level DEFAULT_LOG_LEVEL = Level.WARNING;
919 
920     /** Default log level. */
921     private static volatile Level defaultLogLevel;
922 
923     /** Name of the platform's bootstrap class loader class. */
924     private static volatile String bootstrapClassLoaderClassName;
925 
926     private static volatile boolean bootstrapClassLoaderClassNameInitialized;
927 
928     /** {@code ClassLoader} instance representing the bootstrap class loader. */
929     private static final ClassLoader BOOTSTRAP_CLASSLOADER = new ClassLoader( null )
930     {
931 
932         @Override
933         public String toString()
934         {
935             return DefaultObjectManager.class.getName() + ".BootstrapClassLoader@" + System.identityHashCode( this );
936         }
937 
938     };
939 
940     /** Listeners of the instance. */
941     private List<Listener> listeners;
942 
943     /** Flag indicating that initialization has been performed. */
944     private boolean initialized;
945 
946     /** Log level of the instance. */
947     private Level logLevel;
948 
949     /** Modules of the instance. */
950     private final Map<ClassLoader, Modules> modules = new WeakIdentityHashMap();
951 
952     /** Invokers of the instance. */
953     private final Map<ClassLoader, Invoker> invokers = new WeakIdentityHashMap();
954 
955     /** Scopes of the instance. */
956     private final Map<ClassLoader, Map<String, Scope>> scopes = new WeakIdentityHashMap();
957 
958     /** Locators of the instance. */
959     private final Map<ClassLoader, Map<String, Locator>> locators = new WeakIdentityHashMap();
960 
961     /** Objects of the instance. */
962     private final Map<ClassLoader, Map<Object, Instance>> objects = new WeakIdentityHashMap();
963 
964     /** {@code ObjectManager} singletons. */
965     private static final Map<ClassLoader, ObjectManager> singletons = new WeakIdentityHashMap();
966 
967     /**
968      * Default {@link ObjectManagerFactory#getObjectManager(ClassLoader)} implementation.
969      *
970      * @param classLoader The class loader to use for getting the singleton instance; {@code null} to use the platform's
971      * bootstrap class loader.
972      *
973      * @return The default {@code ObjectManager} singleton instance.
974      *
975      * @see ObjectManagerFactory#getObjectManager(ClassLoader)
976      */
977     public static ObjectManager getObjectManager( final ClassLoader classLoader )
978     {
979         synchronized ( singletons )
980         {
981             final ClassLoader singletonsLoader = getClassLoader( classLoader );
982             ObjectManager manager = singletons.get( singletonsLoader );
983             if ( manager == null )
984             {
985                 manager = ObjectManagerFactory.newObjectManager( classLoader );
986                 singletons.put( singletonsLoader, manager );
987             }
988 
989             return (ObjectManager) manager.getObject( ObjectManager.class );
990         }
991     }
992 
993     /**
994      * Gets the list of registered listeners.
995      * <p>This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
996      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
997      * listeners property.</p>
998      *
999      * @return The list of registered listeners.
1000      */
1001     public List<Listener> getListeners()
1002     {
1003         if ( this.listeners == null )
1004         {
1005             this.listeners = new LinkedList<Listener>();
1006         }
1007 
1008         return this.listeners;
1009     }
1010 
1011     /**
1012      * Gets the default log level events are logged at.
1013      * <p>The default log level is controlled by system property
1014      * {@code org.jomc.ri.DefaultObjectManager.defaultLogLevel} holding the log level to log events at by default.
1015      * If that property is not set, the {@code WARNING} default is returned.</p>
1016      *
1017      * @return The log level events are logged at by default.
1018      *
1019      * @see #getLogLevel()
1020      * @see Level#parse(java.lang.String)
1021      */
1022     public static Level getDefaultLogLevel()
1023     {
1024         if ( defaultLogLevel == null )
1025         {
1026             defaultLogLevel = Level.parse( System.getProperty( "org.jomc.ri.DefaultObjectManager.defaultLogLevel",
1027                                                                DEFAULT_LOG_LEVEL.getName() ) );
1028 
1029         }
1030 
1031         return defaultLogLevel;
1032     }
1033 
1034     /**
1035      * Sets the default log level events are logged at.
1036      *
1037      * @param value The new default level events are logged at or {@code null}.
1038      *
1039      * @see #getDefaultLogLevel()
1040      */
1041     public static void setDefaultLogLevel( final Level value )
1042     {
1043         defaultLogLevel = value;
1044     }
1045 
1046     /**
1047      * Gets the log level of the instance.
1048      *
1049      * @return The log level of the instance.
1050      *
1051      * @see #getDefaultLogLevel()
1052      * @see #setLogLevel(java.util.logging.Level)
1053      * @see #isLoggable(java.util.logging.Level)
1054      */
1055     public Level getLogLevel()
1056     {
1057         if ( this.logLevel == null )
1058         {
1059             this.logLevel = getDefaultLogLevel();
1060             this.log( Level.CONFIG, this.getMessage( "defaultLogLevelInfo", new Object[]
1061                 {
1062                     this.getClass().getCanonicalName(), this.logLevel.getLocalizedName()
1063                 } ), null );
1064 
1065         }
1066 
1067         return this.logLevel;
1068     }
1069 
1070     /**
1071      * Sets the log level of the instance.
1072      *
1073      * @param value The new log level of the instance or {@code null}.
1074      *
1075      * @see #getLogLevel()
1076      * @see #isLoggable(java.util.logging.Level)
1077      */
1078     public void setLogLevel( final Level value )
1079     {
1080         this.logLevel = value;
1081     }
1082 
1083     /**
1084      * Checks if a message at a given level is provided to the listeners of the instance.
1085      *
1086      * @param level The level to test.
1087      *
1088      * @return {@code true} if messages at {@code level} are provided to the listeners of the instance;
1089      * {@code false} if messages at {@code level} are not provided to the listeners of the instance.
1090      *
1091      * @throws NullPointerException if {@code level} is {@code null}.
1092      *
1093      * @see #getLogLevel()
1094      * @see #setLogLevel(java.util.logging.Level)
1095      * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
1096      */
1097     public boolean isLoggable( final Level level )
1098     {
1099         if ( level == null )
1100         {
1101             throw new NullPointerException( "level" );
1102         }
1103 
1104         return level.intValue() >= this.getLogLevel().intValue();
1105     }
1106 
1107     /**
1108      * Gets the name of the platform's bootstrap class loader class.
1109      * <p>The name of the platform's bootstrap class loader class is controlled by system property
1110      * {@code org.jomc.ri.DefaultObjectManager.bootstrapClassLoaderClassName} holding the name of the platform's
1111      * bootstrap class loader class. If that property is not set, the bootstrap class loader is assumed to be
1112      * represented by a {@code null} parent class loader.</p>
1113      *
1114      * @return The name of the platform's bootstrap class loader class or {@code null}.
1115      *
1116      * @see #getClassLoader(java.lang.ClassLoader)
1117      */
1118     public static String getBootstrapClassLoaderClassName()
1119     {
1120         if ( bootstrapClassLoaderClassName == null && !bootstrapClassLoaderClassNameInitialized )
1121         {
1122             bootstrapClassLoaderClassName =
1123                 System.getProperty( "org.jomc.ri.DefaultObjectManager.bootstrapClassLoaderClassName" );
1124 
1125             bootstrapClassLoaderClassNameInitialized = true;
1126         }
1127 
1128         return bootstrapClassLoaderClassName;
1129     }
1130 
1131     /**
1132      * Sets the name of the platform's bootstrap class loader class.
1133      *
1134      * @param value The new name of the platform's bootstrap class loader class or {@code null}.
1135      *
1136      * @see #getBootstrapClassLoaderClassName()
1137      */
1138     public static void setBootstrapClassLoaderClassName( final String value )
1139     {
1140         bootstrapClassLoaderClassName = value;
1141         bootstrapClassLoaderClassNameInitialized = false;
1142     }
1143 
1144     /**
1145      * Gets the modules of a given class loader.
1146      *
1147      * @param classLoader The class loader to get the modules of.
1148      *
1149      * @return The modules of the given class loader.
1150      *
1151      * @throws NullPointerException if {@code classLoader} is {@code null},
1152      */
1153     public Modules getModules( final ClassLoader classLoader )
1154     {
1155         if ( classLoader == null )
1156         {
1157             throw new NullPointerException( "classLoader" );
1158         }
1159 
1160         synchronized ( this.modules )
1161         {
1162             Modules cachedModules = this.modules.get( classLoader );
1163 
1164             if ( cachedModules == null )
1165             {
1166                 try
1167                 {
1168                     final ModelContext modelContext = ModelContext.createModelContext( classLoader );
1169                     modelContext.setLogLevel( this.getLogLevel() );
1170                     modelContext.getListeners().add( new ModelContext.Listener()
1171                     {
1172 
1173                         public void onLog( final Level level, final String message, final Throwable t )
1174                         {
1175                             log( level, message, t );
1176                         }
1177 
1178                     } );
1179 
1180                     cachedModules = modelContext.findModules();
1181                     Module classpathModule =
1182                         cachedModules.getClasspathModule( Modules.getDefaultClasspathModuleName(), classLoader );
1183 
1184                     if ( classpathModule != null )
1185                     {
1186                         cachedModules.getModule().add( classpathModule );
1187                     }
1188 
1189                     cachedModules = modelContext.processModules( cachedModules );
1190 
1191                     final ModelValidationReport validationReport = modelContext.validateModel( cachedModules );
1192 
1193                     for ( ModelValidationReport.Detail d : validationReport.getDetails() )
1194                     {
1195                         this.log( d.getLevel(), d.getMessage(), null );
1196                     }
1197 
1198                     if ( validationReport.isModelValid() )
1199                     {
1200                         final ClassLoader objectsLoader = getClassLoader( classLoader );
1201                         Map<Object, Instance> objectMap = this.objects.get( objectsLoader );
1202                         if ( objectMap == null )
1203                         {
1204                             objectMap = new WeakIdentityHashMap();
1205                             this.objects.put( objectsLoader, objectMap );
1206                         }
1207 
1208                         this.modules.put( classLoader, new Modules( cachedModules, objectMap ) );
1209 
1210                         if ( this.isLoggable( Level.FINEST ) )
1211                         {
1212                             this.logModulesReport( cachedModules, classLoader );
1213                         }
1214                     }
1215                     else
1216                     {
1217                         cachedModules = null;
1218                     }
1219                 }
1220                 catch ( final ModelException e )
1221                 {
1222                     if ( this.isLoggable( Level.SEVERE ) )
1223                     {
1224                         this.log( Level.SEVERE, e.getMessage(), e );
1225                     }
1226 
1227                     cachedModules = null;
1228                 }
1229                 finally
1230                 {
1231                     if ( cachedModules == null )
1232                     {
1233                         cachedModules = new Modules();
1234                     }
1235                 }
1236             }
1237 
1238             return cachedModules;
1239         }
1240     }
1241 
1242     /**
1243      * Gets the class loader of a given class.
1244      *
1245      * @param clazz The class whose class loader to return.
1246      *
1247      * @return The class loader of {@code clazz}.
1248      *
1249      * @throws NullPointerException if {@code clazz} is {@code null}.
1250      */
1251     public static ClassLoader getClassLoader( final Class clazz )
1252     {
1253         if ( clazz == null )
1254         {
1255             throw new NullPointerException( "clazz" );
1256         }
1257 
1258         ClassLoader cl = clazz.getClassLoader();
1259         if ( cl == null )
1260         {
1261             cl = BOOTSTRAP_CLASSLOADER;
1262         }
1263 
1264         return cl;
1265     }
1266 
1267     /**
1268      * Gets the parent class loader of a given class loader recursively.
1269      * <p>This method recursively finds the parent class loader of the given class loader. Recursion stops at the
1270      * platform's bootstrap class loader. That class loader is detected when either the current class loader has no
1271      * parent (a call to the {@code getParent()} method returns {@code null}) or when the class name of the
1272      * current class loader's parent class loader is equal to the name returned by method
1273      * {@code getBootstrapClassLoaderClassName()}. Configuration of the name of the platform's bootstrap class loader
1274      * class is needed when the platform's {@code getParent()} method of the {@code ClassLoader} class does not return
1275      * {@code null} to indicate the bootstrap class loader but instead returns an instance of {@code ClassLoader}.</p>
1276      *
1277      * @param classLoader The class loader whose parent class loader to return or {@code null} to return a
1278      * {@code ClassLoader} instance representing the platform's bootstrap class loader.
1279      *
1280      * @return The parent class loader of {@code classLoader}.
1281      *
1282      * @throws NullPointerException if {@code classLoader} is {@code null}.
1283      *
1284      * @see #getBootstrapClassLoaderClassName()
1285      * @see ClassLoader#getParent()
1286      */
1287     public static ClassLoader getClassLoader( final ClassLoader classLoader )
1288     {
1289         if ( classLoader == null )
1290         {
1291             return BOOTSTRAP_CLASSLOADER;
1292         }
1293 
1294         if ( classLoader.getParent() != null &&
1295              !classLoader.getParent().getClass().getName().equals( getBootstrapClassLoaderClassName() ) )
1296         {
1297             return getClassLoader( classLoader.getParent() );
1298         }
1299 
1300         return classLoader;
1301     }
1302 
1303     /**
1304      * Gets an object of a given instance from a given scope.
1305      *
1306      * @param scope The scope to get the object from or {@code null}.
1307      * @param instance The instance of the object to get.
1308      * @param classLoader The class loader to use for creating the object.
1309      *
1310      * @return An object of {@code instance} from {@code scope} or {@code null} if no such object is found.
1311      *
1312      * @throws NullPointerException if {@code instance} or {@code classLoader} is {@code null}.
1313      * @throws InstantiationException if creating an object fails.
1314      */
1315     public Object getObject( final Scope scope, final Instance instance, final ClassLoader classLoader )
1316         throws InstantiationException
1317     {
1318         if ( instance == null )
1319         {
1320             throw new NullPointerException( "instance" );
1321         }
1322         if ( classLoader == null )
1323         {
1324             throw new NullPointerException( "classLoader" );
1325         }
1326 
1327         Object object = null;
1328         final Modules model = this.getModules( classLoader );
1329 
1330         if ( scope != null )
1331         {
1332             synchronized ( scope )
1333             {
1334                 object = scope.getObject( instance.getIdentifier() );
1335 
1336                 if ( object == null )
1337                 {
1338                     scope.putObject( instance.getIdentifier(), instance );
1339 
1340                     try
1341                     {
1342                         object = model.createObject( instance, classLoader );
1343                     }
1344                     finally
1345                     {
1346                         if ( object != null )
1347                         {
1348                             object = this.createProxy( instance, object );
1349                         }
1350 
1351                         scope.putObject( instance.getIdentifier(), object );
1352                     }
1353                 }
1354                 else if ( object instanceof Instance )
1355                 {
1356                     throw new ObjectManagementException( this.getDependencyCycleMessage(
1357                         ( (Instance) object ).getIdentifier() ) );
1358 
1359                 }
1360             }
1361         }
1362         else
1363         {
1364             try
1365             {
1366                 object = model.createObject( instance, classLoader );
1367             }
1368             finally
1369             {
1370                 if ( object != null )
1371                 {
1372                     object = this.createProxy( instance, object );
1373                 }
1374             }
1375         }
1376 
1377         return object;
1378     }
1379 
1380     /**
1381      * Gets an object for a given location URI.
1382      *
1383      * @param specification The specification class of the object to locate.
1384      * @param location The location URI of the object to locate.
1385      * @param classLoader The class loader to use for loading locator classes.
1386      * @param <T> The type of the object.
1387      *
1388      * @return An object located at {@code location} or {@code null} if no such object is found.
1389      *
1390      * @throws NullPointerException if {@code specification}, {@code location} or {@code classLoader} is {@code null}.
1391      * @throws InstantiationException if instantiating a locator fails.
1392      * @throws ClassNotFoundException if the class of {@code specification} is not found.
1393      * @throws IOException if locating the object fails.
1394      */
1395     public <T> T getObject( final Class<T> specification, final URI location, final ClassLoader classLoader )
1396         throws InstantiationException, ClassNotFoundException, IOException
1397     {
1398         if ( specification == null )
1399         {
1400             throw new NullPointerException( "specification" );
1401         }
1402         if ( location == null )
1403         {
1404             throw new NullPointerException( "location" );
1405         }
1406         if ( classLoader == null )
1407         {
1408             throw new NullPointerException( "classLoader" );
1409         }
1410 
1411         T object = null;
1412         final Locator locator = this.getLocator( classLoader, location );
1413 
1414         if ( locator != null )
1415         {
1416             object = locator.getObject( specification, location );
1417         }
1418         else if ( this.isLoggable( Level.WARNING ) )
1419         {
1420             this.log( Level.WARNING, this.getMissingLocatorMessage( location ), new Exception() );
1421         }
1422 
1423         return object;
1424     }
1425 
1426     /**
1427      * Gets the scope implementation for a given scope identifier.
1428      *
1429      * @param classLoader The class loader to use for loading scope implementations.
1430      * @param identifier The identifier of the scope to get an implementation of.
1431      *
1432      * @return The implementation of the scope identified by {@code identifier} or {@code null} if no such
1433      * scope implementation is found.
1434      *
1435      * @throws NullPointerException if {@code classLoader} or {@code identifier} is {@code null}.
1436      * @throws InstantiationException if instantiating a scope fails.
1437      *
1438      * @see #getDefaultScope(java.lang.String)
1439      */
1440     public Scope getScope( final ClassLoader classLoader, final String identifier ) throws InstantiationException
1441     {
1442         if ( classLoader == null )
1443         {
1444             throw new NullPointerException( "classLoader" );
1445         }
1446         if ( identifier == null )
1447         {
1448             throw new NullPointerException( "identifier" );
1449         }
1450 
1451         final Modules model = this.getModules( classLoader );
1452         final ClassLoader scopesLoader = getClassLoader( classLoader );
1453 
1454         synchronized ( this.scopes )
1455         {
1456             Map<String, Scope> cachedScopes = this.scopes.get( scopesLoader );
1457             if ( cachedScopes == null )
1458             {
1459                 cachedScopes = new HashMap();
1460                 this.scopes.put( scopesLoader, cachedScopes );
1461             }
1462 
1463             Scope scope = cachedScopes.get( identifier );
1464 
1465             if ( scope == null )
1466             {
1467                 // Bootstrap scope loading.
1468                 final Specification scopeSpecification = model.getSpecification( Scope.class );
1469 
1470                 if ( scopeSpecification != null )
1471                 {
1472                     final Implementations implementations =
1473                         model.getImplementations( scopeSpecification.getIdentifier() );
1474 
1475                     if ( implementations != null )
1476                     {
1477                         for ( Implementation i : implementations.getImplementation() )
1478                         {
1479                             if ( identifier.equals( i.getName() ) )
1480                             {
1481                                 final Instance instance = model.getInstance( i.getIdentifier() );
1482 
1483                                 if ( instance != null )
1484                                 {
1485                                     scope = (Scope) model.createObject( instance, classLoader );
1486                                     cachedScopes.put( identifier, scope );
1487                                     if ( this.isLoggable( Level.CONFIG ) )
1488                                     {
1489                                         this.log( Level.CONFIG, this.getMessage( "scopeInfo", new Object[]
1490                                             {
1491                                                 i.getIdentifier(), identifier, scopesLoader.toString()
1492                                             } ), null );
1493 
1494                                     }
1495                                     break;
1496                                 }
1497                                 else if ( this.isLoggable( Level.WARNING ) )
1498                                 {
1499                                     this.log( Level.WARNING, this.getMissingInstanceMessage(
1500                                         i.getIdentifier(), i.getName() ), new Exception() );
1501 
1502                                 }
1503                             }
1504                         }
1505                     }
1506                 }
1507                 else if ( this.isLoggable( Level.WARNING ) )
1508                 {
1509                     this.log( Level.WARNING, this.getMissingSpecificationMessage( Scope.class.getName() ),
1510                               new Exception() );
1511 
1512                 }
1513             }
1514 
1515             if ( scope == null )
1516             {
1517                 scope = this.getDefaultScope( identifier );
1518                 if ( scope != null )
1519                 {
1520                     cachedScopes.put( identifier, scope );
1521                     if ( this.isLoggable( Level.CONFIG ) )
1522                     {
1523                         this.log( Level.CONFIG, this.getDefaultScopeInfoMessage( identifier, scopesLoader ), null );
1524                     }
1525                 }
1526             }
1527 
1528             return scope;
1529         }
1530     }
1531 
1532     /**
1533      * Gets the default scope implementation for a given identifier.
1534      *
1535      * @param identifier The identifier of the scope to get a default implementation of.
1536      *
1537      * @return The default implementation of the scope identified by {@code identifier} or {@code null} if no such
1538      * default implementation is available.
1539      *
1540      * @throws NullPointerException if {@code identifier} is {@code null}.
1541      *
1542      * @see #getScope(java.lang.ClassLoader, java.lang.String)
1543      */
1544     public Scope getDefaultScope( final String identifier )
1545     {
1546         if ( identifier == null )
1547         {
1548             throw new NullPointerException( "identifier" );
1549         }
1550 
1551         DefaultScope defaultScope = null;
1552 
1553         if ( identifier.equals( SINGLETON_SCOPE_IDENTIFIER ) )
1554         {
1555             defaultScope = new DefaultScope( new HashMap<String, Object>() );
1556         }
1557 
1558         return defaultScope;
1559     }
1560 
1561     /**
1562      * Gets a locator to use with a given location URI.
1563      *
1564      * @param classLoader The class loader to use for loading locator implementations.
1565      * @param location The location URI to get a locator for.
1566      *
1567      * @return The locator to use for locating objects at {@code location} or {@code null} if no such locator is
1568      * available.
1569      *
1570      * @throws NullPointerException if {@code classLoader} or {@code location} is {@code null}.
1571      * @throws InstantiationException if instantiating a locator fails.
1572      *
1573      * @see #getDefaultLocator(java.net.URI)
1574      */
1575     public Locator getLocator( final ClassLoader classLoader, final URI location ) throws InstantiationException
1576     {
1577         if ( classLoader == null )
1578         {
1579             throw new NullPointerException( "classLoader" );
1580         }
1581         if ( location == null )
1582         {
1583             throw new NullPointerException( "location" );
1584         }
1585 
1586         final String scheme = location.getScheme();
1587 
1588         if ( scheme != null )
1589         {
1590             final Modules model = this.getModules( classLoader );
1591             final ClassLoader locatorsLoader = getClassLoader( classLoader );
1592 
1593             synchronized ( this.locators )
1594             {
1595                 Map<String, Locator> cachedLocators = this.locators.get( locatorsLoader );
1596                 if ( cachedLocators == null )
1597                 {
1598                     cachedLocators = new HashMap();
1599                     this.locators.put( locatorsLoader, cachedLocators );
1600                 }
1601 
1602                 Locator locator = cachedLocators.get( scheme );
1603 
1604                 if ( locator == null )
1605                 {
1606                     // Bootstrap locator loading.
1607                     final Specification locatorSpecification = model.getSpecification( Locator.class );
1608 
1609                     if ( locatorSpecification != null )
1610                     {
1611                         final Implementations implementations =
1612                             model.getImplementations( locatorSpecification.getIdentifier() );
1613 
1614                         if ( implementations != null )
1615                         {
1616                             for ( Implementation i : implementations.getImplementation() )
1617                             {
1618                                 if ( scheme.equals( i.getName() ) )
1619                                 {
1620                                     final Instance instance = model.getInstance( i.getIdentifier() );
1621 
1622                                     if ( instance != null )
1623                                     {
1624                                         locator = (Locator) model.createObject( instance, classLoader );
1625                                         cachedLocators.put( scheme, locator );
1626 
1627                                         if ( this.isLoggable( Level.CONFIG ) )
1628                                         {
1629                                             this.log( Level.CONFIG, this.getMessage( "locatorInfo", new Object[]
1630                                                 {
1631                                                     i.getIdentifier(), scheme, locatorsLoader.toString()
1632                                                 } ), null );
1633 
1634                                         }
1635 
1636                                         break;
1637                                     }
1638                                     else if ( this.isLoggable( Level.WARNING ) )
1639                                     {
1640                                         this.log( Level.WARNING, this.getMissingInstanceMessage(
1641                                             i.getIdentifier(), i.getName() ), new Exception() );
1642 
1643                                     }
1644                                 }
1645                             }
1646                         }
1647                     }
1648                     else if ( this.isLoggable( Level.WARNING ) )
1649                     {
1650                         this.log( Level.WARNING, this.getMissingSpecificationMessage( Locator.class.getName() ),
1651                                   new Exception() );
1652 
1653                     }
1654                 }
1655 
1656                 if ( locator == null )
1657                 {
1658                     locator = this.getDefaultLocator( location );
1659                     if ( locator != null )
1660                     {
1661                         cachedLocators.put( scheme, locator );
1662                         if ( this.isLoggable( Level.CONFIG ) )
1663                         {
1664                             this.log( Level.CONFIG, this.getDefaultLocatorInfoMessage( scheme, locatorsLoader ), null );
1665                         }
1666                     }
1667                 }
1668 
1669                 return locator;
1670             }
1671         }
1672 
1673         return null;
1674     }
1675 
1676     /**
1677      * Gets the default locator implementation for a given location URI.
1678      *
1679      * @param location The location URI to get a default locator implementation for.
1680      *
1681      * @return The default locator implementation for {@code location} or {@code null} if no default implementation is
1682      * available for {@code location}.
1683      *
1684      * @throws NullPointerException if {@code location} is {@code null}.
1685      *
1686      * @see #getLocator(java.lang.ClassLoader, java.net.URI)
1687      */
1688     public Locator getDefaultLocator( final URI location )
1689     {
1690         if ( location == null )
1691         {
1692             throw new NullPointerException( "location" );
1693         }
1694 
1695         Locator locator = null;
1696         final DefaultLocator defaultLocator = new DefaultLocator();
1697 
1698         if ( defaultLocator.isLocationSupported( location ) )
1699         {
1700             locator = defaultLocator;
1701         }
1702 
1703         return locator;
1704     }
1705 
1706     /**
1707      * Gets the invoker of the given class loader.
1708      *
1709      * @param classLoader The class loader to use for loading invoker implementations.
1710      *
1711      * @return The invoker of the given class loader.
1712      *
1713      * @throws NullPointerException if {@code classLoader} is {@code null}.
1714      * @throws InstantiationException if instantiating a new invoker fails.
1715      */
1716     public Invoker getInvoker( final ClassLoader classLoader ) throws InstantiationException
1717     {
1718         if ( classLoader == null )
1719         {
1720             throw new NullPointerException( "classLoader" );
1721         }
1722 
1723         final Modules model = this.getModules( classLoader );
1724         final ClassLoader invokersLoader = getClassLoader( classLoader );
1725 
1726         synchronized ( this.invokers )
1727         {
1728             Invoker invoker = this.invokers.get( invokersLoader );
1729 
1730             if ( invoker == null )
1731             {
1732                 final Specification invokerSpecification = model.getSpecification( Invoker.class );
1733 
1734                 if ( invokerSpecification != null )
1735                 {
1736                     final Implementations implementations =
1737                         model.getImplementations( invokerSpecification.getIdentifier() );
1738 
1739                     if ( implementations != null && !implementations.getImplementation().isEmpty() )
1740                     {
1741                         for ( Implementation i : implementations.getImplementation() )
1742                         {
1743                             if ( invoker == null )
1744                             {
1745                                 final Instance invokerInstance = model.getInstance( i.getIdentifier() );
1746 
1747                                 if ( invokerInstance != null )
1748                                 {
1749                                     invoker = (Invoker) model.createObject( invokerInstance, classLoader );
1750                                     this.invokers.put( invokersLoader, invoker );
1751 
1752                                     if ( this.isLoggable( Level.CONFIG ) )
1753                                     {
1754                                         this.log( Level.CONFIG, this.getMessage( "invokerInfo", new Object[]
1755                                             {
1756                                                 i.getIdentifier(), invokersLoader.toString()
1757                                             } ), null );
1758 
1759                                     }
1760                                 }
1761                                 else if ( this.isLoggable( Level.WARNING ) )
1762                                 {
1763                                     this.log( Level.WARNING, this.getMissingInstanceMessage(
1764                                         i.getIdentifier(), i.getName() ), new Exception() );
1765 
1766                                 }
1767                             }
1768                             else if ( this.isLoggable( Level.CONFIG ) )
1769                             {
1770                                 this.log( Level.CONFIG, this.getMessage( "ignoredInvoker", new Object[]
1771                                     {
1772                                         i.getIdentifier()
1773                                     } ), null );
1774 
1775                             }
1776                         }
1777                     }
1778                 }
1779                 else if ( this.isLoggable( Level.WARNING ) )
1780                 {
1781                     this.log( Level.WARNING, this.getMissingSpecificationMessage( Invoker.class.getName() ),
1782                               new Exception() );
1783 
1784                 }
1785 
1786                 if ( invoker == null )
1787                 {
1788                     invoker = new DefaultInvoker();
1789                     this.invokers.put( invokersLoader, invoker );
1790                     if ( this.isLoggable( Level.CONFIG ) )
1791                     {
1792                         this.log( Level.CONFIG, this.getMessage( "defaultInvokerInfo", new Object[]
1793                             {
1794                                 invokersLoader.toString()
1795                             } ), null );
1796 
1797                     }
1798                 }
1799             }
1800 
1801             return invoker;
1802         }
1803     }
1804 
1805     /**
1806      * Gets an invocation for a given object, instance, method and arguments.
1807      *
1808      * @param object The object to invoke.
1809      * @param instance The instance of the object to invoke.
1810      * @param method The method to invoke on {@code object}.
1811      * @param arguments The arguments of the invocation or {@code null}.
1812      *
1813      * @return An invocation with {@code object}, {@code instance}, {@code method} and {@code arguments}.
1814      *
1815      * @throws NullPointerException if {@code object}, {@code instance} or {@code method} is {@code null}.
1816      * @throws InstantiationException if instantiating a new invocation fails.
1817      */
1818     public Invocation getInvocation( final Object object, final Instance instance, final Method method,
1819                                      final Object[] arguments ) throws InstantiationException
1820     {
1821         if ( object == null )
1822         {
1823             throw new NullPointerException( "object" );
1824         }
1825         if ( instance == null )
1826         {
1827             throw new NullPointerException( "instance" );
1828         }
1829         if ( method == null )
1830         {
1831             throw new NullPointerException( "method" );
1832         }
1833 
1834         Invocation invocation = null;
1835         final ClassLoader classLoader = getClassLoader( object.getClass() );
1836         final Modules model = this.getModules( classLoader );
1837         final Specification invocationSpecification = model.getSpecification( Invocation.class );
1838 
1839         if ( invocationSpecification != null )
1840         {
1841             final Implementations implementations =
1842                 model.getImplementations( invocationSpecification.getIdentifier() );
1843 
1844             if ( implementations != null && !implementations.getImplementation().isEmpty() )
1845             {
1846                 for ( Implementation i : implementations.getImplementation() )
1847                 {
1848                     if ( invocation == null )
1849                     {
1850                         final Instance invocationInstance = model.getInstance( i.getIdentifier() );
1851 
1852                         if ( invocationInstance != null )
1853                         {
1854                             invocation = (Invocation) model.createObject( invocationInstance, classLoader );
1855                         }
1856                         else if ( this.isLoggable( Level.WARNING ) )
1857                         {
1858                             this.log( Level.WARNING, this.getMissingInstanceMessage(
1859                                 i.getIdentifier(), i.getName() ), new Exception() );
1860 
1861                         }
1862                     }
1863                     else if ( this.isLoggable( Level.CONFIG ) )
1864                     {
1865                         this.log( Level.CONFIG, this.getMessage( "ignoredInvocation", new Object[]
1866                             {
1867                                 i.getIdentifier()
1868                             } ), null );
1869 
1870                     }
1871                 }
1872             }
1873         }
1874         else if ( this.isLoggable( Level.WARNING ) )
1875         {
1876             this.log( Level.WARNING, this.getMissingSpecificationMessage( Invocation.class.getName() ),
1877                       new Exception() );
1878 
1879         }
1880 
1881         if ( invocation == null )
1882         {
1883             invocation = new DefaultInvocation();
1884         }
1885 
1886         invocation.getContext().put( DefaultInvocation.OBJECT_KEY, object );
1887         invocation.getContext().put( DefaultInvocation.METHOD_KEY, method );
1888         invocation.getContext().put( DefaultInvocation.ARGUMENTS_KEY, arguments );
1889         invocation.getContext().put( DefaultInvocation.INSTANCE_KEY, instance );
1890         invocation.getContext().put( DefaultInvocation.MODULES_KEY, model );
1891         invocation.getContext().put( DefaultInvocation.CLASSLOADER_KEY, classLoader );
1892         return invocation;
1893     }
1894 
1895     /**
1896      * Initializes the instance.
1897      * <p>This method is called once on first usage of a new instance.</p>
1898      *
1899      * @throws InstantiationException if initialization fails.
1900      */
1901     public synchronized void initialize() throws InstantiationException
1902     {
1903         if ( !this.initialized )
1904         {
1905             final List<LogRecord> bootstrapLogRecords = new LinkedList<LogRecord>();
1906             final List<Listener> providedListeners = new LinkedList<Listener>();
1907 
1908             try
1909             {
1910                 final long t0 = System.currentTimeMillis();
1911                 this.initialized = true;
1912 
1913                 this.listeners = null;
1914                 this.modules.clear();
1915                 this.invokers.clear();
1916                 this.locators.clear();
1917                 this.scopes.clear();
1918 
1919                 Listener bootstrapListener = new Listener()
1920                 {
1921 
1922                     public void onLog( final Level level, final String message, final Throwable throwable )
1923                     {
1924                         final LogRecord record = new LogRecord( level, message );
1925                         record.setThrown( throwable );
1926                         bootstrapLogRecords.add( record );
1927                     }
1928 
1929                 };
1930                 this.getListeners().add( bootstrapListener );
1931 
1932                 final ClassLoader classLoader = getClassLoader( this.getClass() );
1933                 final Modules model = this.getModules( classLoader );
1934                 final Specification objectManager = model.getSpecification( ObjectManager.class );
1935                 if ( objectManager == null )
1936                 {
1937                     throw new InstantiationException( this.getMissingSpecificationMessage(
1938                         ObjectManager.class.getName() ) );
1939 
1940                 }
1941 
1942                 final Instance thisInstance = model.getInstance( this );
1943                 if ( thisInstance == null )
1944                 {
1945                     throw new InstantiationException( this.getMissingInstanceMessage(
1946                         this.getClass().getName(), this.getArtifactNameMessage() ) );
1947 
1948                 }
1949 
1950                 if ( objectManager.getScope() != null )
1951                 {
1952                     final Scope scope = this.getScope( classLoader, objectManager.getScope() );
1953                     if ( scope == null )
1954                     {
1955                         throw new InstantiationException( this.getMissingScopeMessage( objectManager.getScope() ) );
1956                     }
1957 
1958                     scope.putObject( thisInstance.getIdentifier(), this );
1959                 }
1960 
1961                 // Bootstrap listener loading.
1962                 final Specification listenerSpecification = model.getSpecification( Listener.class );
1963 
1964                 if ( listenerSpecification != null )
1965                 {
1966                     final Implementations implementations =
1967                         model.getImplementations( listenerSpecification.getIdentifier() );
1968 
1969                     if ( implementations != null && !implementations.getImplementation().isEmpty() )
1970                     {
1971                         for ( Implementation i : implementations.getImplementation() )
1972                         {
1973                             final Instance listenerInstance = model.getInstance( i.getIdentifier() );
1974                             if ( listenerInstance != null )
1975                             {
1976                                 final Listener l = (Listener) model.createObject( listenerInstance, classLoader );
1977                                 providedListeners.add( l );
1978                                 this.log( Level.CONFIG, this.getRegisteredListenerMessage(
1979                                     l.getClass().getName() ), null );
1980 
1981                             }
1982                             else if ( this.isLoggable( Level.WARNING ) )
1983                             {
1984                                 this.log( Level.WARNING, this.getMissingInstanceMessage(
1985                                     i.getIdentifier(), i.getName() ), null );
1986 
1987                             }
1988                         }
1989                     }
1990                     else if ( this.isLoggable( Level.WARNING ) )
1991                     {
1992                         this.log( Level.WARNING, this.getMissingImplementationsMessage(
1993                             listenerSpecification.getIdentifier() ), new Exception() );
1994 
1995                     }
1996                 }
1997                 else if ( this.isLoggable( Level.WARNING ) )
1998                 {
1999                     this.log( Level.WARNING, this.getMissingSpecificationMessage(
2000                         Listener.class.getName() ), new Exception() );
2001 
2002                 }
2003 
2004                 if ( this.isLoggable( Level.FINE ) )
2005                 {
2006                     this.log( Level.FINE, this.getImplementationInfoMessage(
2007                         Long.valueOf( System.currentTimeMillis() - t0 ) ), null );
2008 
2009                 }
2010 
2011                 this.getListeners().addAll( providedListeners );
2012                 this.getListeners().remove( bootstrapListener );
2013                 bootstrapListener = null;
2014 
2015                 if ( !this.getListeners().isEmpty() )
2016                 {
2017                     for ( LogRecord logRecord : bootstrapLogRecords )
2018                     {
2019                         this.log( logRecord.getLevel(), logRecord.getMessage(), logRecord.getThrown() );
2020                     }
2021                 }
2022             }
2023             catch ( final InstantiationException e )
2024             {
2025                 Throwable cause = e;
2026                 if ( !bootstrapLogRecords.isEmpty() )
2027                 {
2028                     for ( LogRecord r : bootstrapLogRecords )
2029                     {
2030                         if ( r.getLevel().intValue() > Level.WARNING.intValue() )
2031                         {
2032                             if ( r.getMessage() != null )
2033                             {
2034                                 cause = new ObjectManagementException( r.getMessage(), cause );
2035                             }
2036                             if ( r.getThrown() != null )
2037                             {
2038                                 cause = new ObjectManagementException( r.getThrown().getMessage(), cause );
2039                             }
2040                         }
2041                     }
2042                 }
2043 
2044                 this.listeners = null;
2045                 this.modules.clear();
2046                 this.invokers.clear();
2047                 this.locators.clear();
2048                 this.scopes.clear();
2049                 this.initialized = false;
2050 
2051                 throw (InstantiationException) new InstantiationException( cause.getMessage() ).initCause( cause );
2052             }
2053         }
2054     }
2055 
2056     /**
2057      * Notifies registered listeners.
2058      *
2059      * @param level The level of the event.
2060      * @param message The message of the event or {@code null}.
2061      * @param throwable The throwable of the event or {@code null}.
2062      *
2063      * @throws NullPointerException if {@code level} is {@code null}.
2064      */
2065     protected void log( final Level level, final String message, final Throwable throwable )
2066     {
2067         if ( level == null )
2068         {
2069             throw new NullPointerException( "level" );
2070         }
2071 
2072         if ( this.isLoggable( level ) )
2073         {
2074             for ( Listener l : this.getListeners() )
2075             {
2076                 l.onLog( level, message, throwable );
2077             }
2078         }
2079     }
2080 
2081     /**
2082      * Creates a proxy for a given object.
2083      *
2084      * @param instance The instance of {@code object}.
2085      * @param object The object to create a proxy for.
2086      *
2087      * @return A proxy for {@code object}.
2088      *
2089      * @throws InstantiationException if creating a proxy fails.
2090      */
2091     private Object createProxy( final Instance instance, final Object object ) throws InstantiationException
2092     {
2093         try
2094         {
2095             final ClassLoader classLoader = getClassLoader( object.getClass() );
2096             final Set<Class> interfaces = new HashSet<Class>();
2097             boolean canProxy = instance.getSpecifications() != null;
2098 
2099             if ( canProxy )
2100             {
2101                 for ( Specification s : instance.getSpecifications().getSpecification() )
2102                 {
2103                     if ( s.getClazz() != null )
2104                     {
2105                         final Class clazz = Class.forName( s.getClazz(), true, classLoader );
2106 
2107                         if ( !clazz.isInterface() )
2108                         {
2109                             canProxy = false;
2110                             break;
2111                         }
2112 
2113                         interfaces.add( clazz );
2114                     }
2115                 }
2116             }
2117 
2118             if ( canProxy && !interfaces.isEmpty() )
2119             {
2120                 return Proxy.newProxyInstance( classLoader, interfaces.toArray( new Class[ interfaces.size() ] ),
2121                                                new java.lang.reflect.InvocationHandler()
2122                 {
2123 
2124                     public Object invoke( final Object proxy, final Method method, final Object[] args )
2125                         throws Throwable
2126                     {
2127                         return getInvoker( classLoader ).invoke( getInvocation( object, instance, method, args ) );
2128                     }
2129 
2130                 } );
2131 
2132             }
2133 
2134             return object;
2135         }
2136         catch ( final ClassNotFoundException e )
2137         {
2138             throw (InstantiationException) new InstantiationException( e.getMessage() ).initCause( e );
2139         }
2140     }
2141 
2142     private String getMessage( final String key, final Object arguments )
2143     {
2144         final ResourceBundle bundle =
2145             ResourceBundle.getBundle( DefaultObjectManager.class.getName().replace( '.', '/' ) );
2146 
2147         return new MessageFormat( bundle.getString( key ) ).format( arguments );
2148     }
2149 
2150     private String getArtifactNameMessage()
2151     {
2152         return this.getMessage( "artifactName", null );
2153     }
2154 
2155     private String getMissingSpecificationMessage( final String specification )
2156     {
2157         return this.getMessage( "missingSpecification", new Object[]
2158             {
2159                 specification
2160             } );
2161 
2162     }
2163 
2164     private String getMissingImplementationsMessage( final String specification )
2165     {
2166         return this.getMessage( "missingImplementations", new Object[]
2167             {
2168                 specification
2169             } );
2170 
2171     }
2172 
2173     private String getMissingImplementationMessage( final String implementationName, final String specification )
2174     {
2175         return this.getMessage( "missingImplementation", new Object[]
2176             {
2177                 implementationName, specification
2178             } );
2179 
2180     }
2181 
2182     private String getMissingObjectInstanceMessage( final Object object )
2183     {
2184         return this.getMessage( "missingObjectInstance", new Object[]
2185             {
2186                 object.toString()
2187             } );
2188 
2189     }
2190 
2191     private String getMissingDependencyMessage( final String dependency, final String implementation )
2192     {
2193         return this.getMessage( "missingDependency", new Object[]
2194             {
2195                 dependency, implementation
2196             } );
2197     }
2198 
2199     private String getMissingPropertyMessage( final String property, final String implementation )
2200     {
2201         return this.getMessage( "missingProperty", new Object[]
2202             {
2203                 property, implementation
2204             } );
2205 
2206     }
2207 
2208     private String getMissingMessageMessage( final String message, final String implementation )
2209     {
2210         return this.getMessage( "missingMessage", new Object[]
2211             {
2212                 message, implementation
2213             } );
2214 
2215     }
2216 
2217     private String getMissingInstanceMessage( final String implementation, final String implementationName )
2218     {
2219         return this.getMessage( "missingInstance", new Object[]
2220             {
2221                 implementation, implementationName
2222             } );
2223 
2224     }
2225 
2226     private String getMissingObjectMessage( final String implementation, final String implementationName )
2227     {
2228         return this.getMessage( "missingObject", new Object[]
2229             {
2230                 implementation, implementationName
2231             } );
2232 
2233     }
2234 
2235     private String getDependencyCycleMessage( final String implementation )
2236     {
2237         return this.getMessage( "dependencyCycle", new Object[]
2238             {
2239                 implementation
2240             } );
2241 
2242     }
2243 
2244     private String getImplementationInfoMessage( final Long startMillis )
2245     {
2246         return this.getMessage( "implementationInfo", new Object[]
2247             {
2248                 startMillis
2249             } );
2250 
2251     }
2252 
2253     private String getDefaultScopeInfoMessage( final String modelScope, final ClassLoader classLoader )
2254     {
2255         return this.getMessage( "defaultScopeInfo", new Object[]
2256             {
2257                 modelScope, classLoader.toString()
2258             } );
2259 
2260     }
2261 
2262     private String getMissingScopeMessage( final String modelScope )
2263     {
2264         return this.getMessage( "missingScope", new Object[]
2265             {
2266                 modelScope
2267             } );
2268 
2269     }
2270 
2271     private String getRegisteredListenerMessage( final String listener )
2272     {
2273         return this.getMessage( "listenerInfo", new Object[]
2274             {
2275                 listener
2276             } );
2277 
2278     }
2279 
2280     private String getUnsupportedMultiplicityMessage( final Multiplicity multiplicity )
2281     {
2282         return this.getMessage( "unsupportedMultiplicity", new Object[]
2283             {
2284                 multiplicity
2285             } );
2286     }
2287 
2288     private String getDefaultLocatorInfoMessage( final String scheme, final ClassLoader classLoader )
2289     {
2290         return this.getMessage( "defaultLocatorInfo", new Object[]
2291             {
2292                 scheme, classLoader.toString()
2293             } );
2294 
2295     }
2296 
2297     private String getMissingLocatorMessage( final URI location )
2298     {
2299         return this.getMessage( "missingLocator", new Object[]
2300             {
2301                 location.toString()
2302             } );
2303 
2304     }
2305 
2306     private String getMissingSpecificationClassMessage( final Specification specification )
2307     {
2308         return this.getMessage( "missingSpecificationClass", new Object[]
2309             {
2310                 specification.getIdentifier()
2311             } );
2312 
2313     }
2314 
2315     private void logModulesReport( final Modules mods, final ClassLoader classLoader )
2316     {
2317         final StringBuilder modulesInfo = new StringBuilder();
2318 
2319         this.log( Level.FINEST, this.getMessage( "modulesReport", null ), null );
2320 
2321         modulesInfo.append( "\tClassLoader:" ).append( classLoader );
2322 
2323         if ( mods.getDocumentation() != null )
2324         {
2325             modulesInfo.append( "|Documentation:" ).append( mods.getDocumentation().getText(
2326                 Locale.getDefault().getLanguage() ).getValue() );
2327 
2328         }
2329 
2330         this.log( Level.FINEST, modulesInfo.toString(), null );
2331 
2332         for ( Module m : mods.getModule() )
2333         {
2334             modulesInfo.setLength( 0 );
2335             modulesInfo.append( "\tM:" ).append( m.getName() );
2336 
2337             if ( m.getVersion() != null )
2338             {
2339                 modulesInfo.append( "|Version:" ).append( m.getVersion() );
2340             }
2341             if ( m.getVendor() != null )
2342             {
2343                 modulesInfo.append( "|Vendor:" ).append( m.getVendor() );
2344             }
2345 
2346             this.log( Level.FINEST, modulesInfo.toString(), null );
2347             modulesInfo.setLength( 0 );
2348 
2349             if ( m.getSpecifications() != null )
2350             {
2351                 for ( Specification s : m.getSpecifications().getSpecification() )
2352                 {
2353                     modulesInfo.append( "\t\t" );
2354                     this.appendSpecificationInfo( s, modulesInfo );
2355                     this.log( Level.FINEST, modulesInfo.toString(), null );
2356                     modulesInfo.setLength( 0 );
2357 
2358                     final Implementations available = mods.getImplementations( s.getIdentifier() );
2359 
2360                     if ( available != null )
2361                     {
2362                         for ( Implementation i : available.getImplementation() )
2363                         {
2364                             modulesInfo.append( "\t\t\t" );
2365                             this.appendImplementationInfo( i, modulesInfo ).append( "|Module:" ).
2366                                 append( mods.getModuleOfImplementation( i.getIdentifier() ).getName() );
2367 
2368                             this.log( Level.FINEST, modulesInfo.toString(), null );
2369                             modulesInfo.setLength( 0 );
2370                         }
2371                     }
2372                 }
2373             }
2374 
2375             if ( m.getImplementations() != null )
2376             {
2377                 for ( Implementation i : m.getImplementations().getImplementation() )
2378                 {
2379                     modulesInfo.append( "\t\t" );
2380                     this.appendImplementationInfo( i, modulesInfo );
2381                     this.log( Level.FINEST, modulesInfo.toString(), null );
2382                     modulesInfo.setLength( 0 );
2383 
2384                     if ( i.getImplementations() != null )
2385                     {
2386                         modulesInfo.append( "\t\t\t" );
2387                         for ( ImplementationReference r : i.getImplementations().getReference() )
2388                         {
2389                             this.appendImplementationInfo(
2390                                 mods.getImplementation( r.getIdentifier() ), modulesInfo ).append( "|Module:" ).
2391                                 append( mods.getModuleOfImplementation( r.getIdentifier() ).getName() );
2392 
2393                             this.log( Level.FINEST, modulesInfo.toString(), null );
2394                             modulesInfo.setLength( 0 );
2395                         }
2396                     }
2397                     if ( i.getSpecifications() != null )
2398                     {
2399                         for ( SpecificationReference s : i.getSpecifications().getReference() )
2400                         {
2401                             modulesInfo.append( "\t\t\tS:" ).append( s.getIdentifier() );
2402 
2403                             if ( s.getVersion() != null )
2404                             {
2405                                 modulesInfo.append( "|Version:" ).append( s.getVersion() );
2406                             }
2407 
2408                             modulesInfo.append( "|Module:" ).append( mods.getModuleOfSpecification(
2409                                 s.getIdentifier() ).getName() );
2410 
2411                             this.log( Level.FINEST, modulesInfo.toString(), null );
2412                             modulesInfo.setLength( 0 );
2413                         }
2414                     }
2415 
2416                     if ( i.getDependencies() != null )
2417                     {
2418                         for ( Dependency d : i.getDependencies().getDependency() )
2419                         {
2420                             modulesInfo.append( "\t\t\tD:" ).append( d.getName() ).append( "|Identifier:" ).
2421                                 append( d.getIdentifier() );
2422 
2423                             if ( d.getImplementationName() != null )
2424                             {
2425                                 modulesInfo.append( "|Name:" ).append( d.getImplementationName() );
2426                             }
2427 
2428                             modulesInfo.append( "|Module:" ).append( mods.getModuleOfSpecification(
2429                                 d.getIdentifier() ).getName() );
2430 
2431                             this.log( Level.FINEST, modulesInfo.toString(), null );
2432                             modulesInfo.setLength( 0 );
2433 
2434                             final Implementations available = mods.getImplementations( d.getIdentifier() );
2435 
2436                             if ( available != null )
2437                             {
2438                                 for ( Implementation di : available.getImplementation() )
2439                                 {
2440                                     modulesInfo.append( "\t\t\t\t" );
2441                                     this.appendImplementationInfo( di, modulesInfo ).append( "|Module:" ).
2442                                         append( mods.getModuleOfImplementation( di.getIdentifier() ).getName() );
2443 
2444                                     this.log( Level.FINEST, modulesInfo.toString(), null );
2445                                     modulesInfo.setLength( 0 );
2446                                 }
2447                             }
2448                         }
2449                     }
2450 
2451                     if ( i.getMessages() != null )
2452                     {
2453                         for ( Message msg : i.getMessages().getMessage() )
2454                         {
2455                             modulesInfo.append( "\t\t\tM:" ).append( msg.getName() ).append( "|Text:" ).
2456                                 append( msg.getTemplate().getText( Locale.getDefault().getLanguage() ).getValue() );
2457 
2458                             this.log( Level.FINEST, modulesInfo.toString(), null );
2459                             modulesInfo.setLength( 0 );
2460                         }
2461                     }
2462 
2463                     if ( i.getProperties() != null )
2464                     {
2465                         for ( Property p : i.getProperties().getProperty() )
2466                         {
2467                             modulesInfo.append( "\t\t\tP:" ).append( p.getName() );
2468                             modulesInfo.append( "|Type:" ).append( p.getType() );
2469                             modulesInfo.append( "|Value:" ).append( p.getValue() );
2470 
2471                             try
2472                             {
2473                                 modulesInfo.append( "|JavaValue:" ).append( p.getJavaValue( classLoader ) );
2474                             }
2475                             catch ( final ModelException e )
2476                             {
2477                                 modulesInfo.append( "|JavaValue:" ).append( e );
2478                             }
2479 
2480                             this.log( Level.FINEST, modulesInfo.toString(), null );
2481                             modulesInfo.setLength( 0 );
2482                         }
2483                     }
2484                 }
2485             }
2486         }
2487     }
2488 
2489     private StringBuilder appendSpecificationInfo( final Specification s, final StringBuilder b )
2490     {
2491         b.append( "S:" ).append( s.getIdentifier() );
2492         if ( s.getVersion() != null )
2493         {
2494             b.append( "|Version:" ).append( s.getVersion() );
2495         }
2496         if ( s.getVendor() != null )
2497         {
2498             b.append( "|Vendor:" ).append( s.getVendor() );
2499         }
2500 
2501         b.append( "|Multiplicity:" ).append( s.getMultiplicity() ).append( "|Scope:" ).
2502             append( s.getScope() == null ? "Multiton" : s.getScope() );
2503 
2504         if ( s.getClazz() != null )
2505         {
2506             b.append( "|Class:" ).append( s.getClazz() );
2507         }
2508 
2509         return b;
2510     }
2511 
2512     private StringBuilder appendImplementationInfo( final Implementation i, final StringBuilder b )
2513     {
2514         b.append( "I:" ).append( i.getIdentifier() ).append( "|Name:" ).append( i.getName() ).append( "|Abstract:" ).
2515             append( i.isAbstract() ).append( "|Final:" ).append( i.isFinal() ).append( "|Stateless:" ).
2516             append( i.isStateless() );
2517 
2518         if ( i.getVersion() != null )
2519         {
2520             b.append( "|Version:" ).append( i.getVersion() );
2521         }
2522         if ( i.getVendor() != null )
2523         {
2524             b.append( "|Vendor:" ).append( i.getVendor() );
2525         }
2526         if ( i.getClazz() != null )
2527         {
2528             b.append( "|Class:" ).append( i.getClazz() );
2529         }
2530         if ( i.getLocation() != null )
2531         {
2532             b.append( "|Location:" ).append( i.getLocation() );
2533         }
2534 
2535         return b;
2536     }
2537 
2538     // SECTION-END
2539     // SECTION-START[Dependencies]
2540     // SECTION-END
2541     // SECTION-START[Properties]
2542     // SECTION-END
2543     // SECTION-START[Messages]
2544     // SECTION-END
2545 }