001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.gbean.runtime;
019
020 import java.lang.reflect.Method;
021
022 import org.apache.geronimo.gbean.DynamicGAttributeInfo;
023 import org.apache.geronimo.gbean.DynamicGBean;
024 import org.apache.geronimo.gbean.GAttributeInfo;
025 import org.apache.geronimo.gbean.InvalidConfigurationException;
026 import org.apache.geronimo.kernel.ClassLoading;
027
028 /**
029 * @version $Rev: 549825 $ $Date: 2007-06-22 10:17:31 -0400 (Fri, 22 Jun 2007) $
030 */
031 public class GBeanAttribute {
032 private final GBeanInstance gbeanInstance;
033
034 private final String name;
035
036 private final Class type;
037
038 private final boolean readable;
039
040 private final MethodInvoker getInvoker;
041
042 private final boolean writable;
043
044 private final MethodInvoker setInvoker;
045
046 private final boolean isConstructorArg;
047
048 private final boolean persistent;
049
050 private final boolean manageable;
051
052 private Object persistentValue;
053
054 /**
055 * Is this a special attribute like objectName, classLoader or gbeanContext?
056 * Special attributes are injected at startup just like persistent attrubutes, but are
057 * otherwise unmodifiable.
058 */
059 private final boolean special;
060
061 private final boolean framework;
062
063 private final boolean dynamic;
064
065 private final GAttributeInfo attributeInfo;
066
067 static GBeanAttribute createSpecialAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) {
068 return new GBeanAttribute(attribute, gbeanInstance, name, type, value);
069 }
070
071 private GBeanAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) {
072 this.special = true;
073 this.framework = false;
074 this.dynamic = false;
075
076 if (gbeanInstance == null || name == null || type == null) {
077 throw new IllegalArgumentException("null param(s) supplied");
078 }
079
080 // if we have an attribute verify the gbean instance, name and types match
081 if (attribute != null) {
082 assert (gbeanInstance == attribute.gbeanInstance);
083 assert (name.equals(attribute.name));
084 if (type != attribute.type) {
085 throw new InvalidConfigurationException("Special attribute " + name +
086 " must have the type " + type.getName() + ", but is " +
087 attribute.type.getName() + ": targetClass=" + gbeanInstance.getType().getName());
088 }
089 if (attribute.isPersistent()) {
090 throw new InvalidConfigurationException("Special attributes must not be persistent:" +
091 " name=" + name + ", targetClass=" + gbeanInstance.getType().getName());
092 }
093 }
094
095 this.gbeanInstance = gbeanInstance;
096 this.name = name;
097 this.type = type;
098
099 // getter
100 this.getInvoker = null;
101 this.readable = true;
102
103 // setter
104 if (attribute != null) {
105 this.setInvoker = attribute.setInvoker;
106 this.isConstructorArg = attribute.isConstructorArg;
107 } else {
108 this.setInvoker = null;
109 this.isConstructorArg = false;
110 }
111 this.writable = false;
112
113 // persistence
114 this.persistent = false;
115 initializePersistentValue(value);
116
117 // not manageable
118 this.manageable = false;
119
120 // create an attribute info for this gbean
121 if (attribute != null) {
122 GAttributeInfo attributeInfo = attribute.getAttributeInfo();
123 this.attributeInfo = new GAttributeInfo(this.name,
124 this.type.getName(),
125 this.persistent,
126 this.manageable,
127 this.readable,
128 this.writable,
129 attributeInfo.getGetterName(),
130 attributeInfo.getSetterName());
131 } else {
132 this.attributeInfo = new GAttributeInfo(this.name,
133 this.type.getName(),
134 this.persistent,
135 this.manageable,
136 this.readable,
137 this.writable,
138 null,
139 null);
140 }
141 }
142
143 static GBeanAttribute createFrameworkAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker) {
144 return new GBeanAttribute(gbeanInstance, name, type, getInvoker, null, false, null, true);
145 }
146
147 static GBeanAttribute createFrameworkAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker, MethodInvoker setInvoker, boolean persistent, Object persistentValue, boolean manageable) {
148 return new GBeanAttribute(gbeanInstance, name, type, getInvoker, setInvoker, persistent, persistentValue, manageable);
149 }
150
151 private GBeanAttribute(GBeanInstance gbeanInstance, String name, Class type, MethodInvoker getInvoker, MethodInvoker setInvoker, boolean persistent, Object persistentValue, boolean manageable) {
152 this.special = false;
153 this.framework = true;
154 this.dynamic = false;
155
156 if (gbeanInstance == null || name == null || type == null) {
157 throw new IllegalArgumentException("null param(s) supplied");
158 }
159
160 this.gbeanInstance = gbeanInstance;
161 this.name = name;
162 this.type = type;
163
164 // getter
165 this.getInvoker = getInvoker;
166 this.readable = (this.getInvoker != null);
167
168 // setter
169 this.setInvoker = setInvoker;
170 this.isConstructorArg = false;
171 this.writable = (this.setInvoker != null);
172
173 // persistence
174 this.persistent = persistent;
175 initializePersistentValue(persistentValue);
176
177 // manageable
178 this.manageable = manageable;
179
180 // create an attribute info for this gbean
181 attributeInfo = new GAttributeInfo(this.name,
182 this.type.getName(),
183 this.persistent,
184 this.manageable,
185 this.readable,
186 this.writable,
187 null,
188 null);
189 }
190
191 public GBeanAttribute(GBeanInstance gbeanInstance, GAttributeInfo attributeInfo, boolean isConstructorArg) throws InvalidConfigurationException {
192 this.special = false;
193 this.framework = false;
194
195 if (gbeanInstance == null || attributeInfo == null) {
196 throw new IllegalArgumentException("null param(s) supplied");
197 }
198 if (!attributeInfo.isReadable() && !attributeInfo.isWritable() && !attributeInfo.isPersistent() && !isConstructorArg)
199 {
200 throw new InvalidConfigurationException("An attribute must be readable, writable, persistent or a constructor arg: " +
201 " name=" + attributeInfo.getName() + " targetClass=" + gbeanInstance.getType().getName());
202 }
203 this.gbeanInstance = gbeanInstance;
204 this.attributeInfo = attributeInfo;
205 this.name = attributeInfo.getName();
206 this.isConstructorArg = isConstructorArg;
207 try {
208 this.type = ClassLoading.loadClass(attributeInfo.getType(), gbeanInstance.getClassLoader());
209 } catch (ClassNotFoundException e) {
210 throw new InvalidConfigurationException("Could not load attribute class: " + attributeInfo.getType(), e);
211 }
212 this.persistent = attributeInfo.isPersistent();
213 this.manageable = attributeInfo.isManageable();
214
215 readable = attributeInfo.isReadable();
216 writable = attributeInfo.isWritable();
217
218 // If attribute is persistent or not tagged as unreadable, search for a
219 // getter method
220 if (attributeInfo instanceof DynamicGAttributeInfo) {
221 this.dynamic = true;
222 if (readable) {
223 getInvoker = new DynamicGetterMethodInvoker(name);
224 } else {
225 getInvoker = null;
226 }
227 if (writable) {
228 setInvoker = new DynamicSetterMethodInvoker(name);
229 } else {
230 setInvoker = null;
231 }
232 } else {
233 this.dynamic = false;
234 if (attributeInfo.getGetterName() != null) {
235 try {
236 String getterName = attributeInfo.getGetterName();
237 Method getterMethod = gbeanInstance.getType().getMethod(getterName, null);
238
239 if (!getterMethod.getReturnType().equals(type)) {
240 if (getterMethod.getReturnType().getName().equals(type.getName())) {
241 throw new InvalidConfigurationException("Getter return type in wrong classloader: type: " + type + " wanted in classloader: " + type.getClassLoader() + " actual: " + getterMethod.getReturnType().getClassLoader());
242 } else {
243 throw new InvalidConfigurationException("Getter method of wrong type: " + getterMethod.getReturnType() + " expected " + getDescription());
244 }
245 }
246 if (AbstractGBeanReference.NO_PROXY) {
247 getInvoker = new ReflectionMethodInvoker(getterMethod);
248 } else {
249 getInvoker = new FastMethodInvoker(getterMethod);
250 }
251 } catch (NoSuchMethodException e) {
252 throw new InvalidConfigurationException("Getter method not found " + getDescription(), e);
253 }
254 } else {
255 getInvoker = null;
256 }
257
258 // If attribute is persistent or not tagged as unwritable, search
259 // for a setter method
260 if (attributeInfo.getSetterName() != null) {
261 try {
262 String setterName = attributeInfo.getSetterName();
263 Method setterMethod = gbeanInstance.getType().getMethod(setterName, new Class[]{type});
264 if (AbstractGBeanReference.NO_PROXY) {
265 setInvoker = new ReflectionMethodInvoker(setterMethod);
266 } else {
267 setInvoker = new FastMethodInvoker(setterMethod);
268 }
269 } catch (NoSuchMethodException e) {
270 throw new InvalidConfigurationException("Setter method not found " + getDescription(), e);
271 }
272 } else {
273 setInvoker = null;
274 }
275 }
276
277 initializePersistentValue(null);
278 }
279
280 private void initializePersistentValue(Object value) {
281 if (persistent || special) {
282 if (value == null && type.isPrimitive() && isConstructorArg) {
283 if (type == Boolean.TYPE) {
284 value = Boolean.FALSE;
285 } else if (type == Byte.TYPE) {
286 value = new Byte((byte) 0);
287 } else if (type == Short.TYPE) {
288 value = new Short((short) 0);
289 } else if (type == Integer.TYPE) {
290 value = new Integer(0);
291 } else if (type == Long.TYPE) {
292 value = new Long(0);
293 } else if (type == Character.TYPE) {
294 value = new Character((char) 0);
295 } else if (type == Float.TYPE) {
296 value = new Float(0);
297 } else /** if (type == Double.TYPE) */ {
298 value = new Double(0);
299 }
300 }
301 persistentValue = value;
302 }
303 }
304
305 public String getName() {
306 return name;
307 }
308
309 public GAttributeInfo getAttributeInfo() {
310 return attributeInfo;
311 }
312
313 public boolean isReadable() {
314 return readable;
315 }
316
317 public boolean isWritable() {
318 return writable;
319 }
320
321 public Class getType() {
322 return type;
323 }
324
325 public boolean isFramework() {
326 return framework;
327 }
328
329 public boolean isDynamic() {
330 return dynamic;
331 }
332
333 public boolean isPersistent() {
334 return persistent;
335 }
336
337 public boolean isManageable() {
338 return manageable;
339 }
340
341 public boolean isSpecial() {
342 return special;
343 }
344
345 public void inject(Object target) throws Exception {
346 if ((persistent || special) && !isConstructorArg && writable && persistentValue != null) {
347 setValue(target, persistentValue);
348 }
349 }
350
351 public Object getPersistentValue() {
352 if (!persistent && !special) {
353 throw new IllegalStateException("Attribute is not persistent " + getDescription());
354 }
355 return persistentValue;
356 }
357
358 public void setPersistentValue(Object persistentValue) {
359 if (!persistent && !special) {
360 throw new IllegalStateException("Attribute is not persistent " + getDescription());
361 }
362
363 if (persistentValue == null && type.isPrimitive()) {
364 throw new IllegalArgumentException("Cannot assign null to a primitive attribute. " + getDescription());
365 }
366
367 // @todo actually check type
368 this.persistentValue = persistentValue;
369 }
370
371 public Object getValue(Object target) throws Exception {
372 if (!readable) {
373 if (persistent) {
374 return persistentValue;
375 } else {
376 throw new IllegalStateException("This attribute is not readable. " + getDescription());
377 }
378 }
379
380 if (special) {
381 return persistentValue;
382 }
383
384 // get the target to invoke
385 if (target == null && !framework) {
386 throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
387 }
388
389 // call the getter
390 Object value = getInvoker.invoke(target, null);
391 return value;
392 }
393
394 public void setValue(Object target, Object value) throws Exception {
395 if (!writable) {
396 if (persistent) {
397 throw new IllegalStateException("This persistent attribute is not modifable while the gbean is running. " + getDescription());
398 } else {
399 throw new IllegalStateException("This attribute is not writable. " + getDescription());
400 }
401 }
402
403 // the value can not be null for primitives
404 if (value == null && type.isPrimitive()) {
405 throw new IllegalArgumentException("Cannot assign null to a primitive attribute. " + getDescription());
406 }
407
408 // @todo actually check type
409
410 // get the target to invoke
411 if (target == null && !framework) {
412 throw new IllegalStateException("GBean does not have a target instance to invoke. " + getDescription());
413 }
414
415 // call the setter
416 setInvoker.invoke(target, new Object[]{value});
417 }
418
419 public String getDescription() {
420 return "Attribute Name: " + getName() + ", Type: " + getType() + ", GBeanInstance: " + gbeanInstance.getName();
421 }
422
423 private static final class DynamicGetterMethodInvoker implements MethodInvoker {
424 private final String name;
425
426 public DynamicGetterMethodInvoker(String name) {
427 this.name = name;
428 }
429
430 public Object invoke(Object target, Object[] arguments) throws Exception {
431 return ((DynamicGBean) target).getAttribute(name);
432 }
433 }
434
435 private static final class DynamicSetterMethodInvoker implements MethodInvoker {
436 private final String name;
437
438 public DynamicSetterMethodInvoker(String name) {
439 this.name = name;
440 }
441
442 public Object invoke(Object target, Object[] arguments) throws Exception {
443 ((DynamicGBean) target).setAttribute(name, arguments[0]);
444 return null;
445 }
446 }
447 }