1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.shiro.config;
20
21 import java.beans.PropertyDescriptor;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.LinkedHashMap;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import org.apache.commons.beanutils.BeanUtilsBean;
32 import org.apache.commons.beanutils.ConvertUtilsBean;
33 import org.apache.commons.beanutils.SuppressPropertiesBeanIntrospector;
34 import org.apache.shiro.codec.Base64;
35 import org.apache.shiro.codec.Hex;
36 import org.apache.shiro.config.event.BeanEvent;
37 import org.apache.shiro.config.event.ConfiguredBeanEvent;
38 import org.apache.shiro.config.event.DestroyedBeanEvent;
39 import org.apache.shiro.config.event.InitializedBeanEvent;
40 import org.apache.shiro.config.event.InstantiatedBeanEvent;
41 import org.apache.shiro.event.EventBus;
42 import org.apache.shiro.event.EventBusAware;
43 import org.apache.shiro.event.Subscribe;
44 import org.apache.shiro.event.support.DefaultEventBus;
45 import org.apache.shiro.util.Assert;
46 import org.apache.shiro.util.ByteSource;
47 import org.apache.shiro.util.ClassUtils;
48 import org.apache.shiro.util.Factory;
49 import org.apache.shiro.util.LifecycleUtils;
50 import org.apache.shiro.util.Nameable;
51 import org.apache.shiro.util.StringUtils;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60
61
62
63
64
65
66 public class ReflectionBuilder {
67
68
69
70 private static final Logger log = LoggerFactory.getLogger(ReflectionBuilder.class);
71
72 private static final String OBJECT_REFERENCE_BEGIN_TOKEN = "$";
73 private static final String ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$";
74 private static final String GLOBAL_PROPERTY_PREFIX = "shiro";
75 private static final char MAP_KEY_VALUE_DELIMITER = ':';
76 private static final String HEX_BEGIN_TOKEN = "0x";
77 private static final String NULL_VALUE_TOKEN = "null";
78 private static final String EMPTY_STRING_VALUE_TOKEN = "\"\"";
79 private static final char STRING_VALUE_DELIMETER = '"';
80 private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';
81 private static final char MAP_PROPERTY_END_TOKEN = ']';
82
83 private static final String EVENT_BUS_NAME = "eventBus";
84
85 private final Map<String, Object> objects;
86
87
88
89
90
91 private Interpolator interpolator;
92
93
94
95
96 private EventBus eventBus;
97
98
99
100
101
102
103
104
105 private final Map<String,Object> registeredEventSubscribers;
106
107
108
109
110 private final BeanUtilsBean beanUtilsBean;
111
112
113 private Map<String,Object> createDefaultObjectMap() {
114 Map<String,Object> map = new LinkedHashMap<String, Object>();
115 map.put(EVENT_BUS_NAME, new DefaultEventBus());
116 return map;
117 }
118
119 public ReflectionBuilder() {
120 this(null);
121 }
122
123 public ReflectionBuilder(Map<String, ?> defaults) {
124
125
126
127 beanUtilsBean = new BeanUtilsBean(new ConvertUtilsBean() {
128 @Override
129 public Object convert(String value, Class clazz) {
130 if (clazz.isEnum()){
131 return Enum.valueOf(clazz, value);
132 }else{
133 return super.convert(value, clazz);
134 }
135 }
136 });
137 beanUtilsBean.getPropertyUtils().addBeanIntrospector(SuppressPropertiesBeanIntrospector.SUPPRESS_CLASS);
138
139 this.interpolator = createInterpolator();
140
141 this.objects = createDefaultObjectMap();
142 this.registeredEventSubscribers = new LinkedHashMap<String,Object>();
143 apply(defaults);
144 }
145
146 private void apply(Map<String, ?> objects) {
147 if(!isEmpty(objects)) {
148 this.objects.putAll(objects);
149 }
150 EventBus found = findEventBus(this.objects);
151 Assert.notNull(found, "An " + EventBus.class.getName() + " instance must be present in the object defaults");
152 enableEvents(found);
153 }
154
155 public Map<String, ?> getObjects() {
156 return objects;
157 }
158
159
160
161
162 public void setObjects(Map<String, ?> objects) {
163 this.objects.clear();
164 this.objects.putAll(createDefaultObjectMap());
165 apply(objects);
166 }
167
168
169 private void enableEvents(EventBus eventBus) {
170 Assert.notNull(eventBus, "EventBus argument cannot be null.");
171
172 for (Object subscriber : this.registeredEventSubscribers.values()) {
173 this.eventBus.unregister(subscriber);
174 }
175 this.registeredEventSubscribers.clear();
176
177 this.eventBus = eventBus;
178
179 for(Map.Entry<String,Object> entry : this.objects.entrySet()) {
180 enableEventsIfNecessary(entry.getValue(), entry.getKey());
181 }
182 }
183
184
185 private void enableEventsIfNecessary(Object bean, String name) {
186 boolean applied = applyEventBusIfNecessary(bean);
187 if (!applied) {
188
189
190
191
192
193 if (isEventSubscriber(bean, name)) {
194
195 this.eventBus.register(bean);
196 this.registeredEventSubscribers.put(name, bean);
197 }
198 }
199 }
200
201
202 private boolean isEventSubscriber(Object bean, String name) {
203 List annotatedMethods = ClassUtils.getAnnotatedMethods(bean.getClass(), Subscribe.class);
204 return !isEmpty(annotatedMethods);
205 }
206
207
208 protected EventBus findEventBus(Map<String,?> objects) {
209
210 if (isEmpty(objects)) {
211 return null;
212 }
213
214
215 Object value = objects.get(EVENT_BUS_NAME);
216 if (value != null && value instanceof EventBus) {
217 return (EventBus)value;
218 }
219
220
221 for( Object v : objects.values()) {
222 if (v instanceof EventBus) {
223 return (EventBus)v;
224 }
225 }
226
227 return null;
228 }
229
230 private boolean applyEventBusIfNecessary(Object value) {
231 if (value instanceof EventBusAware) {
232 ((EventBusAware)value).setEventBus(this.eventBus);
233 return true;
234 }
235 return false;
236 }
237
238 public Object getBean(String id) {
239 return objects.get(id);
240 }
241
242 @SuppressWarnings({"unchecked"})
243 public <T> T getBean(String id, Class<T> requiredType) {
244 if (requiredType == null) {
245 throw new NullPointerException("requiredType argument cannot be null.");
246 }
247 Object bean = getBean(id);
248 if (bean == null) {
249 return null;
250 }
251 Assert.state(requiredType.isAssignableFrom(bean.getClass()),
252 "Bean with id [" + id + "] is not of the required type [" + requiredType.getName() + "].");
253 return (T) bean;
254 }
255
256 private String parseBeanId(String lhs) {
257 Assert.notNull(lhs);
258 if (lhs.indexOf('.') < 0) {
259 return lhs;
260 }
261 String classSuffix = ".class";
262 int index = lhs.indexOf(classSuffix);
263 if (index >= 0) {
264 return lhs.substring(0, index);
265 }
266 return null;
267 }
268
269 @SuppressWarnings({"unchecked"})
270 public Map<String, ?> buildObjects(Map<String, String> kvPairs) {
271
272 if (kvPairs != null && !kvPairs.isEmpty()) {
273
274 BeanConfigurationProcessor processor = new BeanConfigurationProcessor();
275
276 for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
277 String lhs = entry.getKey();
278 String rhs = interpolator.interpolate(entry.getValue());
279
280 String beanId = parseBeanId(lhs);
281 if (beanId != null) {
282 processor.add(new InstantiationStatement(beanId, rhs));
283 } else {
284 processor.add(new AssignmentStatement(lhs, rhs));
285 }
286 }
287
288 processor.execute();
289 }
290
291
292 LifecycleUtils.init(objects.values());
293
294 return objects;
295 }
296
297 public void destroy() {
298 final Map<String, Object> immutableObjects = Collections.unmodifiableMap(objects);
299
300
301 List<Map.Entry<String,?>> entries = new ArrayList<Map.Entry<String,?>>(objects.entrySet());
302 Collections.reverse(entries);
303
304 for(Map.Entry<String, ?> entry: entries) {
305 String id = entry.getKey();
306 Object bean = entry.getValue();
307
308
309 if (bean != this.eventBus) {
310 LifecycleUtils.destroy(bean);
311 BeanEvent event = new DestroyedBeanEvent(id, bean, immutableObjects);
312 eventBus.publish(event);
313 this.eventBus.unregister(bean);
314 }
315 }
316
317 LifecycleUtils.destroy(this.eventBus);
318 }
319
320 protected void createNewInstance(Map<String, Object> objects, String name, String value) {
321
322 Object currentInstance = objects.get(name);
323 if (currentInstance != null) {
324 log.info("An instance with name '{}' already exists. " +
325 "Redefining this object as a new instance of type {}", name, value);
326 }
327
328 Object instance;
329 try {
330 instance = ClassUtils.newInstance(value);
331 if (instance instanceof Nameable) {
332 ((Nameable) instance).setName(name);
333 }
334 } catch (Exception e) {
335 String msg = "Unable to instantiate class [" + value + "] for object named '" + name + "'. " +
336 "Please ensure you've specified the fully qualified class name correctly.";
337 throw new ConfigurationException(msg, e);
338 }
339 objects.put(name, instance);
340 }
341
342 protected void applyProperty(String key, String value, Map objects) {
343
344 int index = key.indexOf('.');
345
346 if (index >= 0) {
347 String name = key.substring(0, index);
348 String property = key.substring(index + 1, key.length());
349
350 if (GLOBAL_PROPERTY_PREFIX.equalsIgnoreCase(name)) {
351 applyGlobalProperty(objects, property, value);
352 } else {
353 applySingleProperty(objects, name, property, value);
354 }
355
356 } else {
357 throw new IllegalArgumentException("All property keys must contain a '.' character. " +
358 "(e.g. myBean.property = value) These should already be separated out by buildObjects().");
359 }
360 }
361
362 protected void applyGlobalProperty(Map objects, String property, String value) {
363 for (Object instance : objects.values()) {
364 try {
365 PropertyDescriptor pd = beanUtilsBean.getPropertyUtils().getPropertyDescriptor(instance, property);
366 if (pd != null) {
367 applyProperty(instance, property, value);
368 }
369 } catch (Exception e) {
370 String msg = "Error retrieving property descriptor for instance " +
371 "of type [" + instance.getClass().getName() + "] " +
372 "while setting property [" + property + "]";
373 throw new ConfigurationException(msg, e);
374 }
375 }
376 }
377
378 protected void applySingleProperty(Map objects, String name, String property, String value) {
379 Object instance = objects.get(name);
380 if (property.equals("class")) {
381 throw new IllegalArgumentException("Property keys should not contain 'class' properties since these " +
382 "should already be separated out by buildObjects().");
383
384 } else if (instance == null) {
385 String msg = "Configuration error. Specified object [" + name + "] with property [" +
386 property + "] without first defining that object's class. Please first " +
387 "specify the class property first, e.g. myObject = fully_qualified_class_name " +
388 "and then define additional properties.";
389 throw new IllegalArgumentException(msg);
390
391 } else {
392 applyProperty(instance, property, value);
393 }
394 }
395
396 protected boolean isReference(String value) {
397 return value != null && value.startsWith(OBJECT_REFERENCE_BEGIN_TOKEN);
398 }
399
400 protected String getId(String referenceToken) {
401 return referenceToken.substring(OBJECT_REFERENCE_BEGIN_TOKEN.length());
402 }
403
404 protected Object getReferencedObject(String id) {
405 Object o = objects != null && !objects.isEmpty() ? objects.get(id) : null;
406 if (o == null) {
407 String msg = "The object with id [" + id + "] has not yet been defined and therefore cannot be " +
408 "referenced. Please ensure objects are defined in the order in which they should be " +
409 "created and made available for future reference.";
410 throw new UnresolveableReferenceException(msg);
411 }
412 return o;
413 }
414
415 protected String unescapeIfNecessary(String value) {
416 if (value != null && value.startsWith(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN)) {
417 return value.substring(ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN.length() - 1);
418 }
419 return value;
420 }
421
422 protected Object resolveReference(String reference) {
423 String id = getId(reference);
424 log.debug("Encountered object reference '{}'. Looking up object with id '{}'", reference, id);
425 final Object referencedObject = getReferencedObject(id);
426 if (referencedObject instanceof Factory) {
427 return ((Factory) referencedObject).getInstance();
428 }
429 return referencedObject;
430 }
431
432 protected boolean isTypedProperty(Object object, String propertyName, Class clazz) {
433 if (clazz == null) {
434 throw new NullPointerException("type (class) argument cannot be null.");
435 }
436 try {
437 PropertyDescriptor descriptor = beanUtilsBean.getPropertyUtils().getPropertyDescriptor(object, propertyName);
438 if (descriptor == null) {
439 String msg = "Property '" + propertyName + "' does not exist for object of " +
440 "type " + object.getClass().getName() + ".";
441 throw new ConfigurationException(msg);
442 }
443 Class propertyClazz = descriptor.getPropertyType();
444 return clazz.isAssignableFrom(propertyClazz);
445 } catch (ConfigurationException ce) {
446
447 throw ce;
448 } catch (Exception e) {
449 String msg = "Unable to determine if property [" + propertyName + "] represents a " + clazz.getName();
450 throw new ConfigurationException(msg, e);
451 }
452 }
453
454 protected Set<?> toSet(String sValue) {
455 String[] tokens = StringUtils.split(sValue);
456 if (tokens == null || tokens.length <= 0) {
457 return null;
458 }
459
460
461 if (tokens.length == 1 && isReference(tokens[0])) {
462 Object reference = resolveReference(tokens[0]);
463 if (reference instanceof Set) {
464 return (Set)reference;
465 }
466 }
467
468 Set<String> setTokens = new LinkedHashSet<String>(Arrays.asList(tokens));
469
470
471 Set<Object> values = new LinkedHashSet<Object>(setTokens.size());
472 for (String token : setTokens) {
473 Object value = resolveValue(token);
474 values.add(value);
475 }
476 return values;
477 }
478
479 protected Map<?, ?> toMap(String sValue) {
480 String[] tokens = StringUtils.split(sValue, StringUtils.DEFAULT_DELIMITER_CHAR,
481 StringUtils.DEFAULT_QUOTE_CHAR, StringUtils.DEFAULT_QUOTE_CHAR, true, true);
482 if (tokens == null || tokens.length <= 0) {
483 return null;
484 }
485
486
487 if (tokens.length == 1 && isReference(tokens[0])) {
488 Object reference = resolveReference(tokens[0]);
489 if (reference instanceof Map) {
490 return (Map)reference;
491 }
492 }
493
494 Map<String, String> mapTokens = new LinkedHashMap<String, String>(tokens.length);
495 for (String token : tokens) {
496 String[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER);
497 if (kvPair == null || kvPair.length != 2) {
498 String msg = "Map property value [" + sValue + "] contained key-value pair token [" +
499 token + "] that does not properly split to a single key and pair. This must be the " +
500 "case for all map entries.";
501 throw new ConfigurationException(msg);
502 }
503 mapTokens.put(kvPair[0], kvPair[1]);
504 }
505
506
507 Map<Object, Object> map = new LinkedHashMap<Object, Object>(mapTokens.size());
508 for (Map.Entry<String, String> entry : mapTokens.entrySet()) {
509 Object key = resolveValue(entry.getKey());
510 Object value = resolveValue(entry.getValue());
511 map.put(key, value);
512 }
513 return map;
514 }
515
516
517 protected Collection<?> toCollection(String sValue) {
518
519 String[] tokens = StringUtils.split(sValue);
520 if (tokens == null || tokens.length <= 0) {
521 return null;
522 }
523
524
525 if (tokens.length == 1 && isReference(tokens[0])) {
526 Object reference = resolveReference(tokens[0]);
527 if (reference instanceof Collection) {
528 return (Collection)reference;
529 }
530 }
531
532
533 List<Object> values = new ArrayList<Object>(tokens.length);
534 for (String token : tokens) {
535 Object value = resolveValue(token);
536 values.add(value);
537 }
538 return values;
539 }
540
541 protected List<?> toList(String sValue) {
542 String[] tokens = StringUtils.split(sValue);
543 if (tokens == null || tokens.length <= 0) {
544 return null;
545 }
546
547
548 if (tokens.length == 1 && isReference(tokens[0])) {
549 Object reference = resolveReference(tokens[0]);
550 if (reference instanceof List) {
551 return (List)reference;
552 }
553 }
554
555
556 List<Object> values = new ArrayList<Object>(tokens.length);
557 for (String token : tokens) {
558 Object value = resolveValue(token);
559 values.add(value);
560 }
561 return values;
562 }
563
564 protected byte[] toBytes(String sValue) {
565 if (sValue == null) {
566 return null;
567 }
568 byte[] bytes;
569 if (sValue.startsWith(HEX_BEGIN_TOKEN)) {
570 String hex = sValue.substring(HEX_BEGIN_TOKEN.length());
571 bytes = Hex.decode(hex);
572 } else {
573
574 bytes = Base64.decode(sValue);
575 }
576 return bytes;
577 }
578
579 protected Object resolveValue(String stringValue) {
580 Object value;
581 if (isReference(stringValue)) {
582 value = resolveReference(stringValue);
583 } else {
584 value = unescapeIfNecessary(stringValue);
585 }
586 return value;
587 }
588
589 protected String checkForNullOrEmptyLiteral(String stringValue) {
590 if (stringValue == null) {
591 return null;
592 }
593
594 if (stringValue.equals("\"null\"")) {
595 return NULL_VALUE_TOKEN;
596 }
597
598 else if (stringValue.equals("\"\"\"\"")) {
599 return EMPTY_STRING_VALUE_TOKEN;
600 } else {
601 return stringValue;
602 }
603 }
604
605 protected void applyProperty(Object object, String propertyPath, Object value) {
606
607 int mapBegin = propertyPath.indexOf(MAP_PROPERTY_BEGIN_TOKEN);
608 int mapEnd = -1;
609 String mapPropertyPath = null;
610 String keyString = null;
611
612 String remaining = null;
613
614 if (mapBegin >= 0) {
615
616 mapPropertyPath = propertyPath.substring(0, mapBegin);
617
618 mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin);
619
620 keyString = propertyPath.substring(mapBegin+1, mapEnd);
621
622
623 if (propertyPath.length() > (mapEnd+1)) {
624 remaining = propertyPath.substring(mapEnd+1);
625 if (remaining.startsWith(".")) {
626 remaining = StringUtils.clean(remaining.substring(1));
627 }
628 }
629 }
630
631 if (remaining == null) {
632
633 if (keyString == null) {
634
635 setProperty(object, propertyPath, value);
636 } else {
637
638 if (isTypedProperty(object, mapPropertyPath, Map.class)) {
639 Map map = (Map)getProperty(object, mapPropertyPath);
640 Object mapKey = resolveValue(keyString);
641
642 map.put(mapKey, value);
643 } else {
644
645 int index = Integer.valueOf(keyString);
646 setIndexedProperty(object, mapPropertyPath, index, value);
647 }
648 }
649 } else {
650
651
652 Object referencedValue = null;
653 if (isTypedProperty(object, mapPropertyPath, Map.class)) {
654 Map map = (Map)getProperty(object, mapPropertyPath);
655 Object mapKey = resolveValue(keyString);
656 referencedValue = map.get(mapKey);
657 } else {
658
659 int index = Integer.valueOf(keyString);
660 referencedValue = getIndexedProperty(object, mapPropertyPath, index);
661 }
662
663 if (referencedValue == null) {
664 throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "[" +
665 keyString + "]' does not exist.");
666 }
667
668 applyProperty(referencedValue, remaining, value);
669 }
670 }
671
672 private void setProperty(Object object, String propertyPath, Object value) {
673 try {
674 if (log.isTraceEnabled()) {
675 log.trace("Applying property [{}] value [{}] on object of type [{}]",
676 new Object[]{propertyPath, value, object.getClass().getName()});
677 }
678 beanUtilsBean.setProperty(object, propertyPath, value);
679 } catch (Exception e) {
680 String msg = "Unable to set property '" + propertyPath + "' with value [" + value + "] on object " +
681 "of type " + (object != null ? object.getClass().getName() : null) + ". If " +
682 "'" + value + "' is a reference to another (previously defined) object, prefix it with " +
683 "'" + OBJECT_REFERENCE_BEGIN_TOKEN + "' to indicate that the referenced " +
684 "object should be used as the actual value. " +
685 "For example, " + OBJECT_REFERENCE_BEGIN_TOKEN + value;
686 throw new ConfigurationException(msg, e);
687 }
688 }
689
690 private Object getProperty(Object object, String propertyPath) {
691 try {
692 return beanUtilsBean.getPropertyUtils().getProperty(object, propertyPath);
693 } catch (Exception e) {
694 throw new ConfigurationException("Unable to access property '" + propertyPath + "'", e);
695 }
696 }
697
698 private void setIndexedProperty(Object object, String propertyPath, int index, Object value) {
699 try {
700 beanUtilsBean.getPropertyUtils().setIndexedProperty(object, propertyPath, index, value);
701 } catch (Exception e) {
702 throw new ConfigurationException("Unable to set array property '" + propertyPath + "'", e);
703 }
704 }
705
706 private Object getIndexedProperty(Object object, String propertyPath, int index) {
707 try {
708 return beanUtilsBean.getPropertyUtils().getIndexedProperty(object, propertyPath, index);
709 } catch (Exception e) {
710 throw new ConfigurationException("Unable to acquire array property '" + propertyPath + "'", e);
711 }
712 }
713
714 protected boolean isIndexedPropertyAssignment(String propertyPath) {
715 return propertyPath.endsWith("" + MAP_PROPERTY_END_TOKEN);
716 }
717
718 protected void applyProperty(Object object, String propertyName, String stringValue) {
719
720 Object value;
721
722 if (NULL_VALUE_TOKEN.equals(stringValue)) {
723 value = null;
724 } else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) {
725 value = StringUtils.EMPTY_STRING;
726 } else if (isIndexedPropertyAssignment(propertyName)) {
727 String checked = checkForNullOrEmptyLiteral(stringValue);
728 value = resolveValue(checked);
729 } else if (isTypedProperty(object, propertyName, Set.class)) {
730 value = toSet(stringValue);
731 } else if (isTypedProperty(object, propertyName, Map.class)) {
732 value = toMap(stringValue);
733 } else if (isTypedProperty(object, propertyName, List.class)) {
734 value = toList(stringValue);
735 } else if (isTypedProperty(object, propertyName, Collection.class)) {
736 value = toCollection(stringValue);
737 } else if (isTypedProperty(object, propertyName, byte[].class)) {
738 value = toBytes(stringValue);
739 } else if (isTypedProperty(object, propertyName, ByteSource.class)) {
740 byte[] bytes = toBytes(stringValue);
741 value = ByteSource.Util.bytes(bytes);
742 } else {
743 String checked = checkForNullOrEmptyLiteral(stringValue);
744 value = resolveValue(checked);
745 }
746
747 applyProperty(object, propertyName, value);
748 }
749
750 private Interpolator createInterpolator() {
751
752 if (ClassUtils.isAvailable("org.apache.commons.configuration2.interpol.ConfigurationInterpolator")) {
753 return new CommonsInterpolator();
754 }
755
756 return new DefaultInterpolator();
757 }
758
759
760
761
762
763 public void setInterpolator(Interpolator interpolator) {
764 this.interpolator = interpolator;
765 }
766
767 private class BeanConfigurationProcessor {
768
769 private final List<Statement> statements = new ArrayList<Statement>();
770 private final List<BeanConfiguration> beanConfigurations = new ArrayList<BeanConfiguration>();
771
772 public void add(Statement statement) {
773
774 statements.add(statement);
775
776 if (statement instanceof InstantiationStatement) {
777 InstantiationStatement is = (InstantiationStatement)statement;
778 beanConfigurations.add(new BeanConfiguration(is));
779 } else {
780 AssignmentStatement as = (AssignmentStatement)statement;
781
782
783 boolean addedToConfig = false;
784 String beanName = as.getRootBeanName();
785 for( int i = beanConfigurations.size()-1; i >= 0; i--) {
786 BeanConfiguration mostRecent = beanConfigurations.get(i);
787 String mostRecentBeanName = mostRecent.getBeanName();
788 if (beanName.equals(mostRecentBeanName)) {
789 mostRecent.add(as);
790 addedToConfig = true;
791 break;
792 }
793 }
794
795 if (!addedToConfig) {
796
797
798
799
800 beanConfigurations.add(new BeanConfiguration(as));
801 }
802 }
803 }
804
805 public void execute() {
806
807 for( Statement statement : statements) {
808
809 statement.execute();
810
811 BeanConfiguration bd = statement.getBeanConfiguration();
812
813 if (bd.isExecuted()) {
814
815
816 if (bd.getBeanName().equals(EVENT_BUS_NAME)) {
817 EventBus eventBus = (EventBus)bd.getBean();
818 enableEvents(eventBus);
819 }
820
821
822 if (!bd.isGlobalConfig()) {
823 BeanEvent event = new ConfiguredBeanEvent(bd.getBeanName(), bd.getBean(),
824 Collections.unmodifiableMap(objects));
825 eventBus.publish(event);
826 }
827
828
829 LifecycleUtils.init(bd.getBean());
830
831
832 if (!bd.isGlobalConfig()) {
833 BeanEvent event = new InitializedBeanEvent(bd.getBeanName(), bd.getBean(),
834 Collections.unmodifiableMap(objects));
835 eventBus.publish(event);
836 }
837 }
838 }
839 }
840 }
841
842 private class BeanConfiguration {
843
844 private final InstantiationStatement instantiationStatement;
845 private final List<AssignmentStatement> assignments = new ArrayList<AssignmentStatement>();
846 private final String beanName;
847 private Object bean;
848
849 private BeanConfiguration(InstantiationStatement statement) {
850 statement.setBeanConfiguration(this);
851 this.instantiationStatement = statement;
852 this.beanName = statement.lhs;
853 }
854
855 private BeanConfiguration(AssignmentStatement as) {
856 this.instantiationStatement = null;
857 this.beanName = as.getRootBeanName();
858 add(as);
859 }
860
861 public String getBeanName() {
862 return this.beanName;
863 }
864
865 public boolean isGlobalConfig() {
866
867 return GLOBAL_PROPERTY_PREFIX.equals(getBeanName());
868 }
869
870 public void add(AssignmentStatement as) {
871 as.setBeanConfiguration(this);
872 assignments.add(as);
873 }
874
875
876
877
878
879
880
881 public void setBean(Object bean) {
882 this.bean = bean;
883 }
884
885 public Object getBean() {
886 return this.bean;
887 }
888
889
890
891
892
893 public boolean isExecuted() {
894 if (instantiationStatement != null && !instantiationStatement.isExecuted()) {
895 return false;
896 }
897 for (AssignmentStatement as : assignments) {
898 if (!as.isExecuted()) {
899 return false;
900 }
901 }
902 return true;
903 }
904 }
905
906 private abstract class Statement {
907
908 protected final String lhs;
909 protected final String rhs;
910 protected Object bean;
911 private Object result;
912 private boolean executed;
913 private BeanConfiguration beanConfiguration;
914
915 private Statement(String lhs, String rhs) {
916 this.lhs = lhs;
917 this.rhs = rhs;
918 this.executed = false;
919 }
920
921 public void setBeanConfiguration(BeanConfiguration bd) {
922 this.beanConfiguration = bd;
923 }
924
925 public BeanConfiguration getBeanConfiguration() {
926 return this.beanConfiguration;
927 }
928
929 public Object execute() {
930 if (!isExecuted()) {
931 this.result = doExecute();
932 this.executed = true;
933 }
934 if (!getBeanConfiguration().isGlobalConfig()) {
935 Assert.notNull(this.bean, "Implementation must set the root bean for which it executed.");
936 }
937 return this.result;
938 }
939
940 public Object getBean() {
941 return this.bean;
942 }
943
944 protected void setBean(Object bean) {
945 this.bean = bean;
946 if (this.beanConfiguration.getBean() == null) {
947 this.beanConfiguration.setBean(bean);
948 }
949 }
950
951 public Object getResult() {
952 return result;
953 }
954
955 protected abstract Object doExecute();
956
957 public boolean isExecuted() {
958 return executed;
959 }
960 }
961
962 private class InstantiationStatement extends Statement {
963
964 private InstantiationStatement(String lhs, String rhs) {
965 super(lhs, rhs);
966 }
967
968 @Override
969 protected Object doExecute() {
970 String beanName = this.lhs;
971 createNewInstance(objects, beanName, this.rhs);
972 Object instantiated = objects.get(beanName);
973 setBean(instantiated);
974
975
976
977
978 enableEventsIfNecessary(instantiated, beanName);
979
980 BeanEvent event = new InstantiatedBeanEvent(beanName, instantiated, Collections.unmodifiableMap(objects));
981 eventBus.publish(event);
982
983 return instantiated;
984 }
985 }
986
987 private class AssignmentStatement extends Statement {
988
989 private final String rootBeanName;
990
991 private AssignmentStatement(String lhs, String rhs) {
992 super(lhs, rhs);
993 int index = lhs.indexOf('.');
994 this.rootBeanName = lhs.substring(0, index);
995 }
996
997 @Override
998 protected Object doExecute() {
999 applyProperty(lhs, rhs, objects);
1000 Object bean = objects.get(this.rootBeanName);
1001 setBean(bean);
1002 return null;
1003 }
1004
1005 public String getRootBeanName() {
1006 return this.rootBeanName;
1007 }
1008 }
1009
1010
1011
1012
1013
1014
1015 private static boolean isEmpty(Map m) {
1016 return m == null || m.isEmpty();
1017 }
1018
1019 private static boolean isEmpty(Collection c) {
1020 return c == null || c.isEmpty();
1021 }
1022
1023 }