1 package org.oddjob.jmx; 2 3 import javax.management.MBeanServerConnection ; 4 import javax.management.Notification ; 5 import javax.management.NotificationListener ; 6 import javax.management.ObjectName ; 7 import javax.management.relation.MBeanServerNotificationFilter ; 8 import javax.management.remote.JMXConnector ; 9 import javax.management.remote.JMXConnectorFactory ; 10 import javax.management.remote.JMXServiceURL ; 11 12 import org.apache.log4j.Logger; 13 import org.apache.log4j.MDC; 14 import org.oddjob.Resetable; 15 import org.oddjob.Stateful; 16 import org.oddjob.Stoppable; 17 import org.oddjob.Structural; 18 import org.oddjob.arooa.ArooaConstants; 19 import org.oddjob.arooa.ArooaContext; 20 import org.oddjob.arooa.ArooaRuntime; 21 import org.oddjob.arooa.registry.ComponentRegistry; 22 import org.oddjob.arooa.registry.Path; 23 import org.oddjob.framework.BaseComponent; 24 import org.oddjob.framework.Destroyable; 25 import org.oddjob.images.IconHelper; 26 import org.oddjob.jmx.client.ClientNode; 27 import org.oddjob.jmx.client.NotificationProcessor; 28 import org.oddjob.jmx.client.RemoteLogPoller; 29 import org.oddjob.jmx.server.OddjobMBeanFactory; 30 import org.oddjob.logging.ConsoleArchiver; 31 import org.oddjob.logging.Log4jArchiver; 32 import org.oddjob.logging.LogArchiver; 33 import org.oddjob.logging.LogLevel; 34 import org.oddjob.logging.LogListener; 35 import org.oddjob.state.JobState; 36 import org.oddjob.structural.ChildHelper; 37 import org.oddjob.structural.StructuralListener; 38 39 88 89 public class JMXClientJob extends BaseComponent 90 implements Runnable , Stateful, Resetable, 91 Stoppable, Structural, NotificationListener , 92 LogArchiver, ConsoleArchiver { 93 94 public static final long DEFAULT_LOG_POLLING_INTERVAL = 5000; 95 96 101 private String name; 102 103 104 private volatile boolean childDestroyed; 105 106 111 private String url; 112 113 114 private RemoteLogPoller logPoller; 115 116 117 private NotificationProcessor notificationProcessor; 118 119 120 private ChildHelper childHelper = new ChildHelper(this); 121 122 123 private ComponentRegistry parentRegistry; 124 125 126 private JMXConnector cntor; 127 128 129 135 private int maxLoggerLines; 136 137 143 private int maxConsoleLines; 144 145 151 private long logPollingInterval; 152 153 private static int instance; 154 private Logger theLogger; 155 156 161 public String getName() { 162 return name; 163 } 164 165 170 public void setName(String name) { 171 this.name = name; 172 } 173 174 protected Logger logger() { 175 if (theLogger == null) { 176 synchronized (JMXClientJob.class) { 177 theLogger = Logger.getLogger(JMXClientJob.class.getName() 178 + "." + String.valueOf(instance++)); 179 } 180 } 181 return theLogger; 182 } 183 184 public String getLogger() { 185 return logger().getName(); 186 } 187 188 193 public void setUrl(String lookup) { 194 this.url = lookup; 195 } 196 197 202 public String getUrl() { 203 return url; 204 } 205 206 209 public void addLogListener(LogListener l, Object component, LogLevel level, 210 long last, int history) { 211 if (logPoller == null) { 212 throw new NullPointerException ("logPoller not available"); 213 } 214 logPoller.addLogListener(l, component, level, last, history); 215 synchronized (this) { 217 notifyAll(); 218 } 219 } 220 221 224 public void removeLogListener(LogListener l, Object component) { 225 if (logPoller == null) { 226 return; 228 } 229 logPoller.removeLogListener(l, component); 230 } 231 232 235 public void addConsoleListener(LogListener l, Object component, long last, 236 int max) { 237 if (logPoller == null) { 238 throw new NullPointerException ("logPoller not available"); 239 } 240 logPoller.addConsoleListener(l, component, last, max); 241 synchronized (this) { 243 notifyAll(); 244 } 245 } 246 247 250 public void removeConsoleListener(LogListener l, Object component) { 251 if (logPoller == null) { 252 return; 254 } 255 logPoller.removeConsoleListener(l, component); 256 } 257 258 261 public String consoleIdFor(Object component) { 262 return logPoller.consoleIdFor(component); 263 } 264 265 public void init() { 266 if (maxConsoleLines == 0) { 267 maxConsoleLines = LogArchiver.MAX_HISTORY; 268 } 269 if (maxLoggerLines == 0) { 270 maxLoggerLines = LogArchiver.MAX_HISTORY; 271 } 272 if (logPollingInterval == 0) { 273 logPollingInterval = DEFAULT_LOG_POLLING_INTERVAL; 274 } 275 276 } 277 278 282 public boolean setContext(ArooaContext context) { 283 parentRegistry = (ComponentRegistry) context.get( 284 ArooaConstants.COMPONENT_REGISTRY); 285 arooaRuntime((ArooaRuntime) 286 context.get(ArooaConstants.CURRENTLY_CONFIGURING)); 287 return true; 288 } 289 290 public void run() { 291 lock.accquire("Job executing."); 292 String oldMDC = (String )MDC.get(Log4jArchiver.MDC); 293 try { 294 MDC.put(Log4jArchiver.MDC, logger().getName()); 295 if (!stateHandler.requestJobStateExecuting()) { 296 logger().debug("Can't execute job [" + this + "] because state is [" 297 + stateHandler.getJobState() + "]"); 298 return; 299 } 300 iconHelper.changeIcon(IconHelper.EXECUTING); 301 302 if (!configure()) { 304 return; 305 } 306 logger().info("Starting [" + this + "]"); 307 308 onStart(); 309 } 310 catch (Throwable e) { 311 logger().warn("Exception starting [" + this + "]", e); 312 setJobStateException(e); 313 return; 314 } 315 finally { 316 if (oldMDC != null) { 317 MDC.put(Log4jArchiver.MDC, oldMDC); 318 } 319 else { 320 MDC.remove(Log4jArchiver.MDC); 321 } 322 lock.release(); 323 } 324 } 325 326 330 private void onStart() throws Exception { 331 if (url == null) { 332 throw new IllegalStateException ("url must be provided."); 333 } 334 childDestroyed = false; 335 336 JMXServiceURL address = new JMXServiceURL (url); 337 338 logger().debug("Connecting to [" + url + "] ..."); 339 cntor = JMXConnectorFactory.connect(address); 340 341 MBeanServerConnection mbsc = cntor.getMBeanServerConnection(); 342 MBeanServerNotificationFilter serverFilter = new MBeanServerNotificationFilter (); 343 serverFilter.disableAllObjectNames(); 344 serverFilter.enableObjectName(OddjobMBeanFactory.rootName()); 345 mbsc.addNotificationListener(new ObjectName ("JMImplementation:type=MBeanServerDelegate"), 346 this, serverFilter, null); 347 348 if (parentRegistry == null) { 350 parentRegistry = new ComponentRegistry(); 351 } 352 353 notificationProcessor = new NotificationProcessor(); 354 notificationProcessor.start(); 355 356 Object client = ClientNode.createProxyFor( 358 OddjobMBeanFactory.rootName(), 359 mbsc, 360 this, 361 parentRegistry, 362 notificationProcessor); 363 364 this.logPoller = new RemoteLogPoller(client, 365 maxConsoleLines, maxLoggerLines); 366 childHelper.addChild(client); 367 logPoller.start(); 368 } 369 370 public void stop() { 371 if (destroyed) { 372 throw new IllegalStateException ("[" + this + "] destroyed"); 373 } 374 if (stateHandler.getJobState() != JobState.EXECUTING) { 375 return; 376 } 377 lock.accquire("Stopping."); 378 try { 379 logger().debug("Thread [" + Thread.currentThread().getName() 380 + "] requested [" + this + "] stop."); 381 iconHelper.changeIcon(IconHelper.STOPPING); 382 onStop(); 383 } 384 catch (Throwable t) { 385 logger().warn("Exception stopping [" + this + "]", t); 386 setJobStateException(t); 387 } 388 finally { 389 lock.release(); 390 } 391 } 392 393 private void onStop() throws Exception { 394 parentRegistry.removeChild(this); 395 396 logPoller.interrupt(); 397 logPoller.destroy(); 398 399 if (!childDestroyed) { 401 ((Destroyable) childHelper.getChild()).destroy(); 402 cntor.close(); 403 } 404 childHelper.removeAllChildren(); 405 406 notificationProcessor.interrupt(); 407 408 logPoller.join(); 409 notificationProcessor.join(); 410 411 childHelper.removeAllChildren(); 412 413 cntor = null; 414 logPoller = null; 415 notificationProcessor = null; 416 417 if (childDestroyed) { 418 setJobStateNotComplete(); 419 } 420 else { 421 setJobStateComplete(); 422 } 423 } 424 425 426 429 public void handleNotification(Notification notification, Object handback) { 430 if ("JMX.mbean.unregistered".equals(notification.getType())) { 431 logger().debug("Child unregestered in server."); 432 childDestroyed = true; 433 stop(); 434 } 435 } 436 437 451 public Object lookup(String propertyName) throws IllegalStateException { 452 if (parentRegistry == null) { 453 throw new IllegalStateException ("Client must be run before a lookup is possible"); 454 } 455 ComponentRegistry componentRegistry = parentRegistry.registryOwnedBy(this); 456 if (componentRegistry == null) { 457 throw new IllegalStateException ("Client not running."); 458 } 459 Object found = componentRegistry.objectForPath(new Path(propertyName)); 460 logger().debug("lookup [" + propertyName + "] found [" + found + "]"); 461 return found; 462 } 463 464 467 public void addStructuralListener(StructuralListener listener) { 468 childHelper.addStructuralListener(listener); 469 } 470 471 474 public void removeStructuralListener(StructuralListener listener) { 475 childHelper.removeStructuralListener(listener); 476 } 477 478 481 public void softReset() { 482 lock.accquire("Soft reset in progress."); 483 try { 484 logger().debug("Thread [" + Thread.currentThread().getName() 485 + "] soft reset for [" + this + "]."); 486 if (canSoftReset()) { 487 setJobStateReady(); 488 logger().info("Reset job [" + this + "]"); 489 } 490 } 491 finally { 492 lock.release(); 493 } 494 } 495 496 499 public void hardReset() { 500 lock.accquire("Hard reset in progress."); 501 try { 502 logger().debug("Thread [" + Thread.currentThread().getName() 503 + "] hard reset for [" + this + "]."); 504 if (canHardReset()) { 505 setJobStateReady(); 506 logger().info("Reset job [" + this + "]"); 507 } 508 } 509 finally { 510 lock.release(); 511 } 512 } 513 514 public int getMaxConsoleLines() { 515 return maxConsoleLines; 516 } 517 public void setMaxConsoleLines(int maxConsoleLines) { 518 this.maxConsoleLines = maxConsoleLines; 519 } 520 public int getMaxLoggerLines() { 521 return maxLoggerLines; 522 } 523 public void setMaxLoggerLines(int maxLoggerLines) { 524 this.maxLoggerLines = maxLoggerLines; 525 } 526 public long getLogPollingInterval() { 527 return logPollingInterval; 528 } 529 public void setLogPollingInterval(long logPollingInterval) { 530 this.logPollingInterval = logPollingInterval; 531 } 532 533 public String toString() { 534 if (name == null) { 535 return "Oddjob Client"; 536 } 537 else { 538 return name; 539 } 540 } 541 } 542 | Popular Tags |