1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.geronimo.gbean.runtime;
20
21 import java.lang.reflect.Method;
22
23 import org.apache.geronimo.gbean.DynamicGAttributeInfo;
24 import org.apache.geronimo.gbean.DynamicGBean;
25 import org.apache.geronimo.gbean.GAttributeInfo;
26 import org.apache.geronimo.gbean.InvalidConfigurationException;
27 import org.apache.geronimo.kernel.ClassLoading;
28
29 /**
30 * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
31 */
32 public class GBeanAttribute {
33 private final GBeanInstance gbeanInstance;
34
35 private final String name;
36
37 private final Class type;
38
39 private final boolean readable;
40
41 private final MethodInvoker getInvoker;
42
43 private final boolean writable;
44
45 private final MethodInvoker setInvoker;
46
47 private final boolean isConstructorArg;
48
49 private final boolean persistent;
50
51 private final boolean manageable;
52
53 private Object persistentValue;
54
55 /**
56 * Is this a special attribute like objectName, classLoader or gbeanContext?
57 * Special attributes are injected at startup just like persistent attrubutes, but are
58 * otherwise unmodifiable.
59 */
60 private final boolean special;
61
62 private final boolean framework;
63
64 private final boolean dynamic;
65
66 private final GAttributeInfo attributeInfo;
67
68 static GBeanAttribute createSpecialAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) {
69 return new GBeanAttribute(attribute, gbeanInstance, name, type, value);
70 }
71
72 private GBeanAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) {
73 this.special = true;
74 this.framework = false;
75 this.dynamic = false;
76
77 if (gbeanInstance == null || name == null || type == null) {
78 throw new IllegalArgumentException("null param(s) supplied");
79 }
80
81
82 if (attribute != null) {
83 assert (gbeanInstance == attribute.gbeanInstance);
84 assert (name.equals(attribute.name));
85 if (type != attribute.type) {
86 throw new InvalidConfigurationException("Special attribute " + name +
87 " must have the type " + type.getName() + ", but is " +
88 attribute.type.getName() + ": targetClass=" + gbeanInstance.getType().getName());
89 }
90 if (attribute.isPersistent()) {
91 throw new InvalidConfigurationException("Special attributes must not be persistent:" +
92 " name=" + name + ", targetClass=" + gbeanInstance.getType().getName());
93 }
94 }
95
96 this.gbeanInstance = gbeanInstance;
97 this.name = name;
98 this.type = type;
99
100
101 this.getInvoker = null;
102 this.readable = true;
103
104
105 if (attribute != null) {
106 this.setInvoker = attribute.setInvoker;
107 this.isConstructorArg = attribute.isConstructorArg;
108 } else {
109 this.setInvoker = null;
110 this.isConstructorArg = false;
111 }
112 this.writable = false;
113
114
115 this.persistent = false;
116 initializePersistentValue(value);
117
118
119 this.manageable = false;
120
121
122 if (attribute != null) {
123 GAttributeInfo attributeInfo = attribute.getAttributeInfo();
124 this.attributeInfo = new GAttributeInfo(this.name,
125 this.type.getName(),
126 this.persistent,
127 this.manageable,
128 this.readable,
129 this.writable,
130 attributeInfo.getGetterName(),
131 attributeInfo.getSetterName());
132 } else {
133 this.attributeInfo = new GAttributeInfo(this.name,
134 this.type.getName(),
135 this.persistent,
136 this.manageable,
137 this.readable,
138 this.writable,
139 null,
140 null);
141 }
142 }
143
144 static GBeanAttribute createFrameworkAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker) {
145 return new GBeanAttribute(gbeanInstance, name, type, getInvoker, null, false, null, true);
146 }
147
148 static GBeanAttribute createFrameworkAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker, MethodInvoker setInvoker, boolean persistent, Object persistentValue, boolean manageable) {
149 return new GBeanAttribute(gbeanInstance, name, type, getInvoker, setInvoker, persistent, persistentValue, manageable);
150 }
151
152 private GBeanAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker, MethodInvoker setInvoker, boolean persistent, Object persistentValue, boolean manageable) {
153 this.special = false;
154 this.framework = true;
155 this.dynamic = false;
156
157 if (gbeanInstance == null || name == null || type == null) {
158 throw new IllegalArgumentException("null param(s) supplied");
159 }
160
161 this.gbeanInstance = gbeanInstance;
162 this.name = name;
163 this.type = type;
164
165
166 this.getInvoker = getInvoker;
167 this.readable = (this.getInvoker != null);
168
169
170 this.setInvoker = setInvoker;
171 this.isConstructorArg = false;
172 this.writable = (this.setInvoker != null);
173
174
175 this.persistent = persistent;
176 initializePersistentValue(persistentValue);
177
178
179 this.manageable = manageable;
180
181
182 attributeInfo = new GAttributeInfo(this.name,
183 this.type.getName(),
184 this.persistent,
185 this.manageable,
186 this.readable,
187 this.writable,
188 null,
189 null);
190 }
191
192 public GBeanAttribute(GBeanInstance gbeanInstance, GAttributeInfo attributeInfo, boolean isConstructorArg) throws InvalidConfigurationException {
193 this.special = false;
194 this.framework = false;
195
196 if (gbeanInstance == null || attributeInfo == null) {
197 throw new IllegalArgumentException("null param(s) supplied");
198 }
199 if (!attributeInfo.isReadable() && !attributeInfo.isWritable() && !attributeInfo.isPersistent() && !isConstructorArg)
200 {
201 throw new InvalidConfigurationException("An attribute must be readable, writable, persistent or a constructor arg: " +
202 " name=" + attributeInfo.getName() + " targetClass=" + gbeanInstance.getType().getName());
203 }
204 this.gbeanInstance = gbeanInstance;
205 this.attributeInfo = attributeInfo;
206 this.name = attributeInfo.getName();
207 this.isConstructorArg = isConstructorArg;
208 try {
209 this.type = ClassLoading.loadClass(attributeInfo.getType(), gbeanInstance.getClassLoader());
210 } catch (ClassNotFoundException e) {
211 throw new InvalidConfigurationException("Could not load attribute class: " + attributeInfo.getType());
212 }
213 this.persistent = attributeInfo.isPersistent();
214 this.manageable = attributeInfo.isManageable();
215
216 readable = attributeInfo.isReadable();
217 writable = attributeInfo.isWritable();
218
219
220
221 if (attributeInfo instanceof DynamicGAttributeInfo) {
222 this.dynamic = true;
223 if (readable) {
224 getInvoker = new DynamicGetterMethodInvoker(name);
225 } else {
226 getInvoker = null;
227 }
228 if (writable) {
229 setInvoker = new DynamicSetterMethodInvoker(name);
230 } else {
231 setInvoker = null;
232 }
233 } else {
234 this.dynamic = false;
235 if (attributeInfo.getGetterName() != null) {
236 try {
237 String getterName = attributeInfo.getGetterName();
238 Method getterMethod = gbeanInstance.getType().getMethod(getterName, null);
239
240 if (!getterMethod.getReturnType().equals(type)) {
241 if (getterMethod.getReturnType().getName().equals(type.getName())) {
242 throw new InvalidConfigurationException("Getter return type in wrong classloader: type: " + type + " wanted in classloader: " + type.getClassLoader() + " actual: " + getterMethod.getReturnType().getClassLoader());
243 } else {
244 throw new InvalidConfigurationException("Getter method of wrong type: " + getterMethod.getReturnType() + " expected " + getDescription());
245 }
246 }
247 if (AbstractGBeanReference.NO_PROXY) {
248 getInvoker = new ReflectionMethodInvoker(getterMethod);
249 } else {
250 getInvoker = new FastMethodInvoker(getterMethod);
251 }
252 } catch (NoSuchMethodException e) {
253 throw new InvalidConfigurationException("Getter method not found " + getDescription());
254 }
255 } else {
256 getInvoker = null;
257 }
258
259
260
261 if (attributeInfo.getSetterName() != null) {
262 try {
263 String setterName = attributeInfo.getSetterName();
264 Method setterMethod = gbeanInstance.getType().getMethod(setterName, new Class[]{type});
265 if (AbstractGBeanReference.NO_PROXY) {
266 setInvoker = new ReflectionMethodInvoker(setterMethod);
267 } else {
268 setInvoker = new FastMethodInvoker(setterMethod);
269 }
270 } catch (NoSuchMethodException e) {
271 throw new InvalidConfigurationException("Setter method not found " + getDescription());
272 }
273 } else {
274 setInvoker = null;
275 }
276 }
277
278 initializePersistentValue(null);
279 }
280
281 private void initializePersistentValue(Object value) {
282 if (persistent || special) {
283 if (value == null && type.isPrimitive() && isConstructorArg) {
284 if (type == Boolean.TYPE) {
285 value = Boolean.FALSE;
286 } else if (type == Byte.TYPE) {
287 value = new Byte((byte) 0);
288 } else if (type == Short.TYPE) {
289 value = new Short((short) 0);
290 } else if (type == Integer.TYPE) {
291 value = new Integer(0);
292 } else if (type == Long.TYPE) {
293 value = new Long(0);
294 } else if (type == Character.TYPE) {
295 value = new Character((char) 0);
296 } else if (type == Float.TYPE) {
297 value = new Float(0);
298 } else /** if (type == Double.TYPE) */ {
299 value = new Double(0);
300 }
301 }
302 persistentValue = value;
303 }
304 }
305
306 public String getName() {
307 return name;
308 }
309
310 public GAttributeInfo getAttributeInfo() {
311 return attributeInfo;
312 }
313
314 public boolean isReadable() {
315 return readable;
316 }
317
318 public boolean isWritable() {
319 return writable;
320 }
321
322 public Class getType() {
323 return type;
324 }
325
326 public boolean isFramework() {
327 return framework;
328 }
329
330 public boolean isDynamic() {
331 return dynamic;
332 }
333
334 public boolean isPersistent() {
335 return persistent;
336 }
337
338 public boolean isManageable() {
339 return manageable;
340 }
341
342 public boolean isSpecial() {
343 return special;
344 }
345
346 public void inject(Object target) throws Exception {
347 if ((persistent || special) && !isConstructorArg && writable && persistentValue != null) {
348 setValue(target, persistentValue);
349 }
350 }
351
352 public Object getPersistentValue() {
353 if (!persistent && !special) {
354 throw new IllegalStateException("Attribute is not persistent " + getDescription());
355 }
356 return persistentValue;
357 }
358
359 public void setPersistentValue(Object persistentValue) {
360 if (!persistent && !special) {
361 throw new IllegalStateException("Attribute is not persistent " + getDescription());
362 }
363
364 if (persistentValue == null && type.isPrimitive()) {
365 throw new IllegalArgumentException("Cannot assign null to a primitive attribute. " + getDescription());
366 }
367
368
369 this.persistentValue = persistentValue;
370 }
371
372 public Object getValue(Object target) throws Exception {
373 if (!readable) {
374 if (persistent) {
375 return persistentValue;
376 } else {
377 throw new IllegalStateException("This attribute is not readable. " + getDescription());
378 }
379 }
380
381 if (special) {
382 return persistentValue;
383 }
384
385
386 if (target == null && !framework) {
387 throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
388 }
389
390
391 Object value = getInvoker.invoke(target, null);
392 return value;
393 }
394
395 public void setValue(Object target, Object value) throws Exception {
396 if (!writable) {
397 if (persistent) {
398 throw new IllegalStateException("This persistent attribute is not modifable while the gbean is running. " + getDescription());
399 } else {
400 throw new IllegalStateException("This attribute is not writable. " + getDescription());
401 }
402 }
403
404
405 if (value == null && type.isPrimitive()) {
406 throw new IllegalArgumentException("Cannot assign null to a primitive attribute. " + getDescription());
407 }
408
409
410
411
412 if (target == null && !framework) {
413 throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
414 }
415
416
417 setInvoker.invoke(target, new Object[]{value});
418 }
419
420 public String getDescription() {
421 return "Attribute Name: " + getName() + ", Type: " + getType() + ", GBeanInstance: " + gbeanInstance.getName();
422 }
423
424 private static final class DynamicGetterMethodInvoker implements MethodInvoker {
425 private final String name;
426
427 public DynamicGetterMethodInvoker(String name) {
428 this.name = name;
429 }
430
431 public Object invoke(Object target, Object[] arguments) throws Exception {
432 return ((DynamicGBean) target).getAttribute(name);
433 }
434 }
435
436 private static final class DynamicSetterMethodInvoker implements MethodInvoker {
437 private final String name;
438
439 public DynamicSetterMethodInvoker(String name) {
440 this.name = name;
441 }
442
443 public Object invoke(Object target, Object[] arguments) throws Exception {
444 ((DynamicGBean) target).setAttribute(name, arguments[0]);
445 return null;
446 }
447 }
448 }