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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 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 }