View Javadoc

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          // if we have an attribute verify the gbean instance, name and types match
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         // getter
101         this.getInvoker = null;
102         this.readable = true;
103 
104         // setter
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         // persistence
115         this.persistent = false;
116         initializePersistentValue(value);
117 
118         // not manageable
119         this.manageable = false;
120 
121         // create an attribute info for this gbean
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         // getter
166         this.getInvoker = getInvoker;
167         this.readable = (this.getInvoker != null);
168 
169         // setter
170         this.setInvoker = setInvoker;
171         this.isConstructorArg = false;
172         this.writable = (this.setInvoker != null);
173 
174         // persistence
175         this.persistent = persistent;
176         initializePersistentValue(persistentValue);
177 
178         // manageable
179         this.manageable = manageable;
180 
181         // create an attribute info for this gbean
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         // If attribute is persistent or not tagged as unreadable, search for a
220         // getter method
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             // If attribute is persistent or not tagged as unwritable, search
260             // for a setter method
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         // @todo actually check type
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         // get the target to invoke
386         if (target == null && !framework) {
387             throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
388         }
389 
390         // call the getter
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         // the value can not be null for primitives
405         if (value == null && type.isPrimitive()) {
406             throw new IllegalArgumentException("Cannot assign null to a primitive attribute. " + getDescription());
407         }
408 
409         // @todo actually check type
410 
411         // get the target to invoke
412         if (target == null && !framework) {
413             throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
414         }
415 
416         // call the setter
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 }