Google

Sep 2, 2014

Monitoring and gathering statistics with JMX based jconsole and Yammer metrics

This post extends  Yammer Metrics with Spring tutorial to demonstrate how the metrics can be monitored for the TradeEngineMain application.

Step 1: Run the TradeEngineMain  java class created in the Yammer metrics tutorial.

Step 2: From the DOS command prompt, type jconsole

C:\Users\akumaras>jconsole

Step 3:  The jconsole window pops up as shown below.



Step 4: You can monitor the statistics via the MBeans tab as shown below.



Drill down into each and observe the values.




With Yammer metrics, you can also output metrics to CSV, as JSON via RESTful web service, etc. It is quite a handy tool to monitor your applications.

Labels: , ,

Jun 2, 2014

Event Driven Programming Java example tutorial style - part 2 JMX enabling & jconsole

This extends the basic tutorial on event driven programming in Java. This tutorial covers JMX enabling the EventHub class vian MBeans and monitoring through JMX compliant tool like jconsole.

Step 1: Define the interface with the methods that are intractable via the JMX compliant client like jconsole.


package com.writtentest14;

public interface EventHubMBean {

 /**
  * Attributes
  */
    long getEventCount();
   
    
    /**
     *   Operations
     */
    void fireEventById(String eventId);
}


Step 2: Define the implementation of the above interface. Note it extends StandardMBean from the Java management API.

package com.writtentest14;

import javax.management.StandardMBean;

public class StandardEventHubMBean extends StandardMBean implements EventHubMBean {

    private EventHub eventHub;
    
    public StandardEventHubMBean(EventHub eventHub) {
        super(EventHubMBean.class, false);
        this.eventHub = eventHub;
    }

    @Override
    public long getEventCount() {
        return this.eventHub.getEventCount();
    }

    @Override
    public void fireEventById(String eventId) {
        Event event = new Event(eventId);
        this.eventHub.fire(event);
    }

}


Step 3: Now, the revised EventHib with JMX enabled.

package com.writtentest14;

import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/**
 * register and unregister event listeners
 */

public class EventHub {

 private static final EventHub INSTANCE = createInstance();
 
 private ConcurrentMap<String, List<EventListener<Event>>> registeredListeners = new ConcurrentHashMap<String, List<EventListener<Event>>>();

 private EventDispatcher synchronousDispatcher;

 private AtomicLong eventCount = new AtomicLong();

 public EventHub() {
  registerMBean();
 }

 public static EventHub instance() {
  return INSTANCE;
 }

 public EventHub(EventDispatcher synchronousDispatcher) {
  this.synchronousDispatcher = synchronousDispatcher;
  registerMBean();
 }

 public long getEventCount() {
  return this.eventCount.get();
 }

 

 private long getNextEventNumber() {
  return this.eventCount.incrementAndGet();
 }

 protected EventDispatcher getSynchronousDispatcher() {
  return this.synchronousDispatcher;
 }

 public void setSynchronousDispatcher(EventDispatcher dispatcher) {
  this.synchronousDispatcher = dispatcher;
 }

 public void fire(Event event) {
  dispatch(event, getSynchronousDispatcher());
 }

 
 public synchronized void addListener(String eventId, EventListener<Event> listener) {
  List<EventListener<Event>> listeners = this.registeredListeners.get(eventId);
  if (listeners != null) {
   listeners.add(listener);
  } else {
   listeners = new CopyOnWriteArrayList<EventListener<Event>>();
   listeners.add(listener);
   this.registeredListeners.put(eventId, listeners);
  }

 }


 public void removeListener(String eventId, EventListener<Event> listener) {
  List<EventListener<Event>> listeners = this.registeredListeners.get(eventId);
  if (listeners != null) {
   listeners.remove(listener);

  }
 }

 private void registerMBean() {
  MBeanServer server = getMBeanServer();
  StandardEventHubMBean mbean = new StandardEventHubMBean(this);
  try {
   server.registerMBean(mbean, getMBeanObjectName());
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

 protected void dispatch(Event event, EventDispatcher dispatcher) {
  getNextEventNumber();
  List<EventListener<Event>> listeners = getListeners(event);
  if (!listeners.isEmpty()) {

   dispatcher.dispatch(event, listeners);

  }
 }
 
  private static EventHub createInstance() {
         EventHub instance = new EventHub(new SimpleSynchronousDispatcher());
         return instance;
     }

 private List<EventListener<Event>> getListeners(Event event) {
  List<EventListener<Event>> listeners = this.registeredListeners.get(event.getId());
  return (listeners != null) ? listeners : Collections.<EventListener<Event>>emptyList();
 }

 private MBeanServer getMBeanServer() {
  ArrayList<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
  if (servers != null && !servers.isEmpty()) {
   return (MBeanServer) servers.get(0);
  } else {
   // If it all fails, get the platform default...
   return ManagementFactory.getPlatformMBeanServer();
  }
 }

 private ObjectName getMBeanObjectName() {
  try {
   String name = "com.com.writtentest14:type=EventHub,instance=" + hashCode();
   return new ObjectName(name);
  } catch (MalformedObjectNameException e) {
   throw new RuntimeException(e);
  }
 }

}


Step 4: Run the EveneHubMain defined in part 1. Type jconsole from a DOS command prompt.



Step 5: You can now view the attributes and operations defined in EventHubMBean interface.  It displays the number of events processed.



Step 6:  You can also fire an event by entering an event name and clicking on fireEventById(  ).




Labels: , ,

Jan 30, 2014

Exposing a Java class as a MXBean

This Java tutorial extends Java ExecutorService for multi-threading -- coding question and tutorial by exposing it as an MXBean so that MXBean properties can be viewed and set via JConsole as demonstrated below.

Step 1:  The changes from the previous tutorial are

1. It implements SumEngineMXBean interface  with the naming convention suffix MXBean and the methods that needs to be exposed must be defined.
2. The thread pool size is managed via threadPoolSize variable and it needs to have getThreadPoolSize( ) and setThreadPoolSize(int threadPoolSize) methods to view and modify this variable JConsole, which is a JMX console to monitor your JVM.
3. The getRequestsCount( ) is also managed via JMX to view the requests counts via JConsole.

package com.mycompany.metrics;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

public class SumEngine implements SumEngineMXBean{

private final AtomicInteger requestsCount = new AtomicInteger();

 private ExecutorService executionService = null;
 private int threadPoolSize = 1;

 //executes requests to sum
 public void execute(SumRequest... request) {
  executionService = Executors.newFixedThreadPool(threadPoolSize); //create a thread pool
  List<Callable<SumResponse>> tasks = createExecuteTasks(request);
  List<Future<SumResponse>> results = execute(tasks);
  for (Future<SumResponse> result : results) {

   try {
    System.out.println(Thread.currentThread().getName() + ": Response = " + result.get());
   } catch (InterruptedException e) {
    e.printStackTrace();
   } catch (ExecutionException e) {
    e.printStackTrace();
   }
  }
  
  //initiates an orderly shutdown of thread pool
  executionService.shutdown();
 }

 //create tasks
 private List<Callable<SumResponse>> createExecuteTasks(SumRequest[] requests) {
  List<Callable<SumResponse>> tasks = new LinkedList<Callable<SumResponse>>();
  executingRequests(requests.length);
  for (SumRequest req : requests) {
   Callable<SumResponse> task = createTask(req);
   tasks.add(task);
  }

  return tasks;
 }

 //increment the requests counter
 private void executingRequests(int count) {
  requestsCount.addAndGet(count);
 }

 //creates callable (i.e executable or runnable tasks) 
 private Callable<SumResponse> createTask(final SumRequest request) {
  // anonymous implementation of Callable.
  // Pre Java 8's way of creating closures
  Callable<SumResponse> task = new Callable<SumResponse>() {

   @Override
   public SumResponse call() throws Exception {
    System.out.println(Thread.currentThread().getName() + ": Request = " + request);
    SumProcessor<SumRequest, SumResponse> processor = new SumProcessorImpl<>();
    SumResponse result = processor.sum(request);
    return result;
   }

  };

  return task;
 }

 //executes the tasks
 private <T> List<Future<T>> execute(List<Callable<T>> tasks) {

  List<Future<T>> result = null;
  try {
   //invokes the sum(sumRequest) method by executing the closure call() inside createTask
   result = executionService.invokeAll(tasks);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }

  return result;

 }
 
 
 public int getRequestsCount(){
  return requestsCount.get();
 }
 
 

 public int getThreadPoolSize() {
  return threadPoolSize;
 }

 @Override
 public void setThreadPoolSize(int threadPoolSize) {
  this.threadPoolSize = threadPoolSize;
 } 
}


Step 2: The test class SumEngineTest that runs forever in an endless while loop. This allows you to monitor your app via JConsole as it runs. For example, change the threadPoolSize and view both the threadPoolSize  and  requestsCount. THe MBean server lines are also added to register the MXBean.

package com.mycompany.metrics;

import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class SumEngineTest {

 public static void main(String[] args) throws Exception {

  SumEngine se = new SumEngine();
  
  //Register with MBeanServer
  MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
  ObjectName name = new ObjectName("com.mycompany.metrics:type=SumEngine");
  mbs.registerMBean(se, name);

  List<SumRequest> list = new ArrayList<>();

  //run for ever
  while (true) {

   // sums 1+2, 2+3, 3+4, etc
   for (int i = 1; i <= 5; i++) {
    SumRequest req = new SumRequest();
    req.setOperand1(i);
    req.setOperand2(i + 1);
    list.add(req);
   }

   SumRequest[] req = new SumRequest[list.size()];
   se.execute((SumRequest[]) list.toArray(req));
   
   // sleep for 5 seconds
   TimeUnit.SECONDS.sleep(5); 
   list.clear();
  }

 }

}

Step 3: Run the SumEngineTest, and  wherever the engine is running,   go to the DOS command prompt and type jconsole. This pops up the jconsole GUI. Connect to com.mycompany.metrics.SumEngineTest.





You can try increase the threadPoolSize and check the output to see if more threads are used.  Also, look at the other tabs memory, threads, overview, etc.

Labels:

Jun 9, 2013

Apache Camel JMX link to introspect routes, end points, components, etc at runtime



This is an extension to the last two Apache Camel tutorials:

The JMX based MBeans provided  by Apache Camel is useful for debugging and negative testings where you need to stop one or more routes. The JMX can be initiated via JConsole. We touched on using JConsole for
in the earlier blog posts.

Step 1: Start the "StandAloneCamelWithSpring" that you created in the tutorial  Apache Camel With Spring tutorial - Part 2. Once this route is running, you need to invoke the JConsole from a DOS command prompt.   
 

Step 2: Select "local Process"  and the "StandAloneCamelWithSpring" as highlighted above, and then click on "Connect".

 Step 3: You need to select the "MBeans" tab to see all the  MBeans, and then  one of the MBeans is the Apache Camel under which you have components, routes, consumers, context, endpoint, etc available at runtime.



Step 4: The operations to stop and start routes are performed via the "operations" option as  highlighted below.




JConsole can be used to connect to other remote JVM processes by providing hostname, port, and login credentials.

By default, JMX instrumentation agent is enabled in Camel. This means that Camel runtime creates and registers MBean management objects with a MBeanServer instance in the VM. This allows Camel users instantly obtain insights into how Camel routes perform down to the individual processor's level.

The domain name of the MBean object can be configured by Java VM system property:

-Dorg.apache.camel.jmx.mbeanObjectDomainName=your.domain.name

Or, by adding a jmxAgent element inside the camelContext element in Spring configuration:

 
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
  <jmxAgent id="agent" mbeanObjectDomainName="your.domain.name"/>
    ...
</camelContext> 



JMX can be disabled

<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
  <jmxAgent id="agent" disabled="true"/>
    ...
</camelContext>

or

CamelContext camel = new DefaultCamelContext();
camel.disableJMX();


Labels: ,