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