Source code: com/aendvari/cerberus/component/assembly/ComponentAssembler.java
1 /*
2 * ComponentAssembler.java
3 *
4 * Copyright (c) 2001, 2002 Aendvari, Ltd. All Rights Reserved.
5 *
6 * See the file LICENSE for terms of use.
7 *
8 */
9
10 package com.aendvari.cerberus.component.assembly;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.Iterator;
16
17 import com.aendvari.cerberus.component.descriptor.*;
18 import com.aendvari.cerberus.component.directory.*;
19
20 /**
21 * <p>Builds the components defined in a {@link AssemblyDescriptor}.</p>
22 *
23 * <p>The {@link AssemblyContext} may be used to provide context specific
24 * information during the build process.</p>
25 *
26 * @author Trevor Milne
27 *
28 */
29
30 public class ComponentAssembler
31 {
32 /** The {@link AssemblyContext} supplying context specific information. */
33 private AssemblyContext context;
34
35 /** The {@link AssemblyDescriptor} to build the components from. */
36 private AssemblyDescriptor assembly;
37
38 /** The {@link ComponentDirectory} to register components into. */
39 private ComponentDirectory directory;
40
41
42 /* Constructors. */
43
44
45 /**
46 * Constructs a <code>ComponentAssembler</code> instance.
47 *
48 * The components defined in the supplied {@link AssemblyDescriptor} are assembled
49 * in context of the supplied {@link AssemblyContext}.
50 *
51 * @param setAssemblyContext The {@link AssemblyContext} to build with.
52 * @param setAssembly The {@link AssemblyDescriptor} to build from.
53 * @param setDirectory The {@link ComponentDirectory} to register components.
54 *
55 */
56
57 public ComponentAssembler(
58 AssemblyContext setAssemblyContext, AssemblyDescriptor setAssembly, ComponentDirectory setDirectory)
59 {
60 context = setAssemblyContext;
61 assembly = setAssembly;
62 directory = setDirectory;
63 }
64
65
66 /* Namespaces. */
67
68
69 /**
70 * Adds the supplied namespace to a property name if one does not exist.
71 *
72 * @param namespace The namespace that uniquely identifies the property.
73 * @param name The name of the property.
74 *
75 * @return A uniquely identifiable property name.
76 *
77 */
78
79 private String addNamespace(String namespace, String name)
80 {
81 // check for namespace
82 if (name.indexOf('.') < 0)
83 {
84 // relative attribute name, include namespace
85 name = namespace + "." + name;
86 }
87
88 return name;
89 }
90
91 /**
92 * Allows various resolution methods to act on both attributes and messages.
93 *
94 */
95
96 private interface PropertyResolver
97 {
98 /**
99 * Returns whether the specified property is available within the component definition.
100 *
101 * @param definition The {@link ComponentDefinition} to search.
102 * @param name The name of the property.
103 *
104 * @return True if the definition contains the named property.
105 *
106 */
107
108 public boolean hasProperty(ComponentDefinition definition, String name);
109
110 /**
111 * Creates an error message describing an undefined property.
112 *
113 * @param property The name of the property.
114 * @param reference The context in which the property was referenced.
115 *
116 * @return A string describing the error.
117 *
118 */
119
120 public String getUndefinedPropertyMessage(String property, String reference);
121
122 /**
123 * Creates an error message describing an ambiguously reference property.
124 *
125 * @param property The name of the property.
126 * @param namespaces The potential namespaces that could be chosen.
127 * @param reference The context inwhich the property was referenced.
128 *
129 * @return A string describing the error.
130 *
131 */
132
133 public String getAmbiguousPropertyMessage(String property, Collection namespaces, String reference);
134 }
135
136 /**
137 * An implementation of the {@link PropertyResolver} for component attributes.
138 *
139 */
140
141 private class AttributeResolver implements PropertyResolver
142 {
143 public boolean hasProperty(ComponentDefinition definition, String name)
144 {
145 return (definition.getAttribute(name) != null);
146 }
147
148 public String getUndefinedPropertyMessage(String attribute, String reference)
149 {
150 // call outer class method
151 return createUndefinedPropertyMessage("attribute", attribute, reference);
152 }
153
154 public String getAmbiguousPropertyMessage(String attribute, Collection namespaces, String reference)
155 {
156 // call outer class method
157 return createAmbiguousPropertyMessage("attribute", attribute, namespaces, reference);
158 }
159 }
160
161 /**
162 * An implementation of the {@link PropertyResolver} for component messages.
163 *
164 */
165
166 private class MessageResolver implements PropertyResolver
167 {
168 public boolean hasProperty(ComponentDefinition definition, String name)
169 {
170 return (definition.getMessage(name) != null);
171 }
172
173 public String getUndefinedPropertyMessage(String message, String reference)
174 {
175 // call outer class method
176 return createUndefinedPropertyMessage("message", message, reference);
177 }
178
179 public String getAmbiguousPropertyMessage(String message, Collection namespaces, String reference)
180 {
181 // call outer class method
182 return createAmbiguousPropertyMessage("message", message, namespaces, reference);
183 }
184 }
185
186 /**
187 * Resolves the namespace for the specified property. The definition hierarchy
188 * is traversed to find the namespace for the property.
189 *
190 * @param definition The {@link ComponentDefinition} containing the property.
191 * @param property The name of the property to resolve.
192 * @param resolver The {@link PropertyResolver} to access property data.
193 * @param context The current operating context for error reporting.
194 *
195 * @return The resolved property name.
196 *
197 * @exception AssemblyException Thrown when a problem occurs during assembly.
198 *
199 */
200
201 private String resolveProperty(
202 ComponentDefinition definition, String property, PropertyResolver resolver, String context)
203 throws AssemblyException
204 {
205 // check for namespace
206 if (property.indexOf('.') >= 0)
207 {
208 // check that reference is valid
209 validateProperty(definition, property, resolver, context);
210
211 // if namespace available, then no resolution is needed
212 return property;
213 }
214
215 // travel up hierarchy and search for match
216 ArrayList searchDefinitions = new ArrayList();
217 HashSet searchResults = new HashSet();
218
219 // start at local namespace
220 searchDefinitions.add(definition);
221
222 // continue until all parents have been visited
223 while (searchDefinitions.size() > 0)
224 {
225 // create copy of search list to allow safe iteration
226 ArrayList iterationCollection = new ArrayList(searchDefinitions);
227 Iterator searchIterator = iterationCollection.iterator();
228
229 while (searchIterator.hasNext())
230 {
231 ComponentDefinition searchDefinition = (ComponentDefinition)searchIterator.next();
232
233 // does property exist in this namespace
234
235 // make namespace same as current definition
236 String potential = addNamespace(searchDefinition.getName(), property);
237
238 // search definition
239 if (resolver.hasProperty(searchDefinition, potential))
240 {
241 // property exists, add namespace to result (should be only one)
242 searchResults.add(searchDefinition);
243 }
244 else
245 {
246 // property not found continue up tree
247 Iterator parentIterator = searchDefinition.getParents().iterator();
248
249 while (parentIterator.hasNext())
250 {
251 String parentName = (String)parentIterator.next();
252 ComponentDefinition parentDefinition = assembly.getDefinition(parentName);
253
254 // add parent to search list
255 searchDefinitions.add(parentDefinition);
256 }
257 }
258
259 // remove definition from search list
260 searchDefinitions.remove(searchDefinition);
261 }
262 }
263
264 // error if no match was found
265 if (searchResults.size() == 0)
266 {
267 // construct exception message
268 String message = resolver.getUndefinedPropertyMessage(property, context);
269
270 // throw exception
271 throw new AssemblyException(message.toString());
272 }
273 else
274 // error if more than one match was found
275 if (searchResults.size() > 1)
276 {
277 // construct exception message
278 String message = resolver.getAmbiguousPropertyMessage(property, searchResults, context);
279
280 // throw exception
281 throw new AssemblyException(message.toString());
282 }
283
284 // retrieve property namespace
285 ComponentDefinition namespaceDefinition = (ComponentDefinition)searchResults.iterator().next();
286
287 String resolved = addNamespace(namespaceDefinition.getName(), property);
288
289 return resolved;
290 }
291
292 /**
293 * Determines whether the supplied property name is valid.
294 *
295 * @param definition The {@link ComponentDefinition} containing the property.
296 * @param property The name of the property to resolve.
297 * @param resolver The {@link PropertyResolver} to access property data.
298 * @param context The current operating context for error reporting.
299 *
300 * @return True if the property is valid.
301 *
302 * @exception AssemblyException Thrown when a problem occurs during assembly.
303 *
304 */
305
306 private void validateProperty(
307 ComponentDefinition definition, String property, PropertyResolver resolver, String context)
308 throws AssemblyException
309 {
310 // extract namespace
311 String namespace = property.substring(0, property.indexOf('.'));
312
313 // check for valid namespace (definition)
314 ComponentDefinition propertyDefinition = assembly.getDefinition(namespace);
315
316 if (propertyDefinition == null)
317 {
318 // construct exception message
319 String message = createUndefinedComponentDefinitionMessage(
320 namespace, context + "(" + property + ")");
321
322 // throw exception
323 throw new AssemblyException(message.toString());
324 }
325
326 // check for valid property within namespace
327 if (!resolver.hasProperty(propertyDefinition, property))
328 {
329 // construct exception message
330 String message = resolver.getUndefinedPropertyMessage(property, context);
331
332 // throw exception
333 throw new AssemblyException(message.toString());
334 }
335
336 // does namespace exist in active definition
337
338 // travel up hierarchy and search for definition
339 ArrayList searchDefinitions = new ArrayList();
340 HashSet searchResults = new HashSet();
341
342 // start at local namespace
343 searchDefinitions.add(definition);
344
345 // continue until all parents have been visited
346 while (searchDefinitions.size() > 0)
347 {
348 // create copy of search list to allow safe iteration
349 ArrayList iterationCollection = new ArrayList(searchDefinitions);
350 Iterator searchIterator = iterationCollection.iterator();
351
352 while (searchIterator.hasNext())
353 {
354 ComponentDefinition searchDefinition = (ComponentDefinition)searchIterator.next();
355
356 // does the definition exist in the hierarchy
357 if (searchDefinition.getName().equals(propertyDefinition.getName()))
358 {
359 // the property name is valid
360 return;
361 }
362
363 // definition not found continue up tree
364 Iterator parentIterator = searchDefinition.getParents().iterator();
365
366 while (parentIterator.hasNext())
367 {
368 String parentName = (String)parentIterator.next();
369 ComponentDefinition parentDefinition = assembly.getDefinition(parentName);
370
371 // add parent to search list
372 searchDefinitions.add(parentDefinition);
373 }
374
375 // remove definition from search list
376 searchDefinitions.remove(searchDefinition);
377 }
378 }
379
380 // the definition could not be found in the heritage
381
382 // construct exception message
383 String message = createNonParentDefinitionMessage(
384 definition.getName(), namespace, context + "(" + property + ")");
385
386 // throw exception
387 throw new AssemblyException(message.toString());
388 }
389
390 /**
391 * Resolves the namespaces for the attributes referenced within the supplied value.
392 *
393 * @param definition The {@link ComponentDefinition} containing the property.
394 * @param value The {@link MultiPartValue} to modify.
395 * @param context The current operating context for error reporting.
396 *
397 * @exception AssemblyException Thrown when a problem occurs during assembly.
398 *
399 */
400
401 private void resolveReferencedAttributes(ComponentDefinition definition, MultiPartValue value, String context)
402 throws AssemblyException
403 {
404 // resolve attributes
405 PropertyResolver resolver = new AttributeResolver();
406
407 // create copy of original value
408 MultiPartValue originalValue = new MultiPartValue(value);
409
410 // erase value for replacement
411 value.clear();
412
413 Iterator valueParts = originalValue.getParts().iterator();
414
415 while (valueParts.hasNext())
416 {
417 MultiPartValue.Part part = (MultiPartValue.Part)valueParts.next();
418
419 // literal parts stay the same
420 if (part.getType() == MultiPartValue.Part.Type.Literal)
421 {
422 value.addLiteral(part.getValue());
423 }
424 // attribute references need to be resolved
425 else
426 {
427 String resolved = resolveProperty(definition, part.getValue(), resolver, context);
428 value.addAttribute(resolved);
429 }
430 }
431 }
432
433 /**
434 * Resolves the namespaces for the message referenced within the supplied topic.
435 *
436 * @param definition The {@link ComponentDefinition} containing the message.
437 * @param topic The {@link MultiPartValue} topic to modify.
438 * @param context The current operating context for error reporting.
439 *
440 * @exception AssemblyException Thrown when a problem occurs during assembly.
441 *
442 */
443
444 private void resolveReferencedMessage(ComponentDefinition definition, MultiPartValue topic, String context)
445 throws AssemblyException
446 {
447 // resolve messages
448 PropertyResolver resolver = new MessageResolver();
449
450 // get the message name
451 String mappedName = ((MultiPartValue.Part)topic.getParts().iterator().next()).getValue();
452
453 // erase value for replacement
454 topic.clear();
455
456 String resolved = resolveProperty(definition, mappedName, resolver, context);
457 topic.addAttribute(resolved);
458 }
459
460 /**
461 * Resolves the namespaces and references within the supplied component definition.
462 *
463 * @param definition The {@link ComponentDefinition} to resolve.
464 *
465 * @exception AssemblyException Thrown when a problem occurs during assembly.
466 *
467 */
468
469 private void resolveDefinition(ComponentDefinition definition)
470 throws AssemblyException
471 {
472 String context = "the definition " + definition.getName();
473
474 // resolve property names
475 // if no namespace is specified for property, assume local (definition) namespace
476
477 // resolve attributes
478 Iterator attributeIterator = new ArrayList(definition.getAttributes()).iterator();
479
480 while (attributeIterator.hasNext())
481 {
482 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
483
484 // specify namespace of attribute
485 String oldName = attribute.getName();
486 String newName = addNamespace(definition.getName(), attribute.getName());
487
488 attribute.setName(newName);
489
490 definition.updateAttribute(oldName, attribute);
491 }
492
493 // resolve messages
494 Iterator messageIterator = new ArrayList(definition.getMessages()).iterator();
495
496 while (messageIterator.hasNext())
497 {
498 ComponentMessage message = (ComponentMessage)messageIterator.next();
499
500 // specify namespace of message
501 String oldName = message.getName();
502 String newName = addNamespace(definition.getName(), message.getName());
503
504 message.setName(newName);
505
506 definition.updateMessage(oldName, message);
507 }
508
509 // resolve property values
510 // if no namespace is specified for a referenced property, then search definition
511
512 // resolve attributes
513 attributeIterator = definition.getAttributes().iterator();
514
515 while (attributeIterator.hasNext())
516 {
517 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
518
519 // manipulate attribute values
520 resolveReferencedAttributes(definition, attribute.getValue(), context);
521 }
522
523 // resolve messages
524 messageIterator = definition.getMessages().iterator();
525
526 while (messageIterator.hasNext())
527 {
528 ComponentMessage message = (ComponentMessage)messageIterator.next();
529
530 // check for mapped message
531 if (message.isMapped())
532 {
533 // manipulate mapped message
534 resolveReferencedMessage(definition, message.getTopic(), context);
535 }
536 else
537 {
538 // manipulate attribute values
539 resolveReferencedAttributes(definition, message.getTopic(), context);
540 }
541 }
542 }
543
544 /**
545 * Resolves the namespaces and references within the supplied component definitions.
546 *
547 * @param definitions The {@link ComponentDefinition ComponentDefinitions} to resolve.
548 *
549 * @exception AssemblyException Thrown when a problem occurs during assembly.
550 *
551 */
552
553 private void resolveDefinitions(Collection definitions)
554 throws AssemblyException
555 {
556 // resolve definition namespaces, visit in order of hierarchy
557
558 ArrayList resolvedParents = new ArrayList();
559 ArrayList unresolved = new ArrayList(definitions);
560
561 // continue until all definitions have been resolved
562 while (unresolved.size() > 0)
563 {
564 // create copy of unresolved list to allow safe iteration
565 ArrayList iterationCollection = new ArrayList(unresolved);
566 int unresolvedSize = unresolved.size();
567
568 // resolve any definitions that have their parents resolved
569 Iterator definitionIterator = iterationCollection.iterator();
570
571 while (definitionIterator.hasNext())
572 {
573 ComponentDefinition definition = (ComponentDefinition)definitionIterator.next();
574
575 // can be resolved, if all parents have been resolved
576 if (resolvedParents.containsAll(definition.getParents()))
577 {
578 resolveDefinition(definition);
579
580 // remove from unresolved list
581 unresolved.remove(definition);
582
583 // add as potential parent
584 resolvedParents.add(definition.getName());
585 }
586 }
587
588 // if no changes were made, then a cycle or undefined parent has been detected
589 if (unresolved.size() == unresolvedSize)
590 {
591 // construct exception message
592 String message = createUndefinedParentsMessage(unresolved);
593
594 // throw exception
595 throw new AssemblyException(message.toString());
596 }
597 }
598 }
599
600 /**
601 * Resolves the namespaces and references within the supplied component instance.
602 *
603 * @param instance The {@link ComponentInstance} to resolve.
604 * @param definition The {@link ComponentDefinition} of the instance.
605 *
606 * @exception AssemblyException Thrown when a problem occurs during assembly.
607 *
608 */
609
610 private void resolveInstance(ComponentInstance instance, ComponentDefinition definition)
611 throws AssemblyException
612 {
613 PropertyResolver attributeResolver = new AttributeResolver();
614 PropertyResolver messageResolver = new MessageResolver();
615
616 String context = "the instance " + instance.getName() + "(" + definition.getName() + ")";
617
618 // resolve property names
619
620 // resolve attributes
621 Iterator attributeIterator = new ArrayList(instance.getAttributes()).iterator();
622
623 while (attributeIterator.hasNext())
624 {
625 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
626
627 // determine namespace of attribute
628 String oldName = attribute.getName();
629 String newName = resolveProperty(definition, oldName, attributeResolver, context);
630
631 attribute.setName(newName);
632
633 instance.updateAttribute(oldName, attribute);
634 }
635
636 // resolve messages
637 Iterator messageIterator = new ArrayList(instance.getMessages()).iterator();
638
639 while (messageIterator.hasNext())
640 {
641 ComponentMessage message = (ComponentMessage)messageIterator.next();
642
643 // determine namespace of message
644 String oldName = message.getName();
645 String newName = resolveProperty(definition, oldName, messageResolver, context);
646
647 message.setName(newName);
648
649 instance.updateMessage(oldName, message);
650 }
651
652 // resolve property values
653
654 // resolve attributes
655 attributeIterator = instance.getAttributes().iterator();
656
657 while (attributeIterator.hasNext())
658 {
659 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
660
661 // manipulate attribute values
662 resolveReferencedAttributes(definition, attribute.getValue(), context);
663 }
664
665 // resolve messages
666 messageIterator = instance.getMessages().iterator();
667
668 while (messageIterator.hasNext())
669 {
670 ComponentMessage message = (ComponentMessage)messageIterator.next();
671
672 // check for mapped message
673 if (message.isMapped())
674 {
675 // manipulate mapped message
676 resolveReferencedMessage(definition, message.getTopic(), context);
677 }
678 else
679 {
680 // manipulate attribute values
681 resolveReferencedAttributes(definition, message.getTopic(), context);
682 }
683 }
684 }
685
686
687 /* Assembly. */
688
689
690 /**
691 * Creates a property value based on the {@link MultiPartValue} and the
692 * attributes defined in the {@link ComponentDescriptor}.
693 *
694 * @param descriptor The {@link ComponentDescriptor} for the component.
695 * @param name The name of the property.
696 * @param value A {@link MultiPartValue} describing the value.
697 *
698 * @exception AssemblyException Thrown when a problem occurs during assembly.
699 *
700 */
701
702 private String assemblePropertyValue(ComponentDescriptor descriptor, String name, MultiPartValue value)
703 throws AssemblyException
704 {
705 String context = "the attribute " + name + " of the component " + descriptor.getName();
706 StringBuffer complete = new StringBuffer();
707
708 Iterator partIterator = value.getParts().iterator();
709
710 while (partIterator.hasNext())
711 {
712 // get value part
713 MultiPartValue.Part part = (MultiPartValue.Part)partIterator.next();
714
715 // translate into string value
716 if (part.getType() == MultiPartValue.Part.Type.Literal)
717 {
718 // append literal value
719 complete.append(part.getValue());
720 }
721 else
722 {
723 // get attribute
724 ComponentDescriptor.Attribute attributeObject = descriptor.getAttribute(part.getValue());
725
726 if (attributeObject == null)
727 {
728 // construct exception message
729 String message = createUndefinedPropertyMessage("attribute", part.getValue(), context);
730
731 // throw exception
732 throw new AssemblyException(message.toString());
733 }
734
735 // get attribute value
736 String attribute = attributeObject.getValue();
737
738 // check for null value
739 if (attribute == null)
740 {
741 // construct exception message
742 String message = createUndefinedPropertyValueMessage("attribute", part.getValue(), context);
743
744 // throw exception
745 throw new AssemblyException(message.toString());
746 }
747
748 // append to value
749 complete.append(attribute);
750 }
751 }
752
753 return complete.toString();
754 }
755
756 /**
757 * Checks that the attributes used in the supplied {@link MultiPartValue}
758 * have been defined in the {@link ComponentDescriptor}.
759 *
760 * @param descriptor The {@link ComponentDescriptor} for the component.
761 * @param value A {@link MultiPartValue} describing the value.
762 *
763 */
764
765 private boolean dependenciesAvailable(ComponentDescriptor descriptor, MultiPartValue value)
766 {
767 Iterator partIterator = value.getParts().iterator();
768
769 while (partIterator.hasNext())
770 {
771 // get value part
772 MultiPartValue.Part part = (MultiPartValue.Part)partIterator.next();
773
774 // check if values for internal attributes have been provided
775 if (part.getType() == MultiPartValue.Part.Type.Attribute)
776 {
777 // get attribute value
778 ComponentDescriptor.Attribute attribute = descriptor.getAttribute(part.getValue());
779
780 // check for null value
781 if (attribute == null)
782 {
783 return false;
784 }
785 }
786 }
787
788 return true;
789 }
790
791 /**
792 * Creates and stores attribute values in the {@link ComponentDescriptor}.
793 * Any attributes referenced by the {@link MultiPartValue} are taken from the other
794 * attributes in the {@link ComponentDescriptor}. Those attributes must have their
795 * values specified before being used by another attribute.
796 *
797 * @param definition The {@link ComponentDefinition} for the component.
798 * @param instance The {@link ComponentInstance} for the component.
799 * @param descriptor The {@link ComponentDescriptor} for the component.
800 *
801 * @exception AssemblyException Thrown when a problem occurs during assembly.
802 *
803 */
804
805 private void assembleComponentAttributes(
806 ComponentDefinition definition, ComponentInstance instance, ComponentDescriptor descriptor)
807 throws AssemblyException
808 {
809 Collection unresolved = new ArrayList(definition.getAttributes());
810
811 // continue looping through attributes until all have been resolved or cycle has been detected
812 while (unresolved.size() > 0)
813 {
814 // make a pass through the attributes and set any that can be resolved
815 Collection iterationCollection = new ArrayList(unresolved);
816 Iterator attributeIterator = iterationCollection.iterator();
817
818 int unresolvedSize = unresolved.size();
819
820 while (attributeIterator.hasNext())
821 {
822 // get attribute definitions
823 ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
824 ComponentAttribute instanceAttribute = instance.getAttribute(definitionAttribute.getName());
825
826 // check if instance provides value
827 if (instanceAttribute != null)
828 {
829 // use value specified by instance
830
831 // check if dependent attributes have been resolved
832 boolean valid = dependenciesAvailable(descriptor, instanceAttribute.getValue());
833
834 if (valid)
835 {
836 // the value has been specified
837
838 // remove attribute from undefined list
839 unresolved.remove(definitionAttribute);
840
841 // set value in descriptor
842 String value = assemblePropertyValue(
843 descriptor, instanceAttribute.getName(), instanceAttribute.getValue());
844
845 descriptor.addAttribute(definitionAttribute.getName(), definitionAttribute.getAccess(), value);
846 }
847 }
848 else
849 {
850 // check if attribute already defined (by parent or composite)
851 if (descriptor.getAttribute(definitionAttribute.getName()) != null)
852 {
853 // the value has been specified (typically by composite)
854
855 // remove attribute from undefined list
856 unresolved.remove(definitionAttribute);
857 }
858 else
859 {
860 // check if default value is available in definition
861 MultiPartValue defaultValue = definitionAttribute.getValue();
862
863 if (defaultValue.getParts().size() > 0)
864 {
865 // check if dependent attributes have been resolved
866 boolean valid = dependenciesAvailable(descriptor, defaultValue);
867
868 if (valid)
869 {
870 // the value has been specified
871
872 // remove attribute from undefined list
873 unresolved.remove(definitionAttribute);
874
875 // set value in descriptor
876 String value = assemblePropertyValue(
877 descriptor, definitionAttribute.getName(), definitionAttribute.getValue());
878
879 descriptor.addAttribute(definitionAttribute.getName(), definitionAttribute.getAccess(), value);
880 }
881 }
882 }
883 }
884 }
885
886 // check for attribute cycle
887 if (unresolved.size() == unresolvedSize)
888 {
889 // remove any non-required attributes
890 iterationCollection = new ArrayList(unresolved);
891 attributeIterator = iterationCollection.iterator();
892
893 while (attributeIterator.hasNext())
894 {
895 ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
896
897 // non-required attributes do not cause cycle
898 if (!definitionAttribute.getRequired())
899 {
900 unresolved.remove(definitionAttribute);
901 }
902 }
903
904 // check if still unresolved attributes
905 if (unresolved.size() == unresolvedSize)
906 {
907 // construct exception message
908 String message = createUnresolvedAttributesMessage(descriptor, unresolved);
909
910 // throw exception
911 throw new AssemblyException(message.toString());
912 }
913 }
914 }
915 }
916
917 /**
918 * Determines the message topic referenced by the supplied message.
919 *
920 * @param descriptor The {@link ComponentDescriptor} for the component.
921 * @param definitionMessage The {@link ComponentMessage} of the message definition.
922 * @param resolveMessage The {@link ComponentMessage} containing the message to resolve.
923 * @param unresolved The list of currently unresolved messages.
924 *
925 */
926
927 private void mapMessage(
928 ComponentDescriptor descriptor,
929 ComponentMessage definitionMessage, ComponentMessage resolveMessage,
930 Collection unresolved)
931 {
932 // get name of message, 'topic' contains literal part of mapped message
933 String messageName =
934 ((MultiPartValue.Part)resolveMessage.getTopic().getParts().iterator().next()).getValue();
935
936 // get predefined message from descriptor
937 ComponentDescriptor.Message mappedMessage = descriptor.getMessage(messageName);
938
939 // check if message has topic
940 if (mappedMessage != null)
941 {
942 // copy message topic
943 descriptor.addMessage(
944 definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
945 mappedMessage.getTopic(), definitionMessage.getSignature());
946
947 // remove message from undefined list
948 unresolved.remove(definitionMessage);
949 }
950 }
951
952 /**
953 * Creates and stores message topic values in the {@link ComponentDescriptor}.
954 * Any attributes referenced by the {@link MultiPartValue} are taken from the
955 * attributes in the {@link ComponentDescriptor}. Any messages referenced are
956 * taken from those in the {@link ComponentDescriptor}.
957 *
958 * @param definition The {@link ComponentDefinition} for the component.
959 * @param instance The {@link ComponentInstance} for the component.
960 * @param descriptor The {@link ComponentDescriptor} for the component.
961 *
962 * @exception AssemblyException Thrown when a problem occurs during assembly.
963 *
964 */
965
966 private void assembleComponentMessages(
967 ComponentDefinition definition, ComponentInstance instance, ComponentDescriptor descriptor)
968 throws AssemblyException
969 {
970 Collection unresolved = new ArrayList(definition.getMessages());
971
972 // continue looping through messages until all have been resolved or cycle has been detected
973 while (unresolved.size() > 0)
974 {
975 // pass through the message and set any that can be resolved
976 Collection iterationCollection = new ArrayList(unresolved);
977 Iterator messageIterator = definition.getMessages().iterator();
978
979 int unresolvedSize = unresolved.size();
980
981 while (messageIterator.hasNext())
982 {
983 // get message definitions
984 ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
985 ComponentMessage instanceMessage = instance.getMessage(definitionMessage.getName());
986
987 // check if instance provides value
988 if (instanceMessage != null)
989 {
990 // use topic specified by instance
991
992 // check if mapped message
993 if (instanceMessage.isMapped())
994 {
995 // map to the message specified in the instance message
996 mapMessage(descriptor, definitionMessage, instanceMessage, unresolved);
997 }
998 else
999 // use value of instance
1000 {
1001 // set value in descriptor
1002 String value = assemblePropertyValue(
1003 descriptor, instanceMessage.getName(), instanceMessage.getTopic());
1004
1005 descriptor.addMessage(
1006 definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
1007 value, definitionMessage.getSignature());
1008
1009 // remove message from undefined list
1010 unresolved.remove(definitionMessage);
1011 }
1012 }
1013 else
1014 {
1015 // check if topic already defined (by parent or composite)
1016 if (descriptor.getMessage(definitionMessage.getName()) != null)
1017 {
1018 // the value has been specified (typically by composite)
1019
1020 // remove message from undefined list
1021 unresolved.remove(definitionMessage);
1022 }
1023 // use topic specified by definition, if any
1024 else
1025 {
1026 // check if mapped message
1027 if (definitionMessage.isMapped())
1028 {
1029 // map to the message specified in the definition message
1030 mapMessage(descriptor, definitionMessage, definitionMessage, unresolved);
1031 }
1032 else
1033 {
1034 // check if default topic is available in definition
1035 MultiPartValue defaultTopic = definitionMessage.getTopic();
1036
1037 // if available, override any previously defined topic (ie. composite)
1038 if (defaultTopic.getParts().size() > 0)
1039 {
1040 // set value in descriptor
1041 String value = assemblePropertyValue(
1042 descriptor, definitionMessage.getName(), definitionMessage.getTopic());
1043
1044 descriptor.addMessage(
1045 definitionMessage.getName(), definitionMessage.getType(), definitionMessage.getAccess(),
1046 value, definitionMessage.getSignature());
1047
1048 // remove message from undefined list
1049 unresolved.remove(definitionMessage);
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 // check for message mapping cycle
1057 if (unresolved.size() == unresolvedSize)
1058 {
1059 // remove any non-required messages
1060 iterationCollection = new ArrayList(unresolved);
1061 messageIterator = iterationCollection.iterator();
1062
1063 while (messageIterator.hasNext())
1064 {
1065 ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
1066
1067 // non-required messages do not cause cycle
1068 if (!definitionMessage.getRequired())
1069 {
1070 unresolved.remove(definitionMessage);
1071 }
1072 }
1073
1074 // check if still unresolved messages
1075 if (unresolved.size() == unresolvedSize)
1076 {
1077 // construct exception message
1078 String message = createUnresolvedMessageTopicsMessage(descriptor, unresolved);
1079
1080 // throw exception
1081 throw new AssemblyException(message.toString());
1082 }
1083 }
1084 }
1085 }
1086
1087 /**
1088 * Creates the specified component from the {@link ComponentInstance} and {@link ComponentDescriptor}.
1089 *
1090 * @param instance The {@link ComponentInstance} to assemble.
1091 * @param definition The {@link ComponentDefinition} of the component.
1092 * @param descriptor The {@link ComponentDescriptor} to store information.
1093 *
1094 * @exception AssemblyException Thrown when a problem occurs during assembly.
1095 *
1096 */
1097
1098 private void createComponent(ComponentInstance instance, ComponentDefinition definition, ComponentDescriptor descriptor)
1099 throws AssemblyException
1100 {
1101 try
1102 {
1103 // get component class
1104 Class componentClass = Class.forName(definition.getBackingClass());
1105
1106 // create instance
1107 AssembledComponent componentInstance = (AssembledComponent)componentClass.newInstance();
1108
1109 // provide descriptor to component
1110 componentInstance.createComponent(context, descriptor);
1111 }
1112 catch (ClassNotFoundException exception)
1113 {
1114 throw new AssemblyException(exception);
1115 }
1116 catch (InstantiationException exception)
1117 {
1118 throw new AssemblyException(exception);
1119 }
1120 catch (IllegalAccessException exception)
1121 {
1122 throw new AssemblyException(exception);
1123 }
1124 }
1125
1126 /**
1127 * Stores the information of the base definition into the full definition.
1128 *
1129 * @param baseDefinition The {@link ComponentDefinition} to retrieve data from.
1130 * @param fullDefinition The {@link ComponentDefinition} to store data into.
1131 *
1132 */
1133
1134 private void assembleDefinition(ComponentDefinition baseDefinition, ComponentDefinition fullDefinition)
1135 {
1136 // parents are copied into full definition first, children override parent
1137
1138 // copy parent values
1139 Iterator parents = baseDefinition.getParents().iterator();
1140
1141 while (parents.hasNext())
1142 {
1143 // get name of definition
1144 String name = (String)parents.next();
1145
1146 // add as parent
1147 fullDefinition.addParent(name);
1148
1149 // get component definition from assembly descriptor
1150 ComponentDefinition definition = assembly.getDefinition(name);
1151
1152 // add parent's information to full definition
1153 assembleDefinition(definition, fullDefinition);
1154 }
1155
1156 // copy attributes
1157 Iterator attributeIterator = baseDefinition.getAttributes().iterator();
1158
1159 while (attributeIterator.hasNext())
1160 {
1161 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
1162
1163 fullDefinition.addAttribute(attribute);
1164 }
1165
1166 // copy messages
1167 Iterator messageIterator = baseDefinition.getMessages().iterator();
1168
1169 while (messageIterator.hasNext())
1170 {
1171 ComponentMessage message = (ComponentMessage)messageIterator.next();
1172
1173 fullDefinition.addMessage(message);
1174 }
1175
1176 // copy components
1177 Iterator componentIterator = baseDefinition.getComponents().iterator();
1178
1179 while (componentIterator.hasNext())
1180 {
1181 ComponentInstance instance = (ComponentInstance)componentIterator.next();
1182
1183 fullDefinition.addComponent(instance);
1184 }
1185 }
1186
1187 /**
1188 * Copies the attributes and messages of the source definition into the destination definition.
1189 * The source definition becomes an implicit parent of the destination definition.
1190 *
1191 * @param source The source {@link ComponentDefinition}.
1192 * @param destination The destination {@link ComponentDefinition}.
1193 *
1194 * @exception AssemblyException Thrown when a problem occurs during assembly.
1195 *
1196 */
1197
1198 private void includeDefinition(ComponentDefinition source, ComponentDefinition destination)
1199 throws AssemblyException
1200 {
1201 // source definition as parent of definition
1202 if (source.getName() != null)
1203 {
1204 destination.addParent(source.getName());
1205 }
1206
1207 // add parents
1208 Iterator parentIterator = source.getParents().iterator();
1209
1210 while (parentIterator.hasNext())
1211 {
1212 String parent = (String)parentIterator.next();
1213 destination.addParent(parent);
1214 }
1215
1216 // add attributes
1217 Iterator attributeIterator = source.getAttributes().iterator();
1218
1219 while (attributeIterator.hasNext())
1220 {
1221 ComponentAttribute attribute = (ComponentAttribute)attributeIterator.next();
1222 destination.addAttribute(attribute);
1223 }
1224
1225 // add messages
1226 Iterator messageIterator = source.getMessages().iterator();
1227
1228 while (messageIterator.hasNext())
1229 {
1230 ComponentMessage message = (ComponentMessage)messageIterator.next();
1231 destination.addMessage(message);
1232 }
1233 }
1234
1235 /**
1236 * Creates a new descriptor containing only attributes and messages of the
1237 * specified definition. Elliminates temporary properties built during assembly.
1238 * Whenever possible, simple references (no namespace) to attributes and messages
1239 * are created.
1240 *
1241 * @param definition The instance {@link ComponentDefinition}.
1242 * @param descriptor The {@link ComponentDescriptor} containing final values.
1243 *
1244 * @return A descriptor containing only those properties in the definition.
1245 *
1246 * @exception AssemblyException Thrown when a problem occurs during assembly.
1247 *
1248 */
1249
1250 private ComponentDescriptor createDefinitionDescriptor(ComponentDefinition specification, ComponentDescriptor descriptor)
1251 throws AssemblyException
1252 {
1253 // create basic definition (do not include owning composite)
1254 ComponentDefinition definition = new ComponentDefinition();
1255
1256 definition.setName(specification.getName());
1257 assembleDefinition(specification, definition);
1258
1259 // create empty descriptor
1260 ComponentDescriptor definitionDescriptor = new ComponentDescriptor();
1261
1262 // add attributes
1263 PropertyResolver attributeResolver = new AttributeResolver();
1264 Iterator attributeIterator = definition.getAttributes().iterator();
1265
1266 while (attributeIterator.hasNext())
1267 {
1268 ComponentAttribute definitionAttribute = (ComponentAttribute)attributeIterator.next();
1269
1270 ComponentDescriptor.Attribute descriptorAttribute =
1271 (ComponentDescriptor.Attribute)descriptor.getAttribute(definitionAttribute.getName());
1272
1273 // if no value provided, then skip attribute
1274 if (descriptorAttribute == null)
1275 {
1276 continue;
1277 }
1278
1279 // add attribute
1280 definitionDescriptor.addAttribute(
1281 descriptorAttribute.getName(), descriptorAttribute.getAccess(), descriptorAttribute.getValue());
1282
1283 // consider simple name
1284 String simple = descriptorAttribute.getName().substring(descriptorAttribute.getName().indexOf('.') + 1);
1285
1286 try
1287 {
1288 // check if simple is immediately available
1289 String resolved = resolveProperty(definition, simple, attributeResolver, "");
1290
1291 // if resolved is same, then a simple reference is valid
1292 if (descriptorAttribute.getName().equals(resolved))
1293 {
1294 // add attribute as simple name
1295 definitionDescriptor.addAttribute(
1296 simple, descriptorAttribute.getAccess(), descriptorAttribute.getValue());
1297 }
1298 }
1299 catch (AssemblyException exception)
1300 {
1301 // several attributes having simple name were found, can not add as simple
1302 }
1303 }
1304
1305 // add messages
1306 PropertyResolver messageResolver = new MessageResolver();
1307 Iterator messageIterator = definition.getMessages().iterator();
1308
1309 while (messageIterator.hasNext())
1310 {
1311 ComponentMessage definitionMessage = (ComponentMessage)messageIterator.next();
1312
1313 ComponentDescriptor.Message descriptorMessage =
1314 (ComponentDescriptor.Message)descriptor.getMessage(definitionMessage.getName());
1315
1316 // if no value provided, then skip message
1317 if (descriptorMessage == null)
1318 {
1319 continue;
1320 }
1321
1322 // add message
1323 definitionDescriptor.addMessage(
1324 descriptorMessage.getName(), descriptorMessage.getType(), descriptorMessage.getAccess(),
1325 descriptorMessage.getTopic(), descriptorMessage.getSignature());
1326
1327 // consider simple name
1328 String simple = descriptorMessage.getName().substring(descriptorMessage.getName().indexOf('.') + 1);
1329
1330 try
1331 {
1332 // check if simple is immediately available
1333 String resolved = resolveProperty(definition, simple, messageResolver, "");
1334
1335 // if resolved is same, then a simple reference is valid
1336 if (descriptorMessage.getName().equals(resolved))
1337 {
1338 // add attribute as simple name
1339 definitionDescriptor.addMessage(
1340 simple, descriptorMessage.getType(), descriptorMessage.getAccess(),
1341 descriptorMessage.getTopic(), descriptorMessage.getSignature());
1342 }
1343 }
1344 catch (AssemblyException exception)
1345 {
1346 // several messages having simple name were found, can not add as simple
1347 }
1348 }
1349
1350 return definitionDescriptor;
1351 }
1352
1353 /**
1354 * Assembles the specified component from the {@link ComponentInstance}.
1355 *
1356 * @param instanceNamespace The prefix to uniquely identify this component instance.
1357 * @param instance The {@link ComponentInstance} to assemble.
1358 * @param ownerDefinition The {@link ComponentDefinition} of the owning composite.
1359 * @param ownerDescriptor The {@link ComponentDescriptor} of the owning composite.
1360 *
1361 * @exception AssemblyException Thrown when a problem occurs during assembly.
1362 *
1363 */
1364
1365 private void assembleComponent(
1366 String instanceNamespace, ComponentInstance instance,
1367 ComponentDefinition ownerDefinition, ComponentDescriptor ownerDescriptor)
1368 throws AssemblyException
1369 {
1370 // check for instance name
1371 if (instance.getName() == null)
1372 {
1373 // construct exception message
1374 String message = createUndefinedInstanceName(instance.getDefinition());
1375
1376 // throw exception
1377 throw new AssemblyException(message.toString());
1378 }
1379
1380 // get component definition
1381 ComponentDefinition specification = assembly.getDefinition(instance.getDefinition());
1382
1383 if (specification == null)
1384 {
1385 // construct exception message
1386 String message = createUndefinedComponentDefinitionMessage(instance.getName(), instance.getDefinition());
1387
1388 // throw exception
1389 throw new AssemblyException(message.toString());
1390 }
1391
1392 // create full definition
1393 ComponentDefinition definition = new ComponentDefinition();
1394
1395 // can't use copy constructor since sub-components would be included
1396 // and assembleDefinition() would include them a second time
1397
1398 // copy basic information
1399 definition.setName(specification.getName());
1400 definition.setBackingClass(specification.getBackingClass());
1401
1402 // copy specification parents
1403 Iterator parentIterator = specification.getParents().iterator();
1404
1405 while (parentIterator.hasNext())
1406 {
1407 String parent = (String)parentIterator.next();
1408 definition.addParent(parent);
1409 }
1410
1411 // include owning composite, heritage of owner has already been built
1412 // only copy parents, attributes, and messages, ignore components
1413 includeDefinition(ownerDefinition, definition);
1414
1415 // include heritage
1416 assembleDefinition(specification, definition);
1417