Coverage Report - argos.proxy.DynamicProxy
 
Classes in this File Line Coverage Branch Coverage Complexity
DynamicProxy
0%
0/230
0%
0/144
0
 
 1  
 /*
 2  
 Copyright (c) 2006, University of Tromsø
 3  
 All rights reserved.
 4  
 
 5  
 Redistribution and use in source and binary forms, with or without 
 6  
 modification, are permitted provided that the following conditions are met:
 7  
 
 8  
  * Redistributions of source code must retain the above copyright notice, this list 
 9  
    of conditions and the following disclaimer.
 10  
 
 11  
  * Redistributions in binary form must reproduce the above copyright notice, this 
 12  
    list of conditions and the following disclaimer in the documentation and/or other 
 13  
    materials provided with the distribution.
 14  
 
 15  
  * Neither the name of the University of Tromsø nor the names of its contributors may 
 16  
    be used to endorse or promote products derived from this software without specific 
 17  
    prior written permission.
 18  
 
 19  
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 20  
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 21  
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 22  
 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 23  
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
 24  
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
 25  
 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 26  
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 27  
 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
 28  
 DAMAGE.
 29  
 */
 30  
 
 31  
 package argos.proxy;
 32  
 
 33  
 import java.lang.reflect.Field;
 34  
 import java.lang.reflect.InvocationTargetException;
 35  
 import java.lang.reflect.Method;
 36  
 import java.lang.reflect.Modifier;
 37  
 import java.util.*;
 38  
 import java.util.logging.Level;
 39  
 import java.util.logging.Logger;
 40  
 
 41  
 import javax.management.*;
 42  
 
 43  
 import argos.annotation.*;
 44  
 import argos.naming.NamingService;
 45  
 
 46  
 /**
 47  
  * Created on 24. juni 2006, 18:50
 48  
  * 
 49  
  * @author Dan Peder Eriksen
 50  
  */
 51  
 public final class DynamicProxy implements DynamicMBean, NotificationProxy, NotificationEmitter {
 52  0
         private static final Logger logger = Logger.getLogger(DynamicProxy.class.getName());
 53  
         
 54  
         public static final String NO_DESCRIPTION = "Description not set";
 55  
         
 56  
         private static final String FOR = " for ";
 57  
         public static final String ACTION = "action";
 58  
         public static final String ACTION_INFO = "action info";
 59  
         public static final String INFO = "info";
 60  
         
 61  
         private List<NotificationListener> listeners;
 62  
         private List<NotificationFilter> filters;
 63  
         private List<Object> handbacks;
 64  
         
 65  
         private Object mbean;
 66  
         private String name;
 67  
         private MBeanInfo mbeanInfo;
 68  
         private Map<String, Method> getterAttributes;
 69  
         private Map<String, Method> setterAttributes;
 70  
         private Map<String, List<Method>> operations;
 71  
         private Method notificationInfoMethod;
 72  
         private boolean verbose;
 73  
         
 74  
         public DynamicProxy(String namingServiceName, Object mbean, Boolean verbose) {
 75  0
                 super();
 76  0
                 this.name = namingServiceName;
 77  0
                 listeners = new ArrayList<NotificationListener>();
 78  0
                 filters = new ArrayList<NotificationFilter>();
 79  0
                 handbacks = new ArrayList<Object>();
 80  
                 
 81  0
                 this.mbean = mbean;
 82  0
                 this.verbose = verbose;
 83  0
                 Class<? extends Object> c = mbean.getClass();
 84  
                 
 85  
                 // Collect meta information
 86  0
                 if(c != null) {
 87  0
                         List<AttributeContainer> attributes = new ArrayList<AttributeContainer>();
 88  0
                         Map<String, Boolean> isMap = new HashMap<String, Boolean>();
 89  0
                         getterAttributes = new HashMap<String, Method>();
 90  0
                         setterAttributes = new HashMap<String, Method>();
 91  0
                         operations = new HashMap<String, List<Method>>();
 92  
                         
 93  0
                         boolean dontInstrument = c.isAnnotationPresent(RemoveInstrumentation.class);
 94  
                         
 95  
                         Method[] methods;
 96  0
                         if(c.isAnnotationPresent(InstrumentThisClassOnly.class)) {
 97  0
                                 methods = c.getDeclaredMethods();
 98  
                         }
 99  
                         else {
 100  0
                                 methods = c.getMethods();
 101  
                         }
 102  
                         
 103  
                         // Attributes and operations
 104  0
                         for(Method method : methods) {
 105  
                                 // notification info method
 106  0
                                 if(method.isAnnotationPresent(NotificationInfo.class)) {
 107  0
                                         if(method.getParameterTypes().length == 0) {
 108  0
                                                 notificationInfoMethod = method;
 109  0
                                                 continue;
 110  
                                         }
 111  
                                         else {
 112  0
                                                 logger.warning(method.getName() + " is marked with "
 113  
                                                                 + "NotificationInfo annotation but requires "
 114  
                                                                 + "parameters to invoke. method is disregarded.");
 115  
                                         }
 116  
                                 }
 117  
                                 
 118  
                                 // Instrument?
 119  0
                                 if(isRemoveJMX(method) || (dontInstrument && !isInstrument(method))
 120  
                                                 || !Modifier.isPublic(method.getModifiers())
 121  
                                                 || Modifier.isStatic(method.getModifiers())) {
 122  0
                                         continue;
 123  
                                 }
 124  
                                 
 125  0
                                 if(!method.getDeclaringClass().getName().equals(Object.class.getName())) {
 126  0
                                         if(method.getName().startsWith("get")
 127  
                                                         && method.getParameterTypes().length == 0
 128  
                                                         && getterAttributes.get(method.getName()) == null) {
 129  0
                                                 String methodName = method.getName().substring(3);
 130  0
                                                 getterAttributes.put(methodName, method);
 131  0
                                                 if(!attributes.contains(methodName)) {
 132  0
                                                         attributes.add(new AttributeContainer(methodName,
 133  
                                                                         getDescription(method)));
 134  
                                                 }
 135  0
                                         }
 136  0
                                         else if(method.getName().startsWith("is")
 137  
                                                         && method.getParameterTypes().length == 0
 138  
                                                         && getterAttributes.get(method.getName()) == null) {
 139  0
                                                 String methodName = method.getName().substring(2);
 140  0
                                                 getterAttributes.put(methodName, method);
 141  0
                                                 isMap.put(methodName, Boolean.TRUE);
 142  0
                                                 if(!attributes.contains(methodName)) {
 143  0
                                                         attributes.add(new AttributeContainer(methodName,
 144  
                                                                         getDescription(method)));
 145  
                                                 }
 146  0
                                         }
 147  0
                                         else if(method.getName().startsWith("set")
 148  
                                                         && method.getParameterTypes().length == 1
 149  
                                                         && setterAttributes.get(method.getName()) == null) {
 150  0
                                                 String methodName = method.getName().substring(3);
 151  0
                                                 setterAttributes.put(methodName, method);
 152  0
                                                 if(!attributes.contains(methodName)) {
 153  0
                                                         attributes.add(new AttributeContainer(methodName,
 154  
                                                                         getDescription(method)));
 155  
                                                 }
 156  0
                                         }
 157  
                                         else {
 158  0
                                                 if(operations.get(method.getName()) == null) {
 159  0
                                                         List<Method> temp = new ArrayList<Method>();
 160  0
                                                         temp.add(method);
 161  0
                                                         operations.put(method.getName(), temp);
 162  0
                                                 }
 163  
                                                 else {
 164  0
                                                         List<Method> temp = operations.get(method.getName());
 165  0
                                                         temp.add(method);
 166  
                                                 }
 167  
                                         }
 168  
                                 }
 169  
                         }
 170  
                         
 171  
                         // Attribute Info
 172  0
                         MBeanAttributeInfo[] openAttributes = new MBeanAttributeInfo[attributes
 173  
                                         .size()];
 174  0
                         for(int i = 0; i < attributes.size(); i++) {
 175  0
                                 String attributeName = attributes.get(i).getName();
 176  0
                                 String attDescription = attributes.get(i).getDescription();
 177  0
                                 if(attDescription.equals(NO_DESCRIPTION)) {
 178  0
                                         Method setMethod = setterAttributes.get(attributeName);
 179  0
                                         if(setMethod == null) {
 180  0
                                                 attDescription = NO_DESCRIPTION;
 181  
                                         }
 182  
                                         else {
 183  0
                                                 attDescription = getDescription(setMethod);
 184  
                                         }
 185  
                                 }
 186  
                                 
 187  
                                 try {
 188  0
                                         openAttributes[i] = new MBeanAttributeInfo(attributeName, attDescription,
 189  
                                                         getterAttributes.get(attributeName), setterAttributes.get(attributeName));
 190  
                                 }
 191  0
                                 catch(IntrospectionException e) {
 192  0
                                         if(verbose) {
 193  0
                                                 logger.warning("IntrospectionException: " + e.getMessage());
 194  0
                                                 logger.warning("IntrospectionException: get "
 195  
                                                                 + getterAttributes.get(attributeName).getName());
 196  0
                                                 logger.warning("IntrospectionException: set "
 197  
                                                                 + setterAttributes.get(attributeName).getName());
 198  
                                         }
 199  0
                                 }
 200  
                         }
 201  
                         
 202  
                         // Operation Info
 203  0
                         List<MBeanOperationInfo> operInfos = new ArrayList<MBeanOperationInfo>();
 204  0
                         for(List<Method> list : operations.values()) {
 205  0
                                 for(Method method : list) {
 206  0
                                         String operDescription = getDescription(method);
 207  
                                         
 208  
                                         // Impact
 209  0
                                         int impact = MBeanOperationInfo.ACTION;
 210  0
                                         Impact tempImp = method.getAnnotation(Impact.class);
 211  0
                                         if(tempImp == null) {
 212  0
                                                 if(verbose) {
 213  0
                                                         logger.warning(mbean.getClass().getName() + "."
 214  
                                                                         + method.getName() + "() is missing @Impact");
 215  
                                                 }
 216  
                                         }
 217  
                                         else {
 218  0
                                                 if(tempImp.value() == Impact.ACTION) {
 219  0
                                                         impact = MBeanOperationInfo.ACTION;
 220  
                                                 }
 221  0
                                                 else if(tempImp.value() == Impact.ACTION_INFO) {
 222  0
                                                         impact = MBeanOperationInfo.ACTION_INFO;
 223  
                                                 }
 224  0
                                                 else if(tempImp.value() == Impact.INFO) {
 225  0
                                                         impact = MBeanOperationInfo.INFO;
 226  
                                                 }
 227  
                                         }
 228  
                                         
 229  
                                         // Signature
 230  0
                                         MBeanParameterInfo[] signature = new MBeanParameterInfo[method
 231  
                                                         .getParameterTypes().length];
 232  0
                                         Class<?>[] classes = method.getParameterTypes();
 233  0
                                         for(int i = 0; i < classes.length; i++) {
 234  0
                                                 signature[i] = new MBeanParameterInfo(classes[i].getName(),
 235  
                                                                 classes[i].getName(), "No Description");
 236  
                                         }
 237  0
                                         operInfos.add(new MBeanOperationInfo(method.getName(),
 238  
                                                         operDescription, signature, method.getReturnType().getName(),
 239  
                                                         impact));
 240  0
                                 }
 241  
                         }
 242  
                         
 243  0
                         MBeanConstructorInfo[] openConstructors = new MBeanConstructorInfo[0];
 244  0
                         MBeanOperationInfo[] openOperations = new MBeanOperationInfo[operInfos.size()];
 245  0
                         for(int i = 0; i < operInfos.size(); i++) {
 246  0
                                 openOperations[i] = operInfos.get(i);
 247  
                         }
 248  
                         
 249  
                         MBeanNotificationInfo[] notifications;
 250  0
                         if(notificationInfoMethod == null) {
 251  0
                                 notifications = getNotificationInfo();
 252  
                         }
 253  
                         else {
 254  
                                 try {
 255  0
                                         notifications = (MBeanNotificationInfo[]) notificationInfoMethod
 256  
                                                         .invoke(mbean, new Object[0]);
 257  
                                 }
 258  0
                                 catch(Exception e) {
 259  0
                                         logger.log(Level.SEVERE, "Unable to call the component "
 260  
                                                         + mbean.getClass().getName() + " method marked with "
 261  
                                                         + "the annotation NotificationInfo");
 262  0
                                         notifications = getNotificationInfo();
 263  0
                                 }
 264  
                         }
 265  
                         
 266  0
                         String mbeanDescription = "MBean desciption not set";
 267  0
                         mbeanDescription = getDescription(c);
 268  0
                         mbeanInfo = new MBeanInfo(mbean.getClass().getName(), mbeanDescription,
 269  
                                         openAttributes, openConstructors, openOperations, notifications);
 270  
                         
 271  
                         // Register at NamingService
 272  0
                         if(!NamingService.getInstance().addNotificationProxy(this)) {
 273  0
                                 logger.severe("Unable to add component, name supplied alreayd exists: "
 274  
                                                 + namingServiceName);
 275  0
                                 return;
 276  
                         }
 277  
 
 278  
                         //Notifcation proxy
 279  0
                         for(Field field : mbean.getClass().getFields()) {
 280  0
                                 if(field.isAnnotationPresent(argos.annotation.NotificationSender.class)) {
 281  
                                         try {
 282  0
                                                 field.set(mbean, this);
 283  
                                         }
 284  0
                                         catch(Exception e) {
 285  0
                                                 logger.log(Level.SEVERE, "Unable to set notification proxy for class "
 286  
                                                                 + mbean.getClass().getName(), e);
 287  0
                                         }
 288  
                                 }
 289  
                         }
 290  
                 }
 291  0
         }
 292  
         
 293  
         public Object getAttribute(String attribute) throws AttributeNotFoundException {
 294  0
                 Method method = getterAttributes.get(attribute);
 295  0
                 if(method != null) {
 296  
                         try {
 297  0
                                 return method.invoke(mbean);
 298  
                         }
 299  0
                         catch(IllegalAccessException e) {
 300  0
                                 if(verbose) {
 301  0
                                         logger.warning("Unable to get attribute " + attribute + FOR
 302  
                                                         + mbean.getClass().getName() + ": IllegalAccessException "
 303  
                                                         + e.getMessage());
 304  
                                 }
 305  
                         }
 306  0
                         catch(InvocationTargetException ie) {
 307  0
                                 if(verbose) {
 308  0
                                         logger.warning("Unable to get attribute " + attribute + FOR
 309  
                                                         + mbean.getClass().getName() + ": InvocationTargetException "
 310  
                                                         + ie.getMessage());
 311  
                                 }
 312  0
                         }
 313  
                 }
 314  0
                 throw new AttributeNotFoundException("Attribute not found " + attribute);
 315  
         }
 316  
         
 317  
         public AttributeList getAttributes(String[] attributes) {
 318  0
                 AttributeList list = new AttributeList(attributes.length);
 319  0
                 for(String att : attributes) {
 320  
                         try {
 321  0
                                 list.add(new Attribute(att, getAttribute(att)));
 322  
                         }
 323  0
                         catch(AttributeNotFoundException e) {
 324  0
                                 logger.log(Level.WARNING, e.getMessage(), e);
 325  0
                         }
 326  
                 }
 327  0
                 return list;
 328  
         }
 329  
         
 330  
         public Object invoke(String actionName, Object[] params, String[] signature) 
 331  
                         throws MBeanException, ReflectionException {
 332  0
                 List<Method> list = operations.get(actionName);
 333  0
                 if(list != null) {
 334  0
                         for(Method method : list) {
 335  0
                                 Class<?>[] paramTypes = method.getParameterTypes();
 336  0
                                 if(signature.length == paramTypes.length) {
 337  0
                                         boolean match = true;
 338  0
                                         for(int i = 0; i < signature.length; i++) {
 339  0
                                                 if(!signature[i].equals(paramTypes[i].getName())) {
 340  0
                                                         match = false;
 341  0
                                                         break;
 342  
                                                 }
 343  
                                         }
 344  0
                                         if(match) {
 345  
                                                 try {
 346  0
                                                         return method.invoke(mbean, params);
 347  
                                                 }
 348  0
                                                 catch(IllegalAccessException e) {
 349  0
                                                         if(verbose) {
 350  0
                                                                 logger.warning("Unable to invoke " + actionName + FOR
 351  
                                                                                 + mbean.getClass().getName()
 352  
                                                                                 + ": IllegalAccessException " + e.getMessage());
 353  
                                                         }
 354  0
                                                         throw new ReflectionException(e);
 355  
                                                 }
 356  0
                                                 catch(InvocationTargetException ie) {
 357  0
                                                         if(verbose) {
 358  0
                                                                 logger.warning("Unable to invoke " + actionName + FOR
 359  
                                                                                 + mbean.getClass().getName()
 360  
                                                                                 + ": InvocationTargetException "
 361  
                                                                                 + ie.getMessage());
 362  
                                                         }
 363  0
                                                         throw new ReflectionException(ie);
 364  
                                                 }
 365  
                                         }
 366  
                                 }
 367  0
                         }
 368  0
                         StringBuffer sb = new StringBuffer();
 369  0
                         for(String param : signature) {
 370  0
                                 sb.append(param);
 371  0
                                 if(!signature[signature.length - 1].equals(param)) {
 372  0
                                         sb.append(", ");
 373  
                                 }
 374  
                         }
 375  0
                         logger.severe("Unable to find correct method: " + actionName + "("
 376  
                                         + sb.toString() + ")");
 377  
                 }
 378  0
                 throw new MBeanException(new RuntimeException("Method " + actionName + " with the given " +
 379  
                 "arguments and signatures does not exist."));
 380  
         }
 381  
         
 382  
         private boolean setAttribute(String attribute, Object value) {
 383  0
                 Method method = setterAttributes.get(attribute);
 384  0
                 if(method != null) {
 385  
                         try {
 386  0
                                 method.invoke(mbean, value);
 387  0
                                 return true;
 388  
                         }
 389  0
                         catch(IllegalAccessException e) {
 390  0
                                 if(verbose) {
 391  0
                                         logger.warning("Unable to set attribute " + attribute + FOR
 392  
                                                         + mbean.getClass().getName() + ": IllegalAccessException "
 393  
                                                         + e.getMessage());
 394  
                                 }
 395  
                         }
 396  0
                         catch(InvocationTargetException ie) {
 397  0
                                 if(verbose) {
 398  0
                                         logger.warning("Unable to set attribute " + attribute + FOR
 399  
                                                         + mbean.getClass().getName() + ": InvocationTargetException "
 400  
                                                         + ie.getMessage());
 401  
                                 }
 402  0
                         }
 403  
                 }
 404  0
                 return false;
 405  
         }
 406  
         
 407  
         public void setAttribute(Attribute attribute) {
 408  0
                 setAttribute(attribute.getName(), attribute.getValue());
 409  0
         }
 410  
         
 411  
         public AttributeList setAttributes(AttributeList attributes) {
 412  0
                 AttributeList list = new AttributeList(attributes.size());
 413  0
                 for(int i = 0; i < attributes.size(); i++) {
 414  
                         Attribute att;
 415  
                         try {
 416  0
                                 att = (Attribute) attributes.get(i);
 417  
                         }
 418  0
                         catch(ClassCastException e) {
 419  0
                                 if(verbose) {
 420  0
                                         logger.warning("Unable to set attribute number " + i + FOR
 421  
                                                         + mbean.getClass().getName() + " due to ClassCastException: "
 422  
                                                         + e.getMessage());
 423  
                                 }
 424  0
                                 continue;
 425  0
                         }
 426  0
                         if(setAttribute(att.getName(), att.getValue())) {
 427  0
                                 list.add(att);
 428  
                         }
 429  
                 }
 430  
                 
 431  0
                 return list;
 432  
         }
 433  
         
 434  
         public MBeanInfo getMBeanInfo() {
 435  0
                 return mbeanInfo;
 436  
         }
 437  
         
 438  
         public Object getMbean() {
 439  0
                 return mbean;
 440  
         }
 441  
         
 442  
         public String getName() {
 443  0
                 return name;
 444  
         }
 445  
         
 446  
         private static String getDescription(Method method) {
 447  0
                 Description annotation = method.getAnnotation(Description.class);
 448  0
                 if(annotation == null) {
 449  0
                         logger.fine(method.getName() + "() is missing @Description");
 450  0
                         return NO_DESCRIPTION;
 451  
                 }
 452  
                 else {
 453  0
                         return annotation.value();
 454  
                 }
 455  
         }
 456  
         
 457  
         private String getDescription(Class<? extends Object> c) {
 458  0
                 Description annotation = c.getAnnotation(Description.class);
 459  0
                 if(annotation == null) {
 460  0
                         if(verbose) {
 461  0
                                 logger.warning(c.getName() + " class is missing @Description");
 462  
                         }
 463  0
                         return NO_DESCRIPTION;
 464  
                 }
 465  
                 else {
 466  0
                         return annotation.value();
 467  
                 }
 468  
         }
 469  
         
 470  
         private static boolean isRemoveJMX(Method method) {
 471  0
                 return method.isAnnotationPresent(RemoveInstrumentation.class);
 472  
         }
 473  
         
 474  
         private static boolean isInstrument(Method method) {
 475  0
                 return method.isAnnotationPresent(Instrument.class);
 476  
         }
 477  
         
 478  
         public void addNotificationListener(NotificationListener listener,
 479  
                         NotificationFilter filter, Object handback) {
 480  0
                 listeners.add(listener);
 481  0
                 filters.add(filter);
 482  0
                 handbacks.add(handback);
 483  0
         }
 484  
         
 485  
         public void removeNotificationListener(NotificationListener listener) {
 486  0
                 for(int i = 0; i < listeners.size(); i++) {
 487  0
                         if(listeners.get(i).equals(listener)) {
 488  0
                                 listeners.remove(i);
 489  0
                                 filters.remove(i);
 490  0
                                 handbacks.remove(i);
 491  
                         }
 492  
                 }
 493  0
         }
 494  
         
 495  
         public void removeNotificationListener(NotificationListener listener,
 496  
                         NotificationFilter filter, Object handback) {
 497  0
                 for(int i = 0; i < listeners.size(); i++) {
 498  0
                         if(listeners.get(i).equals(listener)) {
 499  0
                                 listeners.remove(i);
 500  0
                                 filters.remove(i);
 501  0
                                 handbacks.remove(i);
 502  
                         }
 503  
                 }
 504  0
         }
 505  
         
 506  
         public MBeanNotificationInfo[] getNotificationInfo() {
 507  0
                 if(notificationInfoMethod != null) {
 508  
                         try {
 509  0
                                 return (MBeanNotificationInfo[]) notificationInfoMethod.invoke(mbean,
 510  
                                                 new Object[0]);
 511  
                         }
 512  0
                         catch(ClassCastException e) {
 513  0
                                 logger.severe(notificationInfoMethod.getName()
 514  
                                                 + " doesnt return MBeanNotificationInfo[]");
 515  
                         }
 516  0
                         catch(Exception e2) {
 517  0
                                 logger.log(Level.SEVERE, "Unable to call method "
 518  
                                                 + notificationInfoMethod.getName(), e2);
 519  0
                         }
 520  
                 }
 521  0
                 return new MBeanNotificationInfo[0];
 522  
         }
 523  
         
 524  
         public void send(Notification not) {
 525  0
                 for(int z = 0; z < listeners.size(); z++) {
 526  0
                         if(filters.get(z) == null || filters.get(z).isNotificationEnabled(not)) {
 527  0
                                 listeners.get(z).handleNotification(not, handbacks.get(z));
 528  
                         }
 529  
                 }
 530  0
         }
 531  
 }