1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.jelly.impl;
17
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.Set;
24
25 import org.apache.commons.beanutils.ConvertingWrapDynaBean;
26 import org.apache.commons.collections.BeanMap;
27 import org.apache.commons.jelly.DynaBeanTagSupport;
28 import org.apache.commons.jelly.JellyTagException;
29 import org.apache.commons.jelly.MissingAttributeException;
30 import org.apache.commons.jelly.Tag;
31 import org.apache.commons.jelly.XMLOutput;
32 import org.apache.commons.jelly.expression.Expression;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 /***
37 * This tag is bound onto a Java Bean class. When the tag is invoked a bean will be created
38 * using the tags attributes.
39 * The bean may also have an invoke method called invoke(), run(), execute() or some such method
40 * which will be invoked after the bean has been configured.</p>
41 *
42 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
43 * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
44 * @version $Revision: 155420 $
45 */
46 public class DynamicBeanTag extends DynaBeanTagSupport implements BeanSource {
47
48 /*** The Log to which logging calls will be made. */
49 private static final Log log = LogFactory.getLog(DynamicBeanTag.class);
50
51 /*** Empty arguments for Method.invoke() */
52 private static final Object[] emptyArgs = {};
53
54 /*** the bean class */
55 private Class beanClass;
56
57 /*** the current bean instance */
58 private Object bean;
59
60 /*** the method to invoke on the bean */
61 private Method method;
62
63 /***
64 * the tag attribute name that is used to declare the name
65 * of the variable to export after running this tag
66 */
67 private String variableNameAttribute;
68
69 /*** the current variable name that the bean should be exported as */
70 private String var;
71
72 /*** the set of attribute names we've already set */
73 private Set setAttributesSet = new HashSet();
74
75 /*** the attribute definitions */
76 private Map attributes;
77
78 /***
79 *
80 * @param beanClass Class of the bean that will receive the setter events
81 * @param attributes
82 * @param variableNameAttribute
83 * @param method method of the Bean to invoke after the attributes have been set. Can be null.
84 */
85 public DynamicBeanTag(Class beanClass, Map attributes, String variableNameAttribute, Method method) {
86 this.beanClass = beanClass;
87 this.method = method;
88 this.attributes = attributes;
89 this.variableNameAttribute = variableNameAttribute;
90 }
91
92 public void beforeSetAttributes() throws JellyTagException {
93
94 try {
95 bean = beanClass.newInstance();
96 setDynaBean( new ConvertingWrapDynaBean( bean ) );
97 } catch (InstantiationException e) {
98 throw new JellyTagException("Could not instantiate dynabean",e);
99 } catch (IllegalAccessException e) {
100 throw new JellyTagException("Could not instantiate dynabean",e);
101 }
102
103 setAttributesSet.clear();
104 }
105
106 public void setAttribute(String name, Object value) throws JellyTagException {
107 boolean isVariableName = false;
108 if (variableNameAttribute != null ) {
109 if ( variableNameAttribute.equals( name ) ) {
110 if (value == null) {
111 var = null;
112 }
113 else {
114 var = value.toString();
115 }
116 isVariableName = true;
117 }
118 }
119 if (! isVariableName) {
120
121
122
123
124 setAttributesSet.add(name);
125
126
127
128 super.setAttribute(name, value);
129 }
130 }
131
132
133
134 public void doTag(XMLOutput output) throws JellyTagException {
135
136
137 for ( Iterator iter = attributes.values().iterator(); iter.hasNext(); ) {
138 Attribute attribute = (Attribute) iter.next();
139 String name = attribute.getName();
140 if ( ! setAttributesSet.contains( name ) ) {
141 if ( attribute.isRequired() ) {
142 throw new MissingAttributeException(name);
143 }
144
145 Object value = null;
146 Expression expression = attribute.getDefaultValue();
147 if ( expression != null ) {
148 value = expression.evaluate(context);
149 }
150
151
152 if ( value != null ) {
153 super.setAttribute(name, value);
154 }
155 }
156 }
157
158
159 if (bean instanceof Tag)
160 {
161 Tag tag = (Tag) bean;
162 tag.setBody(getBody());
163 tag.setContext(getContext());
164 tag.setParent(getParent());
165 ((Tag) bean).doTag(output);
166
167 return;
168 }
169
170 invokeBody(output);
171
172
173 if ( var != null ) {
174 context.setVariable(var, bean);
175 }
176
177
178 if ( method != null ) {
179 try {
180 method.invoke( bean, emptyArgs );
181 }
182 catch (IllegalAccessException e) {
183 methodInvocationException(bean, method, e);
184 }
185 catch (IllegalArgumentException e) {
186 methodInvocationException(bean, method, e);
187 }
188 catch (InvocationTargetException e) {
189
190
191 Throwable inner = e.getTargetException();
192
193 throw new JellyTagException(inner);
194
195 }
196 }
197 }
198
199 /***
200 * Report the state of the bean when method invocation fails
201 * so that the user can determine any problems that might
202 * be occuring while using dynamic jelly beans.
203 *
204 * @param bean Bean on which <code>method</code was invoked
205 * @param method Method that was invoked
206 * @param e Exception throw when <code>method</code> was invoked
207 */
208 private void methodInvocationException(Object bean, Method method, Exception e) throws JellyTagException {
209 log.error("Could not invoke " + method, e);
210 BeanMap beanMap = new BeanMap(bean);
211
212 log.error("Bean properties:");
213 for (Iterator i = beanMap.keySet().iterator(); i.hasNext();) {
214 String property = (String) i.next();
215 Object value = beanMap.get(property);
216 log.error(property + " -> " + value);
217 }
218
219 log.error(beanMap);
220 throw new JellyTagException(e);
221 }
222
223
224
225 /***
226 * @return the bean that has just been created
227 */
228 public Object getBean() {
229 return bean;
230 }
231 }