001/*
002 * HA-JDBC: High-Availability JDBC
003 * Copyright (C) 2012  Paul Ferraro
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Lesser General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package net.sf.hajdbc.management;
019
020import java.beans.BeanInfo;
021import java.beans.Introspector;
022import java.beans.MethodDescriptor;
023import java.beans.PropertyDescriptor;
024import java.lang.reflect.InvocationTargetException;
025import java.lang.reflect.Method;
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import javax.management.Attribute;
031import javax.management.AttributeList;
032import javax.management.AttributeNotFoundException;
033import javax.management.DynamicMBean;
034import javax.management.InvalidAttributeValueException;
035import javax.management.MBeanAttributeInfo;
036import javax.management.MBeanException;
037import javax.management.MBeanInfo;
038import javax.management.MBeanOperationInfo;
039import javax.management.ReflectionException;
040
041/**
042 * @author Paul Ferraro
043 */
044public class AnnotatedMBean implements DynamicMBean
045{
046        private final MBeanInfo info;
047        private final Object bean;
048        private final Map<String, Method> accessorMap = new HashMap<String, Method>();
049        private final Map<String, Method> mutatorMap = new HashMap<String, Method>();
050        
051        public AnnotatedMBean(Object bean)
052        {
053                Class<?> beanClass = bean.getClass();
054                
055                MBean mbean = beanClass.getAnnotation(MBean.class);
056                
057                if (mbean == null)
058                {
059                        throw new IllegalArgumentException(String.format("%s is not an @MBean", beanClass));
060                }
061                
062                this.bean = bean;
063                
064                try
065                {
066                        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
067                        PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
068                        List<MBeanAttributeInfo> attributeList = new ArrayList<MBeanAttributeInfo>(properties.length);
069                        
070                        for (PropertyDescriptor descriptor: properties)
071                        {
072                                Method accessor = descriptor.getReadMethod();
073                                ManagedAttribute managedAccessor = (accessor != null) ? accessor.getAnnotation(ManagedAttribute.class) : null;
074
075                                Method mutator = descriptor.getWriteMethod();
076                                ManagedAttribute managedMutator = (mutator != null) ? mutator.getAnnotation(ManagedAttribute.class) : null;
077                                
078                                if ((managedAccessor != null) || (managedMutator != null))
079                                {
080                                        String name = descriptor.getName();
081                                        
082                                        Description description = (accessor != null) ? accessor.getAnnotation(Description.class) : null;
083                                        if ((description == null) && (mutator != null))
084                                        {
085                                                description = mutator.getAnnotation(Description.class);
086                                        }
087                                        
088                                        attributeList.add(new MBeanAttributeInfo(name, (description != null) ? description.value() : null, (managedAccessor != null) ? accessor : null, (managedMutator != null) ? mutator : null));
089                                        
090                                        if (managedAccessor != null)
091                                        {
092                                                this.accessorMap.put(name, accessor);
093                                        }
094                                        
095                                        if (managedMutator != null)
096                                        {
097                                                this.mutatorMap.put(name, mutator);
098                                        }
099                                }
100                        }
101                        
102                        MethodDescriptor[] methods = beanInfo.getMethodDescriptors();
103                        List<MBeanOperationInfo> operationList = new ArrayList<MBeanOperationInfo>(methods.length);
104                        
105                        for (MethodDescriptor descriptor: beanInfo.getMethodDescriptors())
106                        {
107                                Method method = descriptor.getMethod();
108                                ManagedOperation managedMethod = method.getAnnotation(ManagedOperation.class);
109                                
110                                if (managedMethod != null)
111                                {
112                                        Description description = method.getAnnotation(Description.class);
113                                        
114                                        operationList.add(new MBeanOperationInfo((description != null) ? description.value() : null, method));
115                                }
116                        }
117                        
118                        Description description = beanClass.getAnnotation(Description.class);
119                        
120                        this.info = new MBeanInfo(beanClass.getName(), (description != null) ? description.value() : null, attributeList.toArray(new MBeanAttributeInfo[attributeList.size()]), null, operationList.toArray(new MBeanOperationInfo[operationList.size()]), null);
121                }
122                catch (java.beans.IntrospectionException e)
123                {
124                        throw new IllegalArgumentException(e);
125                }
126                catch (javax.management.IntrospectionException e)
127                {
128                        throw new IllegalArgumentException(e);
129                }
130        }
131        
132        /**
133         * {@inheritDoc}
134         * @see javax.management.DynamicMBean#getAttribute(java.lang.String)
135         */
136        @Override
137        public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
138        {
139                Method method = this.accessorMap.get(name);
140                
141                if (method == null)
142                {
143                        throw new AttributeNotFoundException(name);
144                }
145                
146                try
147                {
148                        return method.invoke(this.bean);
149                }
150                catch (InvocationTargetException e)
151                {
152                        throw new ReflectionException(e);
153                }
154                catch (IllegalArgumentException e)
155                {
156                        throw new MBeanException(e);
157                }
158                catch (IllegalAccessException e)
159                {
160                        throw new MBeanException(e);
161                }
162        }
163
164        /**
165         * {@inheritDoc}
166         * @see javax.management.DynamicMBean#getAttributes(java.lang.String[])
167         */
168        @Override
169        public AttributeList getAttributes(String[] names)
170        {
171                AttributeList list = new AttributeList(names.length);
172                
173                for (String name: names)
174                {
175                        try
176                        {
177                                list.add(new Attribute(name, this.getAttribute(name)));                         
178                        }
179                        catch (Exception e)
180                        {
181                                // Ignore
182                        }
183                }
184
185                return list;
186        }
187
188        /**
189         * {@inheritDoc}
190         * @see javax.management.DynamicMBean#getMBeanInfo()
191         */
192        @Override
193        public MBeanInfo getMBeanInfo()
194        {
195                return this.info;
196        }
197
198        /**
199         * {@inheritDoc}
200         * @see javax.management.DynamicMBean#invoke(java.lang.String, java.lang.Object[], java.lang.String[])
201         */
202        @Override
203        public Object invoke(String method, Object[] args, String[] types) throws MBeanException, ReflectionException
204        {
205                Class<?>[] classes = new Class<?>[types.length];
206                
207                try
208                {
209                        for (int i = 0; i < types.length; ++i)
210                        {
211                                classes[i] = this.bean.getClass().getClassLoader().loadClass(types[i]);
212                        }
213                        
214                        return this.bean.getClass().getMethod(method, classes).invoke(this.bean, args);
215                }
216                catch (ClassNotFoundException e)
217                {
218                        throw new MBeanException(e);
219                }
220                catch (IllegalArgumentException e)
221                {
222                        throw new MBeanException(e);
223                }
224                catch (SecurityException e)
225                {
226                        throw new MBeanException(e);
227                }
228                catch (IllegalAccessException e)
229                {
230                        throw new MBeanException(e);
231                }
232                catch (InvocationTargetException e)
233                {
234                        throw new ReflectionException(e);
235                }
236                catch (NoSuchMethodException e)
237                {
238                        throw new MBeanException(e);
239                }
240        }
241
242        /**
243         * {@inheritDoc}
244         * @see javax.management.DynamicMBean#setAttribute(javax.management.Attribute)
245         */
246        @Override
247        public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
248        {
249                Method method = this.mutatorMap.get(attribute.getName());
250                
251                if (method == null)
252                {
253                        throw new AttributeNotFoundException(attribute.getName());
254                }
255                
256                try
257                {
258                        this.mutatorMap.get(attribute.getName()).invoke(this.bean, attribute.getValue());
259                }
260                catch (IllegalArgumentException e)
261                {
262                        throw new InvalidAttributeValueException(e.getMessage());
263                }
264                catch (IllegalAccessException e)
265                {
266                        throw new MBeanException(e);
267                }
268                catch (InvocationTargetException e)
269                {
270                        throw new ReflectionException(e);
271                }
272        }
273
274        /**
275         * {@inheritDoc}
276         * @see javax.management.DynamicMBean#setAttributes(javax.management.AttributeList)
277         */
278        @Override
279        public AttributeList setAttributes(AttributeList attributes)
280        {
281                AttributeList list = new AttributeList(attributes.size());
282                
283                for (Object attribute: attributes)
284                {
285                        try
286                        {
287                                this.setAttribute((Attribute) attribute);
288                                
289                                list.add(attribute);
290                        }
291                        catch (Exception e)
292                        {
293                                // Ignore
294                        }
295                }
296                
297                return list;
298        }
299}