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