1 package org.oddjob.jmx.client; 2 3 import java.io.IOException ; 4 import java.lang.reflect.InvocationHandler ; 5 import java.lang.reflect.Method ; 6 import java.lang.reflect.Proxy ; 7 import java.rmi.RemoteException ; 8 import java.util.Arrays ; 9 import java.util.HashMap ; 10 import java.util.Map ; 11 import java.util.Vector ; 12 13 import javax.management.JMException ; 14 import javax.management.MBeanServerConnection ; 15 import javax.management.Notification ; 16 import javax.management.NotificationListener ; 17 import javax.management.ObjectName ; 18 19 import org.apache.log4j.Logger; 20 import org.oddjob.Iconic; 21 import org.oddjob.OddjobException; 22 import org.oddjob.Stateful; 23 import org.oddjob.Structural; 24 import org.oddjob.arooa.Lifecycle; 25 import org.oddjob.arooa.registry.Address; 26 import org.oddjob.arooa.registry.ComponentRegistry; 27 import org.oddjob.arooa.registry.ServerId; 28 import org.oddjob.framework.BasePrimary; 29 import org.oddjob.framework.Destroyable; 30 import org.oddjob.framework.Exportable; 31 import org.oddjob.framework.Transportable; 32 import org.oddjob.images.IconEvent; 33 import org.oddjob.images.IconHelper; 34 import org.oddjob.images.IconListener; 35 import org.oddjob.jmx.SharedConstants; 36 import org.oddjob.jmx.Utils; 37 import org.oddjob.jmx.server.IconicInfo; 38 import org.oddjob.jmx.server.OddjobMBean; 39 import org.oddjob.jmx.server.ServerInfo; 40 import org.oddjob.jmx.server.StatefulInfo; 41 import org.oddjob.jmx.server.StructuralInfo; 42 import org.oddjob.logging.LogEvent; 43 import org.oddjob.state.JobStateEvent; 44 import org.oddjob.state.JobStateListener; 45 import org.oddjob.structural.ChildHelper; 46 import org.oddjob.structural.StructuralEvent; 47 import org.oddjob.structural.StructuralListener; 48 49 64 65 public class ClientNode implements InvocationHandler , Exportable { 66 private static final Logger logger = Logger.getLogger(ClientNode.class); 67 68 69 private static final Map methods = new HashMap (); 70 71 76 static { 77 Method [] ms = ClientNode.class.getMethods(); 78 for (int i = 0; i < ms.length; ++i) { 79 methods.put(uniqueMethodName(ms[i]), ms[i]); 80 } 81 } 82 83 private final NotificationProcessor notificationProcessor; 84 85 86 private final ClientStateHelper stateHelper; 87 88 89 private final ChildHelper structuralHelper; 90 91 92 private final ClientIconHelper iconHelper; 93 94 95 private final ObjectName objectName; 96 97 private final MBeanServerConnection serverConnection; 98 99 100 private final Object proxy; 101 102 104 private ComponentRegistry componentRegistry; 105 106 107 private final ClientListener clientListener; 108 109 110 private ServerInfo serverInfo; 111 112 113 private String toString; 114 115 private final static int RESYNC_PENDING = 0; 116 117 private final static int RESYNC_STARTED = 1; 118 119 private final static int RESYNC_FINISHED = 2; 120 121 private final static int DESTROYED = 3; 122 123 private volatile int phase = RESYNC_PENDING; 124 125 126 127 138 private ClientNode(ObjectName objectName, 139 MBeanServerConnection serverConnection, 140 NotificationProcessor notificationProcessor) 141 throws JMException , IOException { 142 143 this.objectName = objectName; 144 this.serverConnection = serverConnection; 145 this.notificationProcessor = notificationProcessor; 146 147 Vector interfaces = new Vector (); 148 interfaces.add(Destroyable.class); 149 interfaces.add(Exportable.class); 150 interfaces.add(LogPollable.class); 151 152 updateServerInfo(); 153 updateToString(); 155 156 Class [] serverSideInterfaces = serverInfo.getInterfaces(); 157 interfaces.addAll(Arrays.asList(serverSideInterfaces)); 158 159 Class [] interfaceArray = (Class []) interfaces.toArray(new Class [0]); 160 if (logger.isDebugEnabled()) { 161 String debug = ""; 162 for (int i = 0; i < interfaceArray.length; ++i) { 163 debug += "[" + interfaceArray[i].getName() + "]"; 164 } 165 logger.debug("Creating proxy for [" + objectName + " interfaces " + debug + "]"); 166 } 167 this.proxy = Proxy.newProxyInstance(ClientNode.class.getClassLoader(), 168 interfaceArray, this); 169 170 if (proxy instanceof Structural) { 171 structuralHelper = new ChildHelper(proxy); 172 } else { 173 structuralHelper = null; 174 } 175 if (proxy instanceof Stateful) { 176 stateHelper = new ClientStateHelper(proxy); 177 } else { 178 stateHelper = null; 179 } 180 if (proxy instanceof Iconic) { 181 iconHelper = new ClientIconHelper(proxy); 182 } else { 183 iconHelper = null; 184 } 185 186 clientListener = new ClientListener(); 187 serverConnection.addNotificationListener(objectName, 188 clientListener, null, null); 189 logger.debug("Registered for notifications. Client Node creation complete."); 190 } 191 192 203 public static Object createProxyFor(ObjectName objectName, 204 MBeanServerConnection serverConnection, 205 Object linkNode, 206 ComponentRegistry parentRegistry, 207 NotificationProcessor notificationProcessor) 208 throws JMException , IOException { 209 ClientNode client = new ClientNode(objectName, serverConnection, notificationProcessor); 210 client.componentRegistry = new ComponentRegistry( 211 new ServerId(client.serverInfo.getUrl())); 212 client.componentRegistry.register(client.serverInfo.getId(), client.proxy); 213 parentRegistry.addChild(client.componentRegistry, linkNode); 214 client.resync(); 215 return client.proxy; 216 } 217 218 226 static protected Object createProxyFor(ObjectName objectName, 227 ClientNode parent) 228 throws JMException , IOException { 229 ClientNode client = new ClientNode(objectName, 230 parent.serverConnection, parent.notificationProcessor); 231 parent.updateServerInfo(); 232 if (parent.serverInfo.isRegistryOwner()) { 233 ComponentRegistry childRegistry 234 = parent.componentRegistry.registryOwnedBy(parent.proxy); 235 if (childRegistry == null) { 236 client.componentRegistry = new ComponentRegistry( 237 new ServerId(client.serverInfo.getUrl())); 238 parent.componentRegistry.addChild(client.componentRegistry, parent.proxy); 239 client.componentRegistry.register( 240 parent.serverInfo.getChildId(), parent.proxy); 241 logger.debug("Created new registry for server [" 242 + client.serverInfo.getUrl() + "]"); 243 } 244 else { 245 client.componentRegistry = childRegistry; 246 } 247 } 248 else { 249 client.componentRegistry = parent.componentRegistry; 250 } 253 254 client.componentRegistry.register(client.serverInfo.getId(), client.proxy); 255 256 client.resync(); 257 return client.proxy; 258 } 259 260 264 public String toString() { 265 return toString; 266 } 267 268 272 void updateServerInfo() 273 throws IOException , JMException { 274 this.serverInfo = (ServerInfo) serverConnection.invoke( 275 objectName, 276 SharedConstants.SERVER_INFO_METHOD, 277 new Object [0], 278 new String [0]); 279 } 280 281 284 void updateToString() 285 throws IOException , JMException { 286 toString = (String ) serverConnection.invoke(objectName, 287 SharedConstants.TO_STRING_METHOD, 288 new Object [0], new String [0]); 289 logger.debug("Retrieved [" + toString + "] as toString for remote node."); 290 } 291 292 295 public Object invoke(Object proxy, Method method, Object [] args) 296 throws Exception { 297 298 String umn = uniqueMethodName(method); 299 Method localMethod = (Method ) methods.get(umn); 300 if (localMethod != null) { 301 return localMethod.invoke(this, args); 303 } 304 else { 305 logger.debug("Invoking remotely [" + umn + "] on [" + toString() + "]"); 306 Object [] exported = Utils.export(args); 307 String [] signature = Utils.classArray2StringArray(method.getParameterTypes()); 308 309 Object result = null; 310 result = serverConnection.invoke(objectName, 311 method.getName(), 312 exported, 313 signature); 314 return Utils.importResolve(result, componentRegistry); 315 } 316 } 317 318 324 void resync() throws JMException , IOException { 325 if (phase != RESYNC_PENDING) { 326 return; 327 } 328 logger.debug("Starting resync invocation for [" + toString + "]"); 329 serverConnection.invoke(objectName, 330 SharedConstants.RESYNC_METHOD, new Object [0], 331 new String [0]); 332 logger.debug("Completed resync invocation for [" + toString + "]"); 333 notificationProcessor.check(this); 334 } 335 336 342 public void addJobStateListener(JobStateListener listener) { 343 stateHelper.addJobStateListener(listener); 344 } 345 346 352 public void removeJobStateListener(JobStateListener listener) { 353 stateHelper.removeJobStateListener(listener); 354 } 355 356 357 359 public void addIconListener(IconListener listener) { 360 iconHelper.addIconListener(listener); 361 } 362 363 public void removeIconListener(IconListener listener) { 364 iconHelper.removeIconListener(listener); 365 } 366 367 369 372 public void addStructuralListener(StructuralListener listener) { 373 structuralHelper.addStructuralListener(listener); 374 } 375 376 379 public void removeStructuralListener(StructuralListener listener) { 380 structuralHelper.removeStructuralListener(listener); 381 } 382 383 385 public LogEvent[] retrieveLogEvents(long from, int max) { 386 try { 387 return (LogEvent[]) serverConnection.invoke(objectName, 388 SharedConstants.RETRIEVE_LOG_EVENTS_METHOD, 389 new Object [] { 390 new Long (from), 391 new Integer (max) }, 392 new String [] { 393 Long .class.getName(), Integer .class.getName() }); 394 } catch (Exception e) { 395 throw new OddjobException(e); 396 } 397 } 398 399 public LogEvent[] retrieveConsoleEvents(long from, int max) { 400 try { 401 return (LogEvent[]) serverConnection.invoke(objectName, 402 SharedConstants.RETRIEVE_CONSOLE_EVENTS_METHOD, 403 new Object [] { 404 new Long (from), 405 new Integer (max) }, 406 new String [] { 407 Long .class.getName(), Integer .class.getName() }); 408 } catch (Exception e) { 409 throw new OddjobException(e); 410 } 411 } 412 413 421 public String consoleId() { 422 if (serverInfo.getConsoleId() == null) { 424 throw new NullPointerException ("No ConsoleArchiver availble on server."); 425 } 426 return serverInfo.getConsoleId(); 427 } 428 429 431 public String url() { 432 return serverInfo.getConsoleId(); 433 } 434 435 437 441 public class DeadNode extends BasePrimary 442 implements Stateful { 443 private static final long serialVersionUID = 20051130; 444 private final ObjectName objectName; 445 public DeadNode(ObjectName objectName) { 446 this.objectName = objectName; 447 } 448 void exception(Exception e) { 449 stateHandler.setJobStateException(e); 450 iconHelper.changeIcon(IconHelper.EXCEPTION); 451 } 452 public String toString() { 453 return "Failed Accessing [" + objectName + "]"; 454 } 455 } 456 457 462 public class ClientListener implements NotificationListener { 463 public void handleNotification(Notification notification, Object object) { 465 String type = notification.getType(); 466 logger.debug("Handling notification [" + type + "] sequence [" 467 + notification.getSequenceNumber() + "] for [" + toString 468 + "]"); 469 470 if (phase == DESTROYED) { 471 logger.debug("Ignoring notification as client destroyed [" + toString + "]"); 472 return; 473 } 474 475 if (phase == RESYNC_PENDING 476 && OddjobMBean.RESYNC_STARTED_NOTIF_TYPE.equals(type)) { 477 phase = RESYNC_STARTED; 478 logger.debug("Beginning of Resync Notifcation Recieved for [" + toString + "]"); 479 return; 481 } 482 if (phase == RESYNC_STARTED 483 && OddjobMBean.RESYNC_FINISHED_NOTIF_TYPE.equals(type)) { 484 phase = RESYNC_FINISHED; 485 logger.debug("End of Resync Notifcation Recieved for [" + toString + "]"); 486 return; 488 } 489 490 if (phase == RESYNC_PENDING) { 492 logger.debug("Waiting to start resync - ignoring notification type [" + type + "]"); 493 return; 494 } 495 496 if (phase == RESYNC_FINISHED) { 497 String stem = type.substring(OddjobMBean.RESYNC_PREFIX.length()); 498 if (stem.equals(OddjobMBean.RESYNC_PREFIX)) { 499 logger.debug("Ignoring resync [" + type + "]"); 500 return; 501 } 502 } 503 504 if (phase == RESYNC_STARTED) { 505 type = type.substring(OddjobMBean.RESYNC_PREFIX.length(), 506 type.length()); 507 } 508 509 if (StatefulInfo.STATE_CHANGE_NOTIF_TYPE.equals(type)) { 510 final JobStateEvent event = (JobStateEvent) notification 511 .getUserData(); 512 Runnable r = new Runnable () { 513 public void run() { 514 stateHelper.jobStateChange(event); 515 } 516 }; 517 notificationProcessor.enqueue(r); 518 } else if (StructuralInfo.CHILD_ADDED_NOTIF_TYPE.equals(type)) { 519 final StructuralEvent se = (StructuralEvent) notification 520 .getUserData(); 521 Runnable r = new Runnable () { 522 public void run() { 523 int index = se.getIndex(); 524 ObjectName childName = (ObjectName ) se.getChild(); 525 Object childProxy = null; 526 try { 527 childProxy = ClientNode.createProxyFor(childName, 528 ClientNode.this); 529 } catch (Exception e) { 530 DeadNode deadNode = new DeadNode(childName); 531 deadNode.exception(e); 532 childProxy = deadNode; 533 } 534 structuralHelper.insertChild(index, childProxy); 535 } 536 }; 537 notificationProcessor.enqueue(r); 538 } else if (StructuralInfo.CHILD_REMOVED_NOTIF_TYPE.equals(type)) { 539 final StructuralEvent se = (StructuralEvent) notification 540 .getUserData(); 541 Runnable r = new Runnable () { 542 public void run() { 543 int index = se.getIndex(); 544 try { 545 Object child = structuralHelper.removeChildAt(index); 549 Lifecycle.destroy(child); 550 } catch (Exception e) { 551 logger.error("Exception removing child [" + index 552 + "] from [" + toString + "]", e); 553 } 554 } 555 }; 556 notificationProcessor.enqueue(r); 557 } else if (IconicInfo.ICON_CHANGED_NOTIF_TYPE.equals(type)) { 558 final IconEvent ie = (IconEvent) notification.getUserData(); 559 Runnable r = new Runnable () { 560 public void run() { 561 try { 562 iconHelper.iconEvent(ie); 563 } catch (Exception e) { 564 logger.debug(e); 566 } 567 } 568 }; 569 notificationProcessor.enqueue(r); 570 } 571 } 572 } 573 574 581 public boolean equals(Object other) { 582 return (other == proxy); 583 } 584 585 588 public void destroy() { 589 phase = DESTROYED; 590 componentRegistry.remove(proxy); 591 try { 594 if (clientListener != null) { 596 serverConnection.removeNotificationListener(objectName, 597 clientListener); 598 } 599 } catch (JMException e) { 600 logger.debug(e); 601 } catch (IOException e) { 602 logger.debug(e); 603 } 604 if (structuralHelper != null) { 607 structuralHelper.destroyAll(); 608 } 609 logger.debug("Destroyed client for [" + toString + "]"); 610 } 611 612 618 public Transportable exportTransportable() { 619 Address address = componentRegistry.addressForObject(proxy); 620 logger.debug("[" + proxy + "] exported with address [" + address + "]"); 621 ComponentTransportable transportable = new ComponentTransportable(address); 622 return transportable; 623 } 624 625 626 633 static String uniqueMethodName(Method m) { 634 String methodName = m.getName(); 635 Class [] paramTypes = m.getParameterTypes(); 636 StringBuffer buf = new StringBuffer (); 637 buf.append(methodName); 638 for (int j = 0; j < paramTypes.length; ++j) { 639 buf.append('#'); 640 buf.append(paramTypes[j].getName()); 641 } 642 return buf.toString(); 643 } 644 } 645 | Popular Tags |