Coverage Report - argos.deploy.ComponentRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
ComponentRunner
0%
0/204
0%
0/98
0
ComponentRunner$Worker
0%
0/38
0%
0/8
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.deploy;
 32  
 
 33  
 import java.io.IOException;
 34  
 import java.lang.management.ManagementFactory;
 35  
 import java.lang.reflect.Field;
 36  
 import java.lang.reflect.InvocationTargetException;
 37  
 import java.lang.reflect.Method;
 38  
 import java.net.MalformedURLException;
 39  
 import java.util.*;
 40  
 import java.util.logging.Level;
 41  
 import java.util.logging.Logger;
 42  
 
 43  
 import javax.management.*;
 44  
 import javax.management.remote.JMXConnector;
 45  
 import javax.management.remote.JMXConnectorFactory;
 46  
 import javax.management.remote.JMXServiceURL;
 47  
 import javax.management.timer.Timer;
 48  
 
 49  
 import argos.annotation.*;
 50  
 import argos.config.Config;
 51  
 import argos.metadata.AttributeValue;
 52  
 import argos.metadata.ComponentMetaInfo;
 53  
 import argos.naming.NamingService;
 54  
 import argos.proxy.DynamicProxyUtil;
 55  
 import argos.proxy.NotificationProxy;
 56  
 
 57  
 /**
 58  
  * Created on 07.jul.2006
 59  
  * 
 60  
  * @author Dan Peder Eriksen
 61  
  */
 62  0
 public class ComponentRunner implements NotificationFilter, NotificationListener {
 63  
         public static final long serialVersionUID = 2807813948753461L;
 64  
         
 65  0
         private static final Logger logger = Logger.getLogger(ComponentRunner.class.getName());
 66  
         private static final String NOTIFICATION_TYPE = "RunExecute";
 67  
         private static final String COMPONENT_EVENT = "COMPONENT_EVENT";
 68  
         private static final String EXECUTE_HANDBACK = "EXECUTE_HANDBACK";
 69  
         
 70  
         
 71  
         private ComponentMetaInfo meta;
 72  
         private Object component;
 73  
         private ObjectName objectName;
 74  
         private transient Method unload;
 75  
         private transient Method execute;
 76  
         private transient Method notificationHandler;
 77  
         private transient Timer timer;
 78  
         private List<String> listenToRemote;
 79  
         
 80  
         public ComponentRunner(ComponentMetaInfo meta) {
 81  0
                 super();
 82  0
                 this.meta = meta;
 83  0
                 listenToRemote = new ArrayList<String>();
 84  0
         }
 85  
         
 86  
         public Object start() {
 87  0
                 Method init = null;
 88  0
                 String domain = null;
 89  
                 try {
 90  
                         // Create instance
 91  0
                         MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
 92  0
                         ObjectName classLoaderName = new ObjectName("Classloaders:name=" + meta.getService().getName());
 93  0
                         if(!mbeanServer.isRegistered(classLoaderName)) {
 94  0
                                 mbeanServer.registerMBean(meta.getService().getClassLoader(), classLoaderName);
 95  
                         }
 96  
                         
 97  0
                         Class<?> compClass = Class.forName(meta.getClassName(), true, meta.getService().getClassLoader());
 98  0
                         component = compClass.newInstance();
 99  
                         
 100  
                         // Create MBean
 101  0
                         domain = Config.get(Config.DEFAULT_COMPONENT_DOMAIN);
 102  0
                         if(meta.getService().getName().startsWith(Config.BANG_BANG)) {
 103  0
                                 domain = Config.get(Config.DEFAULT_BANGBANG_DOMAIN);
 104  
                         }
 105  0
                         objectName = DynamicProxyUtil.instrument(domain, meta.getName(), component, classLoaderName);
 106  
                         
 107  
                         // Find marked methods
 108  0
                         for(Method method : compClass.getMethods()) {
 109  0
                                 if(method.getParameterTypes().length == 0) {
 110  0
                                         if(method.isAnnotationPresent(Init.class)) {
 111  0
                                                 if(init != null) {
 112  0
                                                         logger.warning("Found more than one method in component " + 
 113  
                                                                         meta.getName() + " marked with Init annotation.");
 114  
                                                 }
 115  0
                                                 init = method;
 116  
                                         }
 117  0
                                         else if(method.isAnnotationPresent(Unload.class)) {
 118  0
                                                 if(unload != null) {
 119  0
                                                         logger.warning("Found more than one method in component " + 
 120  
                                                                         meta.getName() + " marked with Init annotation.");
 121  
                                                 }
 122  0
                                                 unload = method;
 123  
                                         }
 124  0
                                         else if(method.isAnnotationPresent(Execute.class)) {
 125  0
                                                 execute = method;
 126  0
                                                 timer = new Timer();
 127  0
                                                 timer.addNotificationListener(this, this, EXECUTE_HANDBACK);
 128  0
                                                 Execute ex = execute.getAnnotation(Execute.class);
 129  0
                                                 timer.addNotification(NOTIFICATION_TYPE, "execute", null,
 130  
                                                                 Calendar.getInstance().getTime(), (int) (ex.value() * 1000));
 131  0
                                         }
 132  
                                 }
 133  
                                 else {
 134  0
                                         if(method.isAnnotationPresent(Init.class)) {
 135  0
                                                 logger.severe("Method marked with @Init can not take arguments");
 136  
                                         }
 137  0
                                         else if(method.isAnnotationPresent(Unload.class)) {
 138  0
                                                 logger.severe("Method marked with @Unload can not take arguments");
 139  
                                         }
 140  0
                                         else if(method.isAnnotationPresent(Execute.class)) {
 141  0
                                                 logger.severe("Method marked with @Exceute can not take arguments");
 142  
                                         }
 143  
                                         
 144  0
                                         if(method.getParameterTypes().length == 1 && 
 145  
                                                         Notification.class.isAssignableFrom(method.getParameterTypes()[0]) &&
 146  
                                                         method.isAnnotationPresent(NotificationHandler.class)) {
 147  0
                                                 notificationHandler = method;
 148  
                                         }
 149  
                                         // Config
 150  0
                                         else if(method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
 151  0
                                                 for(AttributeValue att : meta.getAttributeValues()) {
 152  0
                                                         if(method.getName().substring(3).equalsIgnoreCase(att.getName())) {
 153  
                                                                 try {
 154  0
                                                                         Object[] types = method.getParameterTypes();
 155  0
                                                                         Object value = null;
 156  
                                                                         // Transform to the right type
 157  0
                                                                         if(types[0].toString().equals("int")) {
 158  0
                                                                                 value = Integer.parseInt(att.getValue());
 159  
                                                                         }
 160  0
                                                                         else if(types[0].toString().equals("short")) {
 161  0
                                                                                 value = Short.parseShort(att.getValue());
 162  
                                                                         }
 163  0
                                                                         else if(types[0].toString().equals("long")) {
 164  0
                                                                                 value = Long.parseLong(att.getValue());
 165  
                                                                         }
 166  0
                                                                         else if(types[0].toString().equals("double")) {
 167  0
                                                                                 value = Double.parseDouble(att.getValue());
 168  
                                                                         }
 169  0
                                                                         else if(types[0].toString().equals("byte")) {
 170  0
                                                                                 value = Byte.parseByte(att.getValue());
 171  
                                                                         }
 172  0
                                                                         else if(types[0].toString().equals("character")) {
 173  0
                                                                                 value = att.getValue().charAt(0);
 174  
                                                                         }
 175  0
                                                                         else if(types[0].equals(String.class)) {
 176  0
                                                                                 value = att.getValue();
 177  
                                                                         }
 178  0
                                                                         else if(types[0].equals(Integer.class)) {
 179  0
                                                                                 value = Integer.valueOf(Integer.parseInt(att
 180  
                                                                                                 .getValue()));
 181  
                                                                         }
 182  0
                                                                         else if(types[0].equals(Short.class)) {
 183  0
                                                                                 value = Short.valueOf(Short
 184  
                                                                                                 .parseShort(att.getValue()));
 185  
                                                                         }
 186  0
                                                                         else if(types[0].equals(Long.class)) {
 187  0
                                                                                 value = Long.valueOf(Long.parseLong(att.getValue()));
 188  
                                                                         }
 189  0
                                                                         else if(types[0].equals(Double.class)) {
 190  0
                                                                                 value = Double.valueOf(Double.parseDouble(att
 191  
                                                                                                 .getValue()));
 192  
                                                                         }
 193  0
                                                                         else if(types[0].equals(Byte.class)) {
 194  0
                                                                                 value = Byte.valueOf(Byte.parseByte(att.getValue()));
 195  
                                                                         }
 196  0
                                                                         else if(types[0].equals(Character.class)) {
 197  0
                                                                                 value = Character.valueOf(att.getValue().charAt(0));
 198  
                                                                         }
 199  
                                                                         else {
 200  0
                                                                                 logger.warning("Unknown type: " + types[0].toString());
 201  
                                                                         }
 202  0
                                                                         method.invoke(component, value);
 203  
                                                                 }
 204  0
                                                                 catch(InvocationTargetException e) {
 205  0
                                                                         logger.log(Level.SEVERE, "Unable to set attribute "
 206  
                                                                                                         + att.getName()
 207  
                                                                                                         + ", check log for exception ", e);
 208  0
                                                                 }
 209  
                                                         }
 210  
                                                 }
 211  
                                         }
 212  
                                 }
 213  
                                 
 214  
                         }
 215  
                         
 216  
                         //Set class loader in component
 217  0
                         for(Field field : component.getClass().getFields()) {
 218  0
                                 if(field.isAnnotationPresent(argos.annotation.ServiceClassLoader.class)) {
 219  
                                         try {
 220  0
                                                 field.set(component, meta.getService().getClassLoader());
 221  
                                         }
 222  0
                                         catch(Exception e) {
 223  0
                                                 logger.log(Level.SEVERE, "Unable to set classloader for class "
 224  
                                                                 + component.getClass().getName(), e);
 225  0
                                         }
 226  
                                 }
 227  0
                                 else if(field.isAnnotationPresent(ServiceMeta.class)) {
 228  
                                         try {
 229  0
                                                 field.set(component, meta.getService());
 230  
                                         }
 231  0
                                         catch(Exception e) {
 232  0
                                                 logger.log(Level.SEVERE, "Unable to set service meta for class "
 233  
                                                                 + component.getClass().getName(), e);
 234  0
                                         }
 235  
                                 }
 236  0
                                 else if(field.isAnnotationPresent(ComponentMeta.class)) {
 237  
                                         try {
 238  0
                                                 field.set(component, meta);
 239  
                                         }
 240  0
                                         catch(Exception e) {
 241  0
                                                 logger.log(Level.SEVERE, "Unable to set component meta for class "
 242  
                                                                 + component.getClass().getName(), e);
 243  0
                                         }
 244  
                                 }
 245  0
                                 else if(field.isAnnotationPresent(Component.class)) {
 246  
                                         try {
 247  0
                                                 Component annontation = field.getAnnotation(Component.class);
 248  0
                                                 field.set(component, 
 249  
                                                                 NamingService.getInstance().getComponentByName(annontation.value()));
 250  
                                         }
 251  0
                                         catch(Exception e) {
 252  0
                                                 logger.log(Level.SEVERE, "Unable to set component in class "
 253  
                                                                 + component.getClass().getName(), e);
 254  0
                                         }
 255  
                                 }
 256  
                         }
 257  
                         
 258  0
                         meta.setListenerProxy(this);
 259  0
                         meta.setMbeanName(domain + ":name=" + meta.getName());
 260  
                         
 261  
                         // Init
 262  0
                         if(init != null) {
 263  
                                 try {
 264  0
                                     ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 265  0
                                     Thread.currentThread().setContextClassLoader(meta.getService().getClassLoader());
 266  0
                                         init.invoke(component);
 267  0
                                         Thread.currentThread().setContextClassLoader(classLoader);
 268  
                                 }
 269  0
                                 catch(Exception e) {
 270  0
                                         logger.log(Level.SEVERE, "Unable to run init method on component " + 
 271  
                                                         meta.getName() + ": " + e.getMessage(), e);
 272  0
                                         removeMbeanIgnore();
 273  0
                                         return null;
 274  0
                                 }
 275  
                         }
 276  
                         
 277  
                         // Add local listeners
 278  0
                         for(String comp : meta.getListenTo()) {
 279  
                                 Object o;
 280  0
                                 if(comp.startsWith(Config.BANG_BANG)) {
 281  0
                                         o = NamingService.getInstance().getBangBang(comp);
 282  
                                 }
 283  
                                 else {
 284  0
                                         o = NamingService.getInstance().getComponentByName(comp);
 285  
                                 }
 286  
                                 
 287  0
                                 if(o == null) {
 288  0
                                         logger.severe(meta.getName() + " can not listen to " + comp
 289  
                                                         + " as it doesnt exists.");
 290  
                                 }
 291  
                                 else {
 292  
                                         try {
 293  0
                                                 NotificationProxy proxy = NamingService.getInstance().getNotificationProxy(o);
 294  0
                                                 if(proxy == null) {
 295  0
                                                         logger.severe("Unable to find NotificationProxy for " + comp);
 296  
                                                 }
 297  
                                                 else {
 298  0
                                                         proxy.addNotificationListener(this, this, COMPONENT_EVENT);
 299  
                                                 }
 300  
                                                 
 301  
                                         }
 302  0
                                         catch(ClassCastException e) {
 303  0
                                                 logger.severe(meta.getName() + " can not listen to " + comp
 304  
                                                                 + " as it doesnt implement NotificationEmitter.");
 305  0
                                                 stop();
 306  0
                                         }
 307  
                                 }
 308  0
                         }
 309  
                         
 310  
                         //Add remote listeners
 311  0
                         Map<String, JMXConnector> map = new HashMap<String, JMXConnector>();
 312  0
                         List<String> list = meta.getListenToRemote();
 313  0
                         for(int i = 0; i < list.size(); i +=2) {
 314  0
                                 String url = list.get(i);
 315  0
                                 String comp = list.get(i + 1);
 316  0
                                 JMXConnector connector = map.get(url);
 317  
                                 try {
 318  
                                         MBeanServerConnection server;
 319  0
                                         if(connector == null) {
 320  0
                                                 connector = JMXConnectorFactory.connect(new JMXServiceURL(url)); 
 321  0
                                                 map.put(url, connector);
 322  0
                                                 server = connector.getMBeanServerConnection();
 323  
                                         }
 324  
                                         else {
 325  0
                                                 server = connector.getMBeanServerConnection();
 326  
                                         }
 327  0
                                         server.addNotificationListener(new ObjectName(comp), this, null, COMPONENT_EVENT);
 328  
                                 }
 329  0
                                 catch(InstanceNotFoundException e) {
 330  0
                                         logger.severe("Unable to remote listen to " + comp + " on url " + url);
 331  
                                 }
 332  0
                                 catch(MalformedURLException e) {
 333  0
                                         logger.severe("Unable to remote listen to " + comp + " url is malformed " + url + ": " + e.getMessage());
 334  
                                 }
 335  0
                                 catch(IOException e) {
 336  0
                                         logger.warning("Unable to connect to " + url + ", trying later.");
 337  0
                                         listenToRemote.add(url);
 338  0
                                         listenToRemote.add(comp);
 339  
                                 }
 340  0
                                 catch(MalformedObjectNameException e) {
 341  0
                                         logger.severe("Malformed object name: " + comp + ": " + e.getMessage());
 342  0
                                 }
 343  
                         }
 344  0
                         if(!listenToRemote.isEmpty()) {
 345  0
                                 Worker worker = new Worker(this);
 346  0
                                 worker.start();
 347  0
                                 logger.info("Started thread for adding remote listeners when they becomme availible.");
 348  
                         }
 349  
                         
 350  
                         // Start Timer
 351  0
                         if(timer != null) {
 352  0
                                 timer.start();
 353  
                         }
 354  
                         
 355  
                         // Done
 356  0
                         logger.info("Component " + meta.getName() + " has been started.");
 357  
                 }
 358  0
                 catch(ClassNotFoundException e) {
 359  0
                         logger.severe("Unable to start component " + meta.getName()
 360  
                                         + " due to ClassNotFoundException");
 361  0
                         removeMbeanIgnore();
 362  0
                         return null;
 363  
                 }
 364  0
                 catch(IllegalAccessException e2) {
 365  0
                         logger.severe("Unable to start component " + meta.getName()
 366  
                                         + " due to IllegalAccessException: " + e2.getMessage());
 367  0
                         removeMbeanIgnore();
 368  0
                         return null;
 369  
                 }
 370  0
                 catch(Exception e3) {
 371  0
                         logger.log(Level.SEVERE, "Unable to start component " + meta.getName()
 372  
                                         + " due to Exception: " + e3.getMessage(), e3);
 373  0
                         removeMbeanIgnore();
 374  0
                         return null;
 375  0
                 }
 376  
                 
 377  0
                 return component;
 378  
         }
 379  
         
 380  
         private void removeMbeanIgnore() {
 381  
                 try {
 382  0
                         ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
 383  
                 }
 384  0
                 catch(Exception e) {
 385  0
                         logger.log(Level.FINE, "Ignoring Exception", e);
 386  0
                 }
 387  0
         }
 388  
         
 389  
         public void stop() {
 390  0
                 if(timer != null) {
 391  0
                         timer.stop();
 392  
                 }
 393  0
                 if(unload != null) {
 394  
                         try {
 395  0
                                 unload.invoke(component);
 396  
                         }
 397  0
                         catch(Exception e) {
 398  0
                                 logger.log(Level.SEVERE, "Unable to run unload method check log.", e);
 399  0
                         }
 400  
                 }
 401  
                 try {
 402  0
                         ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
 403  
                 }
 404  0
                 catch(Exception e) {
 405  0
                         logger.log(Level.SEVERE, "Unable to unregister mbean " + objectName, e);
 406  0
                 }
 407  0
                 logger.info("Stopped component " + meta.getName());
 408  0
         }
 409  
         
 410  
         public boolean isNotificationEnabled(Notification not) {
 411  0
                 return true;
 412  
         }
 413  
         
 414  
         public void handleNotification(Notification not, Object handback) {
 415  0
                 if(EXECUTE_HANDBACK.equals(handback)){
 416  
                         try {
 417  0
                                 execute.invoke(component);
 418  
                         }
 419  0
                         catch(Exception e) {
 420  0
                                 logger.log(Level.SEVERE, "Component " + meta.getName() + ": Unable to call method marked with "
 421  
                                                 + "annotation execute: " + e.getMessage(), e);
 422  0
                         }
 423  
                 }
 424  
                 else {
 425  
                         try {
 426  0
                                 Class<?> to = Class.forName(notificationHandler.getParameterTypes()[0].getName(), true, meta.getService().getClassLoader());
 427  0
                                 Object o = to.cast(not);
 428  0
                                 notificationHandler.invoke(component, o);
 429  
                         }
 430  0
                         catch(Exception e) {
 431  0
                                 logger.log(Level.SEVERE, "Component " + meta.getName() + ": Unable to call method marked with "
 432  
                                                 + "annotation NotificationHandler: " + e.getMessage(), e);
 433  0
                         }
 434  
                 }
 435  0
         }
 436  
         
 437  
         public ComponentMetaInfo getMeta() {
 438  0
                 return meta;
 439  
         }
 440  
         
 441  
         class Worker extends Thread {
 442  
                 private ComponentRunner runner;
 443  0
                 public Worker(ComponentRunner runner) {
 444  0
                         super("Worker");
 445  0
                         this.runner = runner;
 446  0
                 }
 447  
                 
 448  
                 @Override
 449  
                 public void run() {
 450  0
                         while(!listenToRemote.isEmpty()) {
 451  0
                                 Map<String, JMXConnector> map = new HashMap<String, JMXConnector>();
 452  0
                                 List<String> list = meta.getListenToRemote();
 453  0
                                 for(int i = 0; i < list.size(); i +=2) {
 454  0
                                         String url = list.get(i);
 455  0
                                         String comp = list.get(i + 1);
 456  0
                                         JMXConnector connector = map.get(url);
 457  
                                         try {
 458  
                                                 MBeanServerConnection server;
 459  0
                                                 if(connector == null) {
 460  0
                                                         connector = JMXConnectorFactory.connect(new JMXServiceURL(url)); 
 461  0
                                                         map.put(url, connector);
 462  0
                                                         server = connector.getMBeanServerConnection();
 463  
                                                 }
 464  
                                                 else {
 465  0
                                                         server = connector.getMBeanServerConnection();
 466  
                                                 }
 467  0
                                                 server.addNotificationListener(new ObjectName(comp), runner, runner, COMPONENT_EVENT);
 468  0
                                                 listenToRemote.remove(i);
 469  0
                                                 listenToRemote.remove(i + 1);
 470  0
                                                 i -= 2;
 471  
                                         }
 472  0
                                         catch(MalformedURLException e) {
 473  0
                                                 logger.severe("Unable to remote listen to " + comp + " on url " + url);
 474  
                                         }
 475  0
                                         catch(MalformedObjectNameException e) {
 476  0
                                                 logger.severe("Unable to remote listen to " + comp + " on url " + url);
 477  
                                         }
 478  0
                                         catch(IOException e) {
 479  0
                                                 logger.severe("Unable to remote listen to " + comp + " on url " + url);
 480  
                                         }
 481  0
                                         catch(InstanceNotFoundException e) {
 482  0
                                                 logger.severe("Unable to remote listen to " + comp + " on url " + url);
 483  0
                                         }
 484  
                                 }
 485  0
                                 for(JMXConnector connector : map.values()) {
 486  
                                         try {
 487  0
                                                 connector.close();
 488  
                                         }
 489  0
                                         catch(IOException e) {
 490  0
                                                 logger.log(Level.FINE, "Error while disconnecting from jmx connector", e);
 491  0
                                         }
 492  
                                 }
 493  0
                         }
 494  
                         try {
 495  0
                                 sleep(60 * 1000);
 496  
                         }
 497  0
                         catch(InterruptedException ignore) {}
 498  0
                 }
 499  
         }
 500  
 }