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