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    }