001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.geronimo.gbean.runtime; 020 021 import java.lang.reflect.Method; 022 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: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $ 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 Object persistentValue; 054 055 /** 056 * Is this a special attribute like objectName, classLoader or gbeanContext? 057 * Special attributes are injected at startup just like persistent attrubutes, but are 058 * otherwise unmodifiable. 059 */ 060 private final boolean special; 061 062 private final boolean framework; 063 064 private final boolean dynamic; 065 066 private final GAttributeInfo attributeInfo; 067 068 static GBeanAttribute createSpecialAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) { 069 return new GBeanAttribute(attribute, gbeanInstance, name, type, value); 070 } 071 072 private GBeanAttribute(GBeanAttribute attribute, GBeanInstance gbeanInstance, String name, Class type, Object value) { 073 this.special = true; 074 this.framework = false; 075 this.dynamic = false; 076 077 if (gbeanInstance == null || name == null || type == null) { 078 throw new IllegalArgumentException("null param(s) supplied"); 079 } 080 081 // if we have an attribute verify the gbean instance, name and types match 082 if (attribute != null) { 083 assert (gbeanInstance == attribute.gbeanInstance); 084 assert (name.equals(attribute.name)); 085 if (type != attribute.type) { 086 throw new InvalidConfigurationException("Special attribute " + name + 087 " must have the type " + type.getName() + ", but is " + 088 attribute.type.getName() + ": targetClass=" + gbeanInstance.getType().getName()); 089 } 090 if (attribute.isPersistent()) { 091 throw new InvalidConfigurationException("Special attributes must not be persistent:" + 092 " name=" + name + ", targetClass=" + gbeanInstance.getType().getName()); 093 } 094 } 095 096 this.gbeanInstance = gbeanInstance; 097 this.name = name; 098 this.type = type; 099 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 }