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    }