1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.beans.factory.groovy;
18
19 import java.io.IOException;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import groovy.lang.Binding;
27 import groovy.lang.Closure;
28 import groovy.lang.GString;
29 import groovy.lang.GroovyObject;
30 import groovy.lang.GroovyObjectSupport;
31 import groovy.lang.GroovyShell;
32 import groovy.lang.GroovySystem;
33 import groovy.lang.MetaClass;
34
35 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
36 import org.codehaus.groovy.runtime.InvokerHelper;
37
38 import org.springframework.beans.MutablePropertyValues;
39 import org.springframework.beans.factory.BeanDefinitionStoreException;
40 import org.springframework.beans.factory.config.RuntimeBeanReference;
41 import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
42 import org.springframework.beans.factory.parsing.Location;
43 import org.springframework.beans.factory.parsing.Problem;
44 import org.springframework.beans.factory.support.AbstractBeanDefinition;
45 import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
46 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
47 import org.springframework.beans.factory.support.GenericBeanDefinition;
48 import org.springframework.beans.factory.support.ManagedList;
49 import org.springframework.beans.factory.support.ManagedMap;
50 import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
51 import org.springframework.beans.factory.xml.NamespaceHandler;
52 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
53 import org.springframework.beans.factory.xml.XmlReaderContext;
54 import org.springframework.core.io.DescriptiveResource;
55 import org.springframework.core.io.Resource;
56 import org.springframework.core.io.support.EncodedResource;
57 import org.springframework.util.ObjectUtils;
58 import org.springframework.util.StringUtils;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader implements GroovyObject {
134
135
136
137
138
139 private final XmlBeanDefinitionReader standardXmlBeanDefinitionReader;
140
141
142
143
144
145 private final XmlBeanDefinitionReader groovyDslXmlBeanDefinitionReader;
146
147 private final Map<String, String> namespaces = new HashMap<String, String>();
148
149 private final Map<String, DeferredProperty> deferredProperties = new HashMap<String, DeferredProperty>();
150
151 private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
152
153 private Binding binding;
154
155 private GroovyBeanDefinitionWrapper currentBeanDefinition;
156
157
158
159
160
161
162 public GroovyBeanDefinitionReader(BeanDefinitionRegistry registry) {
163 super(registry);
164 this.standardXmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
165 this.groovyDslXmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
166 this.groovyDslXmlBeanDefinitionReader.setValidating(false);
167 }
168
169
170
171
172
173
174
175 public GroovyBeanDefinitionReader(XmlBeanDefinitionReader xmlBeanDefinitionReader) {
176 super(xmlBeanDefinitionReader.getRegistry());
177 this.standardXmlBeanDefinitionReader = new XmlBeanDefinitionReader(xmlBeanDefinitionReader.getRegistry());
178 this.groovyDslXmlBeanDefinitionReader = xmlBeanDefinitionReader;
179 }
180
181
182 public void setMetaClass(MetaClass metaClass) {
183 this.metaClass = metaClass;
184 }
185
186 public MetaClass getMetaClass() {
187 return this.metaClass;
188 }
189
190
191
192
193
194 public void setBinding(Binding binding) {
195 this.binding = binding;
196 }
197
198
199
200
201 public Binding getBinding() {
202 return this.binding;
203 }
204
205
206
207
208
209
210
211
212
213
214
215
216 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
217 return loadBeanDefinitions(new EncodedResource(resource));
218 }
219
220
221
222
223
224
225
226
227
228
229 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
230
231 String filename = encodedResource.getResource().getFilename();
232 if (StringUtils.endsWithIgnoreCase(filename, ".xml")) {
233 return this.standardXmlBeanDefinitionReader.loadBeanDefinitions(encodedResource);
234 }
235
236 Closure beans = new Closure(this) {
237 public Object call(Object[] args) {
238 invokeBeanDefiningClosure((Closure) args[0]);
239 return null;
240 }
241 };
242 Binding binding = new Binding() {
243 @Override
244 public void setVariable(String name, Object value) {
245 if (currentBeanDefinition != null) {
246 applyPropertyToBeanDefinition(name, value);
247 }
248 else {
249 super.setVariable(name, value);
250 }
251 }
252 };
253 binding.setVariable("beans", beans);
254
255 int countBefore = getRegistry().getBeanDefinitionCount();
256 try {
257 GroovyShell shell = new GroovyShell(getResourceLoader().getClassLoader(), binding);
258 shell.evaluate(encodedResource.getReader(), "beans");
259 }
260 catch (Throwable ex) {
261 throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + ex.getMessage(),
262 new Location(encodedResource.getResource()), null, ex));
263 }
264 return getRegistry().getBeanDefinitionCount() - countBefore;
265 }
266
267
268
269
270
271
272
273
274
275 public GroovyBeanDefinitionReader beans(Closure closure) {
276 return invokeBeanDefiningClosure(closure);
277 }
278
279
280
281
282
283
284 public GenericBeanDefinition bean(Class<?> type) {
285 GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
286 beanDefinition.setBeanClass(type);
287 return beanDefinition;
288 }
289
290
291
292
293
294
295
296 public AbstractBeanDefinition bean(Class<?> type, Object...args) {
297 GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
298 try {
299 Closure callable = null;
300 Collection constructorArgs = null;
301 if (!ObjectUtils.isEmpty(args)) {
302 int index = args.length;
303 Object lastArg = args[index-1];
304 if (lastArg instanceof Closure) {
305 callable = (Closure) lastArg;
306 index--;
307 }
308 if (index > -1) {
309 constructorArgs = resolveConstructorArguments(args, 0, index);
310 }
311 }
312 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, type, constructorArgs);
313 if (callable != null) {
314 callable.call(this.currentBeanDefinition);
315 }
316 return this.currentBeanDefinition.getBeanDefinition();
317
318 }
319 finally {
320 this.currentBeanDefinition = current;
321 }
322 }
323
324
325
326
327
328 public void xmlns(Map<String, String> definition) {
329 if (!definition.isEmpty()) {
330 for (Map.Entry<String,String> entry : definition.entrySet()) {
331 String namespace = entry.getKey();
332 String uri = entry.getValue();
333 if (uri == null) {
334 throw new IllegalArgumentException("Namespace definition must supply a non-null URI");
335 }
336 NamespaceHandler namespaceHandler = this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve(
337 uri);
338 if (namespaceHandler == null) {
339 throw new BeanDefinitionParsingException(new Problem("No namespace handler found for URI: " + uri,
340 new Location(new DescriptiveResource(("Groovy")))));
341 }
342 this.namespaces.put(namespace, uri);
343 }
344 }
345 }
346
347
348
349
350
351
352 public void importBeans(String resourcePattern) throws IOException {
353 loadBeanDefinitions(resourcePattern);
354 }
355
356
357
358
359
360
361
362
363 public Object invokeMethod(String name, Object arg) {
364 Object[] args = (Object[])arg;
365 if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) {
366 return beans((Closure) args[0]);
367 }
368 else if ("ref".equals(name)) {
369 String refName;
370 if (args[0] == null)
371 throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found");
372
373 if (args[0] instanceof RuntimeBeanReference) {
374 refName = ((RuntimeBeanReference)args[0]).getBeanName();
375 }
376 else {
377 refName = args[0].toString();
378 }
379 boolean parentRef = false;
380 if (args.length > 1) {
381 if (args[1] instanceof Boolean) {
382 parentRef = (Boolean) args[1];
383 }
384 }
385 return new RuntimeBeanReference(refName, parentRef);
386 }
387 else if (this.namespaces.containsKey(name) && args.length > 0 && (args[0] instanceof Closure)) {
388 GroovyDynamicElementReader reader = createDynamicElementReader(name);
389 reader.invokeMethod("doCall", args);
390 }
391 else if (args.length > 0 && args[0] instanceof Closure) {
392
393 return invokeBeanDefiningMethod(name, args);
394 }
395 else if (args.length > 0 && (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) {
396 return invokeBeanDefiningMethod(name, args);
397 }
398 else if (args.length > 1 && args[args.length -1] instanceof Closure) {
399 return invokeBeanDefiningMethod(name, args);
400 }
401 MetaClass mc = DefaultGroovyMethods.getMetaClass(getRegistry());
402 if (!mc.respondsTo(getRegistry(), name, args).isEmpty()){
403 return mc.invokeMethod(getRegistry(), name, args);
404 }
405 return this;
406 }
407
408 private boolean addDeferredProperty(String property, Object newValue) {
409 if (newValue instanceof List) {
410 this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
411 new DeferredProperty(this.currentBeanDefinition, property, newValue));
412 return true;
413 }
414 else if (newValue instanceof Map) {
415 this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
416 new DeferredProperty(this.currentBeanDefinition, property, newValue));
417 return true;
418 }
419 return false;
420 }
421
422 private void finalizeDeferredProperties() {
423 for (DeferredProperty dp : this.deferredProperties.values()) {
424 if (dp.value instanceof List) {
425 dp.value = manageListIfNecessary((List) dp.value);
426 }
427 else if (dp.value instanceof Map) {
428 dp.value = manageMapIfNecessary((Map) dp.value);
429 }
430 dp.apply();
431 }
432 this.deferredProperties.clear();
433 }
434
435
436
437
438
439
440 protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) {
441 callable.setDelegate(this);
442 callable.call();
443 finalizeDeferredProperties();
444 return this;
445 }
446
447
448
449
450
451
452
453
454 private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) {
455 boolean hasClosureArgument = args[args.length - 1] instanceof Closure;
456 if (args[0] instanceof Class) {
457 Class<?> beanClass = (args[0] instanceof Class ? (Class) args[0] : args[0].getClass());
458 if (args.length >= 1) {
459 if (hasClosureArgument) {
460 if (args.length-1 != 1) {
461 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
462 beanName, beanClass, resolveConstructorArguments(args,1,args.length-1));
463 }
464 else {
465 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, beanClass);
466 }
467 }
468 else {
469 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
470 beanName, beanClass, resolveConstructorArguments(args,1,args.length));
471 }
472
473 }
474 }
475 else if (args[0] instanceof RuntimeBeanReference) {
476 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
477 this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(((RuntimeBeanReference) args[0]).getBeanName());
478 }
479 else if (args[0] instanceof Map) {
480
481 if (args.length > 1 && args[1] instanceof Class) {
482 List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length-1 : args.length);
483 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class)args[1], constructorArgs);
484 Map namedArgs = (Map)args[0];
485 for (Object o : namedArgs.keySet()) {
486 String propName = (String) o;
487 setProperty(propName, namedArgs.get(propName));
488 }
489 }
490
491 else {
492 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
493
494 Map.Entry factoryBeanEntry = (Map.Entry) ((Map) args[0]).entrySet().iterator().next();
495
496
497 int constructorArgsTest = hasClosureArgument?2:1;
498
499 if (args.length > constructorArgsTest){
500
501 int endOfConstructArgs = (hasClosureArgument? args.length - 1 : args.length);
502 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null,
503 resolveConstructorArguments(args, 1, endOfConstructArgs));
504 }
505 else {
506 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
507 }
508 this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(factoryBeanEntry.getKey().toString());
509 this.currentBeanDefinition.getBeanDefinition().setFactoryMethodName(factoryBeanEntry.getValue().toString());
510 }
511
512 }
513 else if (args[0] instanceof Closure) {
514 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
515 this.currentBeanDefinition.getBeanDefinition().setAbstract(true);
516 }
517 else {
518 List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length-1 : args.length);
519 currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
520 }
521
522 if (hasClosureArgument) {
523 Closure callable = (Closure)args[args.length-1];
524 callable.setDelegate(this);
525 callable.setResolveStrategy(Closure.DELEGATE_FIRST);
526 callable.call(new Object[]{currentBeanDefinition});
527 }
528
529 GroovyBeanDefinitionWrapper beanDefinition = currentBeanDefinition;
530 this.currentBeanDefinition = null;
531 beanDefinition.getBeanDefinition().setAttribute(GroovyBeanDefinitionWrapper.class.getName(), beanDefinition);
532 getRegistry().registerBeanDefinition(beanName, beanDefinition.getBeanDefinition());
533 return beanDefinition;
534 }
535
536 protected List<Object> resolveConstructorArguments(Object[] args, int start, int end) {
537 Object[] constructorArgs = Arrays.copyOfRange(args, start, end);
538 for (int i = 0; i < constructorArgs.length; i++) {
539 if (constructorArgs[i] instanceof GString) {
540 constructorArgs[i] = constructorArgs[i].toString();
541 }
542 else if (constructorArgs[i] instanceof List) {
543 constructorArgs[i] = manageListIfNecessary((List) constructorArgs[i]);
544 }
545 else if (constructorArgs[i] instanceof Map){
546 constructorArgs[i] = manageMapIfNecessary((Map) constructorArgs[i]);
547 }
548 }
549 return Arrays.asList(constructorArgs);
550 }
551
552
553
554
555
556
557
558 private Object manageMapIfNecessary(Map<?, ?> map) {
559 boolean containsRuntimeRefs = false;
560 for (Object element : map.values()) {
561 if (element instanceof RuntimeBeanReference) {
562 containsRuntimeRefs = true;
563 break;
564 }
565 }
566 if (containsRuntimeRefs) {
567 Map<Object, Object> managedMap = new ManagedMap<Object, Object>();
568 managedMap.putAll(map);
569 return managedMap;
570 }
571 return map;
572 }
573
574
575
576
577
578
579
580 private Object manageListIfNecessary(List<?> list) {
581 boolean containsRuntimeRefs = false;
582 for (Object element : list) {
583 if (element instanceof RuntimeBeanReference) {
584 containsRuntimeRefs = true;
585 break;
586 }
587 }
588 if (containsRuntimeRefs) {
589 List<Object> managedList = new ManagedList<Object>();
590 managedList.addAll(list);
591 return managedList;
592 }
593 return list;
594 }
595
596
597
598
599
600 public void setProperty(String name, Object value) {
601 if (this.currentBeanDefinition != null) {
602 applyPropertyToBeanDefinition(name, value);
603 }
604 }
605
606 protected void applyPropertyToBeanDefinition(String name, Object value) {
607 if (value instanceof GString) {
608 value = value.toString();
609 }
610 if (addDeferredProperty(name, value)) {
611 return;
612 }
613 else if (value instanceof Closure) {
614 GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
615 try {
616 Closure callable = (Closure) value;
617 Class<?> parameterType = callable.getParameterTypes()[0];
618 if (parameterType.equals(Object.class)) {
619 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper("");
620 callable.call(this.currentBeanDefinition);
621 }
622 else {
623 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, parameterType);
624 callable.call((Object) null);
625 }
626
627 value = this.currentBeanDefinition.getBeanDefinition();
628 }
629 finally {
630 this.currentBeanDefinition = current;
631 }
632 }
633 this.currentBeanDefinition.addProperty(name, value);
634 }
635
636
637
638
639
640
641
642
643
644
645
646 public Object getProperty(String name) {
647 Binding binding = getBinding();
648 if (binding != null && binding.hasVariable(name)) {
649 return binding.getVariable(name);
650 }
651 else {
652 if (this.namespaces.containsKey(name)) {
653 return createDynamicElementReader(name);
654 }
655 if (getRegistry().containsBeanDefinition(name)) {
656 GroovyBeanDefinitionWrapper beanDefinition = (GroovyBeanDefinitionWrapper)
657 getRegistry().getBeanDefinition(name).getAttribute(GroovyBeanDefinitionWrapper.class.getName());
658 if (beanDefinition != null) {
659 return new GroovyRuntimeBeanReference(name, beanDefinition, false);
660 }
661 else {
662 return new RuntimeBeanReference(name, false);
663 }
664 }
665
666
667 else if (this.currentBeanDefinition != null) {
668 MutablePropertyValues pvs = this.currentBeanDefinition.getBeanDefinition().getPropertyValues();
669 if (pvs.contains(name)) {
670 return pvs.get(name);
671 }
672 else {
673 DeferredProperty dp = this.deferredProperties.get(this.currentBeanDefinition.getBeanName() + name);
674 if (dp != null) {
675 return dp.value;
676 }
677 else {
678 return getMetaClass().getProperty(this, name);
679 }
680 }
681 }
682 else {
683 return getMetaClass().getProperty(this, name);
684 }
685 }
686 }
687
688 private GroovyDynamicElementReader createDynamicElementReader(String namespace) {
689 XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(new DescriptiveResource(
690 "Groovy"));
691 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
692 boolean decorating = (this.currentBeanDefinition != null);
693 if (!decorating) {
694 this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(namespace);
695 }
696 return new GroovyDynamicElementReader(namespace, this.namespaces, delegate, this.currentBeanDefinition, decorating) {
697 @Override
698 protected void afterInvocation() {
699 if (!this.decorating) {
700 currentBeanDefinition = null;
701 }
702 }
703 };
704 }
705
706
707
708
709
710
711
712
713 private static class DeferredProperty {
714
715 private final GroovyBeanDefinitionWrapper beanDefinition;
716
717 private final String name;
718
719 public Object value;
720
721 public DeferredProperty(GroovyBeanDefinitionWrapper beanDefinition, String name, Object value) {
722 this.beanDefinition = beanDefinition;
723 this.name = name;
724 this.value = value;
725 }
726
727 public void apply() {
728 this.beanDefinition.addProperty(this.name, this.value);
729 }
730 }
731
732
733
734
735
736 private class GroovyRuntimeBeanReference extends RuntimeBeanReference implements GroovyObject {
737
738 private final GroovyBeanDefinitionWrapper beanDefinition;
739
740 private MetaClass metaClass;
741
742 public GroovyRuntimeBeanReference(String beanName, GroovyBeanDefinitionWrapper beanDefinition, boolean toParent) {
743 super(beanName, toParent);
744 this.beanDefinition = beanDefinition;
745 this.metaClass = InvokerHelper.getMetaClass(this);
746 }
747
748 public MetaClass getMetaClass() {
749 return this.metaClass;
750 }
751
752 public Object getProperty(String property) {
753 if (property.equals("beanName")) {
754 return getBeanName();
755 }
756 else if (property.equals("source")) {
757 return getSource();
758 }
759 else if (this.beanDefinition != null) {
760 return new GroovyPropertyValue(
761 property, this.beanDefinition.getBeanDefinition().getPropertyValues().get(property));
762 }
763 else {
764 return this.metaClass.getProperty(this, property);
765 }
766 }
767
768 public Object invokeMethod(String name, Object args) {
769 return this.metaClass.invokeMethod(this, name, args);
770 }
771
772 public void setMetaClass(MetaClass metaClass) {
773 this.metaClass = metaClass;
774 }
775
776 public void setProperty(String property, Object newValue) {
777 if (!addDeferredProperty(property, newValue)) {
778 this.beanDefinition.getBeanDefinition().getPropertyValues().add(property, newValue);
779 }
780 }
781
782
783
784
785
786
787 private class GroovyPropertyValue extends GroovyObjectSupport {
788
789 private final String propertyName;
790
791 private final Object propertyValue;
792
793 public GroovyPropertyValue(String propertyName, Object propertyValue) {
794 this.propertyName = propertyName;
795 this.propertyValue = propertyValue;
796 }
797
798 public void leftShift(Object value) {
799 InvokerHelper.invokeMethod(this.propertyValue, "leftShift", value);
800 updateDeferredProperties(value);
801 }
802
803 public boolean add(Object value) {
804 boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "add", value);
805 updateDeferredProperties(value);
806 return retVal;
807 }
808
809 public boolean addAll(Collection values) {
810 boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "addAll", values);
811 for (Object value : values) {
812 updateDeferredProperties(value);
813 }
814 return retVal;
815 }
816
817 public Object invokeMethod(String name, Object args) {
818 return InvokerHelper.invokeMethod(this.propertyValue, name, args);
819 }
820
821 public Object getProperty(String name) {
822 return InvokerHelper.getProperty(this.propertyValue, name);
823 }
824
825 public void setProperty(String name, Object value) {
826 InvokerHelper.setProperty(this.propertyValue, name, value);
827 }
828
829 private void updateDeferredProperties(Object value) {
830 if (value instanceof RuntimeBeanReference) {
831 deferredProperties.put(beanDefinition.getBeanName(),
832 new DeferredProperty(beanDefinition, this.propertyName, this.propertyValue));
833 }
834 }
835 }
836 }
837
838 }