Google

Oct 23, 2014

Debugging and working like a pro with a Java application tutorial with eclipse IDE

Often you get to work on a fully functional Java application that is already in production to fix production issues or to enhance its existing functionality. As a new Java developer in the team, it is not easy to get started and contributing. The approach would be slightly different from working on a brand new project. Here are a few tips that will help you hit the road running as quickly as possible.

This post will also help you answer a common developer interview question --

Q. How will you hit the road running fast on an enhancement or a bug fix task of a large  existing application with 1000+ artifacts?
A. Key terms are: Setting up the environment, familiarizing with the app, reverse engineering, impact analysis, monitoring the logs, and collaborating  with the team members and knowledge transferring.

Step #1: Firstly, you need to get the application running locally in your development environment. So, with the help of your existing team members and online documentation provided via the internal wiki or confluence page you need to set up your development environment to run the application.
  1. Get all the Java tools required like Java, Maven, eclipse IDE, Tomcat/JBoss Server, tex editor like Notepad++, ssh emulator like putty/cygwin/mobaXterm/MSYS, tortoise SVN, etc
  2. Set up your Java and Maven locally
  3. Checkout the relevant Java projects from a code repository like Subversion (aka SVN).
  4. Set up data sources, environment variables, and .properties files.
  5. Build and deploy the Java application locally in your machine.

Step #2: Run the application locally, and get a basic familiarity as to how to navigate through the application.  Focus on the functionality you will be working with.


Step #3: A level of reverse engineering will be required to navigate through the actual user interface and then drill down into the code that is responsible for the functionality you are focusing on foxing on enhancing. Here are the tips to do that.


Step #3.1: Run the application in debug mode by modifying the log4j config file. This will print more log info like class names, method names, and stored procedure names being executed while you perform the UI navigation. You can then check the log file to get a list of relevant class names, methods, stored procedures, etc.

Step #3.1.1: Once you have the relevant class and method details, you can

Start to work like an eclipse pro to navigate through code:

You can use CTRL+SHIFT+T to search for Java class files



You can use CTRL+SHIFT+R to search for any resource files like .properties, .xml (Spring context file), .sql files, etc



Once you are within a Java class files, you can search for a particular method with CTRL+O.



You can drill down into other classes with F3 and Ctrl + left mouse click. The CTRL+ mouse click is very powerful drill down from ClassA to ClassB method invocations.



Step #3.1.2: Once you have worked out which artifacts to be modified, it is essential to perform impact analysis as to who is using these artifacts and if your fixes or enhancements going to impact any other existing functionality.

Start to work like an eclipse pro to perform impact analysis:

Highlight either a class name or a method name and press CTRL+SHIFT+G to find out from which classes and methods it is used or referenced from.


You can also highlight the class and press CTRL+H to search both Java classes and non Java type files like Spring context files, properties files, etc. This will cast a wider search where you have a number of tabs like file, Java, JavaScript, etc to search from.



Step #3.2: As an alternative to running the application in debug mode, you can also pick some labels key text on the GUI, and then use CTRL+H and then select the "File Search" tab to search for the web resources like .jsp, ,html, .xml, etc that have that search text. You can then drill down the other artifacts from there.

Step #4: When you are ready to make the code changes you can use code assist short cut keys described in  top 10 eclipse short-cut keys every Java eclipse developer must know.


Step #5: If you can't remember these short-cut keys initially, all you have to remember is

CTRL+SHIFT+L once
CTRL+SHIFT+L twice  


While unscrambling the relationship between the software functionality and code, maintain a documentation and produce high level diagrams. For example:

CashUI.jsp --> CashController.java --> CashService.java --> CashDao.java --&gt  getAvailbleCash.sql

Obviously you need to collaborate with a number of different team members to pick their knowledge, but most often team members can;t spoon feed you, and you need to hit the road running fast. This requires both good technical know how and great soft skills to get the job done.

Labels: ,

Oct 22, 2014

Yammer metrics to monitor RESTful web services and report them via its admin web

This is a Yammer metrics tutorial to monitor RESTful web services. This extends RESTEasy web service tutorial basic and the basic stand-alone Yammer tutorial.

Step 1: The pom.xml file needs to have relevant dependencies defined relating to Yammer and Spring Web in addition to the JAX-RS librraies that was covered in the previous tutorial.


  <!-- Yammer Metrics -->
  <dependency>
   <groupId>com.yammer.metrics</groupId>
   <artifactId>metrics-core</artifactId>
   <scope>compile</scope>
   <version>${metrics.version}</version>
  </dependency>
  <dependency>
   <groupId>com.yammer.metrics</groupId>
   <artifactId>metrics-annotation</artifactId>
   <scope>compile</scope>
   <version>${metrics.version}</version>
  </dependency>
  <dependency>
   <groupId>com.yammer.metrics</groupId>
   <artifactId>metrics-spring</artifactId>
   <scope>runtime</scope>
   <version>${metrics.version}</version>
  </dependency>
  <dependency>
   <groupId>com.yammer.metrics</groupId>
   <artifactId>metrics-servlet</artifactId>
   <scope>compile</scope>
   <version>${metrics.version}</version>
  </dependency>
  <dependency>
   <groupId>com.yammer.metrics</groupId>
   <artifactId>metrics-web</artifactId>
   <scope>runtime</scope>
   <version>${metrics.version}</version>
  </dependency>

  <!-- Servelet API -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <scope>provided</scope>
   <version>${javax-servlet.version}</version>
  </dependency>

  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <scope>compile</scope>
   <version>${spring.version}</version>
  </dependency>


Step 2: Add metrics annotation to the RESTful web service interface and implementation

package com.mytutorial;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

import com.yammer.metrics.annotation.ExceptionMetered;
import com.yammer.metrics.annotation.Timed;

@Path("/myapp")
public interface SimpleRestWeb {

 @GET
 @Path("/name/{name}")
 @Timed
 @ExceptionMetered
 public String sayHello(@PathParam("name") String name);

}


package com.mytutorial;

import java.util.concurrent.atomic.AtomicLong;

import com.yammer.metrics.annotation.Gauge;
import com.yammer.metrics.annotation.Timed;

public class SimpleRestWebImpl implements SimpleRestWeb {

 @Gauge
 AtomicLong requestCount = new AtomicLong();
 
 
@Override
@Metered
 public String sayHello(String name) {
  String result = "Hello " + name;
  requestCount.incrementAndGet();
  return result;
 }

}


Note: A gauge "requestCount" was added to count the number of requests.

Step 3: The applicationContext.xml file under com/mytutorial with mterics bootstrapping.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:metrics="http://www.yammer.com/schema/metrics"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
                        http://www.yammer.com/schema/metrics http://www.yammer.com/schema/metrics/metrics.xsd">
    
    <metrics:metrics-registry id="rest-metrics"/>
    <metrics:health-check-registry id="rest-health"/>
    <metrics:annotation-driven metrics-registry="rest-metrics" health-check-registry="rest-health" proxy-target-class="true" />
    
     <bean id="simpleRestWeb" class="com.mytutorial.SimpleRestWebImpl"  />
    
    
</beans>


Step 4: The web.xml file with the Yammer admin servlet and relevant listeners defined.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>


 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:/com/mytutorial/applicationContext.xml</param-value>
 </context-param>

 <context-param>
  <param-name>resteasy.use.deployment.sensitive.factory</param-name>
  <param-value>false</param-value>
 </context-param>

 <context-param>
  <param-name>resteasy.servlet.mapping.prefix</param-name>
  <param-value>/rest</param-value>
 </context-param>


 <listener>
  <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
 </listener>
 <listener>
  <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
 </listener>
 <listener>
     <listener-class>com.mytutorial.MetricsContextLoaderListener</listener-class>
    </listener>

 
 <servlet>
  <servlet-name>resteasy-simple</servlet-name>
  <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
 </servlet>
 <servlet>
  <servlet-name>admin-servlet</servlet-name>
  <servlet-class>com.yammer.metrics.reporting.AdminServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>


 <servlet-mapping>
  <servlet-name>resteasy-simple</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>admin-servlet</servlet-name>
  <url-pattern>/admin/*</url-pattern>
 </servlet-mapping>
</web-app>


Step 5: The ServletContextListener com.mytutorial.MetricsContextLoaderListener defined in the above web.xml is a custom listener used to make MetricsRegistry available from the spring context to the Yammer admin servlet defined in the above web.xml.

package com.mytutorial;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.reporting.MetricsServlet;

/**
 * Application Lifecycle Listener for Metrics.
 * 
 *I need to fetch the MetricsRegistry from the Spring Context and put it into the ServletContext,
 * for the yammer admin-servlet to be able to find it.....
 *
 */
public class MetricsContextLoaderListener implements ServletContextListener {

    public MetricsContextLoaderListener() {

    }

 public void contextInitialized(ServletContextEvent event) {
        ServletContext context = event.getServletContext();
        MetricsRegistry metricsRegistry = getMetricsRegistry(context);
        setMetricsRegistry(metricsRegistry, context);
    }

 
    public void contextDestroyed(ServletContextEvent event) {

    }

   
    protected MetricsRegistry getMetricsRegistry(ServletContext context) {
        WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(context); 
        return springContext.getBean(MetricsRegistry.class);
    }
    
   
    protected void setMetricsRegistry(MetricsRegistry registry, ServletContext context) {
        context.setAttribute(MetricsServlet.REGISTRY_ATTRIBUTE, registry);
    }
}


Step 6: Build and deploy the war file.

Hit the url: http://localhost:8080/tutorial/rest/myapp/name/arul to invoke the RESTful web service.
Hit the url: http://localhost:8080/tutorial/admin/metrics?pretty=true to get the metrics as json String.
Hit the url: http://localhost:8080/tutorial/admin to get the Yammer menu.

Step 7: The json output for metrics (e.g. http://localhost:8080/tutorial/admin/metrics?pretty=true) will look like

{
{
  "jvm" : {
    "vm" : {
      "name" : "Java HotSpot(TM) 64-Bit Server VM",
      "version" : "1.6.0_45-b06"
    },
    "memory" : {
      "totalInit" : 2.695299072E9,
      "totalUsed" : 2.8351824E8,
      "totalMax" : 5.953159168E9,
      "totalCommitted" : 2.606497792E9,
      "heapInit" : 2.155872256E9,
      "heapUsed" : 2.29205536E8,
      "heapMax" : 3.780509696E9,
      "heapCommitted" : 2.066087936E9,
      "heap_usage" : 0.06062821006450872,
      "non_heap_usage" : 0.02499863539883529,
      "memory_pool_usages" : {
        "Code Cache" : 0.06720225016276042,
        "PS Eden Space" : 0.16319328855716045,
        "PS Old Gen" : 0.009577277144031412,
        "PS Perm Gen" : 0.023997800623475327,
        "PS Survivor Space" : 0.0
      }
    },
    "daemon_thread_count" : 28,
    "thread_count" : 49,
    "current_time" : 1395644957593,
    "uptime" : 24,
    "fd_usage" : "NaN",
    "thread-states" : {
      "new" : 0.0,
      "timed_waiting" : 0.4897959183673469,
      "terminated" : 0.0,
      "waiting" : 0.16326530612244897,
      "blocked" : 0.0,
      "runnable" : 0.3469387755102041
    },
    "garbage-collectors" : {
      "PS MarkSweep" : {
        "runs" : 2,
        "time" : 134
      },
      "PS Scavenge" : {
        "runs" : 2,
        "time" : 33
      }
    }
  },
  "com.mytutorial.SimpleRestWebImpl" : {
    "requestCount" : {
      "type" : "gauge",
      "value" : 1
    },
    "sayHello" : {
      "type" : "timer",
      "duration" : {
        "unit" : "milliseconds",
        "min" : 2999.710987,
        "max" : 2999.710987,
        "mean" : 2999.710987,
        "std_dev" : 0.0,
        "median" : 2999.710987,
        "p75" : 2999.710987,
        "p95" : 2999.710987,
        "p98" : 2999.710987,
        "p99" : 2999.710987,
        "p999" : 2999.710987
      },
      "rate" : {
        "unit" : "seconds",
        "count" : 1,
        "mean" : 0.06855060368303201,
        "m1" : 0.0,
        "m5" : 0.0,
        "m15" : 0.0
      }
    }
  }
}


Take note of "requestCount", which counts the number of requests.

Labels: , ,

Oct 10, 2014

When to use which Java collection or data structure? and why?

List, Set, Map, and Queue(access the ends FIFO or LIFO) are the basic Java data structure interfaces for which there are different implementations to cater for different usage patterns. Java data structure interview questions are very popular, and pays to know the differences.

#1 Array (Employee[]) Vs List (List<Employee>): 
  • Array is a fixed length data structure whilst a List is a variable length Collection class.
  • java.util.Arrays has the helper classes for the arrays and java.util.Collections has the helper classes for the Collection classes.
  • An array can use primitive data types, but the Collection classes can only use objects. 

Q. Which one to favor/use?
A. Favor  List.

  • Arrays are inflexible and do not have the expressive power of generic types.  
  • List gives you the data abstraction as you can swap ArrayList, LinkedList, CopyOnWriteArrayList, etc depending on the requirements.
  • List allows you to add and subtract elements even it is an O(n) operation in worst case scenario.

#2: ArrayList (ArrayList<employee>)Vs LinkedList (LinkedList<employee>)

  • Insertions and deletions are faster in LinkedList compared to an ArrayList as LinkedList uses links (i.e. before and next reference) as opposed to an ArrayList, which uses an array under the covers, and may need to resize the array if the array gets full.  Adding to an ArrayList has a worst case scenario of O(n) whilst LinkedList has O(1). 
  • LinkedList has more memory footprint than ArrayList. An ArrayList only holds actual object whereas LinkedList holds both data and reference of next and previous node.  
  • Random access has the worst case scenario of O(n) in LinkedList as to access 6th element in a LinkedList with 8 elements, you need to traverse through 1 to 5th element before you can get to the 6th element, whereas in an ArrayList, you can get the 6th element with O(1) with list.get(5)


Q. Which one to favor/use?
A. Almost always favor an ArrayList. ArrayLists are good for write once and randomly read many times, and for adding elements at the end, but bad at add/remove from the front or middle. LinkedLists are useful in rare usage patterns where insertion and deletion in the front and middle is significantly more than the retrievals, and the LinkedList can live with the O(n) cost of random access.

If you are after adding/removing from both ends, ArrayDeque is better than a LinkedList. The Deque interface is pronounced as "deck", and represents a double-ended queue. ArrayDeque is backed by an array. ArrayDeque can be used as both a Queue (i.e. FIFO) and a Stack (i.e. LIFO). ArrayDeque performs better than LinkedList, as  ArrayDeque is backed by an array and Array is more cache friendly. Also, ArrayDeque is more memory efficient than LinkedList since it does not have to keep an additional reference to previous or next node. ArrayDeque does not take null elements, but a LinkedList does. The best operation in a LinkedList implementation is removing the current element during the iteration. LinkedList implementations are also not ideal to iterate. So, LinkedList is very rarely used.


#3: List Vs Set

Set can't have duplicates whereas a List can have duplicates.

#4: HasMap Vs TreeMap

TreeMap is an implementation of a SortedMap, where the order of the keys can be sorted, and when iterating over the keys, you can expect that keys will be in order. HashMap on the other hand, makes no such guarantee on the order.

Q. Which one to favor/use?
A. In general, favor HashMap as it is more efficient in general, and use a Comparator to sort when required. Use a TreeMap only when it is required to keep the keys in a sorted order to iterate over them.


#5: HashSet, ArrayList Vs. CopyOnWriteSet, CopyOnWriteArrayList

HashSet and ArryList are not thread-safe and you need to provide your own synchronization, whereas CopyOnWriteArraySet and CopyOnWriteArrayList are not only thread-safe, but more efficient as they allow concurrent multiple  reads and single write. This concurrent read and write behavior is accomplished by  making a brand new copy of the list every time it is altered.

Q. Which one to favor/use?
A. Favor CopyOnWriteArraySet or CopyOnWriteArrayList only when the number of reads is significantly more than the number of writes. It also gives you fail safe iteration when you want to add/remove elements during iteration.


#6: HashMap Vs ConcurrentHashMap

HashMap is not thread-safe and you need to provide your own synchronization with Collections.synchornizedMap(hashMap), which will return a collection which is almost equivalent to the legacy Hashtable, where every modification operation on Map is locked. As the name implies, ConcurrentHashMap provides thread-safety by dividing the whole map into different partitions based upon concurrency level and locking only particular portion instead of locking the whole map.  ConcurrentHashMap does not allow NULL key values, whereas HashMap there can only be one null key.

Q. Which one to favor/use?
A. Favor ConcurrentHashMap  for scalability.


#7: new SynchronousQueue( ) Vs. new ArrayBlockingQueue(1) or LinkedBlockingQueue(1)


SynchronousQueue is a queue that can only contain a single element internally. A thread inserting an element into a SynchronousQueue blocked until another thread takes that element from the queue. Similarly, if a thread tries to take an element, and no element is currently present, that thread will be blocked until a thread inserts an element into the SynchronousQueue.

SynchronousQueue provides extremely good throughput when compared to a unit sized ArrayBlockingQueue(1) or LinkedBlockingQueue(1). SynchronousQueue  is the default BlockingQueue used in the Executor framework for the Executors.newCachedThreadPool( ).


Q. Which one to favor/use?
A.
  • Use SynchronousQueue in scenarios where a task to be executed asynchronously while discarding any further requests until the task is finished. 
  • executor = new ThreadPoolExecutor(
        1, 1, 
        2000, TimeUnit.SECONDS, 
        new SynchronousQueue<runnable>(),
        new ThreadPoolExecutor.DiscardPolicy());
    
  • Use SynchronousQueue to improve application performance. 

#8: HashMap Vs LinkedHashMap

LinkedHashMap will iterate in the order in which the entries were put into the map. HashMap does not provide any grantees about the iteration order.



Q. What is a BlockingQueue?
A. BlockingQueue is a Queue that supports additional operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element. The main advantage is that a BlockingQueue is that it provides a correct, thread-safe implementation with  throttling.

  • The producers are throttled to add elements if the consumers get too far behind. 
  • If the Queue capacity is limited, the memory consumption will be limited as well. 


Q. Why Vector, Stack, and Hashtable are legacy data structures and not to be used?
A. All methods in these classes are synchronized (i.e. coarse grained lock), hence not efficient. Favor the concurrent data structures for concurrent read and single write.

Labels:

Oct 1, 2014

How to prepare or train for Java and JEE Job Interviews?

What are the core concepts?

If you just rely only on your experience alone, it can take a long time to get a good handle on the core concepts and the 16 key areas. The best way to fast track your career is to proactively learn and apply them.

Java developer training

What are these 16 technical key areas? 

Software development key areas


  1. Language Fundamentals (LF)
  2. Specification Fundamentals (SF)
  3. Platform Fundamentals (PF)
  4. Design Considerations  (DC)
  5. Design Patterns (DP)
  6. Concurrency Management (CM)
  7. Performance Considerations  (PC)
  8. Memory/Resource Considerations  (MC)
  9. Transaction Management  (TM)
  10. Security (SE)
  11. Scalability  (SC)
  12. Best Practices (BP)
  13. Coding (CO)
  14. Exception Handling (EH)
  15. Software Development Processes (SDP)
  16. Quality of Service  (QoS)



How would you go about preparing for the job interviews?

1. Firstly, reflect back on your past experiences and achievements by going through your resume to sell yourself more effectively. Think of situations where you
  • fixed performance issues, security holes, memory leaks and/or thread-safety issues.
  • took a project through full Software Development Life Cycle (SDLC).
  • worked well in an agile environment.
  • earned the reputation as a "go to person".
  • worked on "QuickWins" projects.
  • took initiatives and collaborated well with the business.
Note: The Java training dashboard has "16 key areas to do well in Java". The technical key areas are vital to sell yourself or set you apart from your competition. I am yet to work for an organization that did not face challenges relating to performance, scalability, security, concurrency, etc.

2. Secondly, understand your prospective employers' requirements and correlate the requirements to your experiences & achievements so that you can convince your prospective employer as to how you can add value.

3. Thirdly, research the organization you will be interviewing with. Employers like to hire those who show real interest in them.

4. You have no control over what questions get asked, and also not expected to know everything. Interviews are not memory contests to see who gets the most questions right. The quality and clarity of the answers you give to some of the key questions will not only make you standout from your competition, but also make your interviewers overlook other shortcomings like not having enough experience with a particular framework/technology or not knowing answers to some other less important questions.

5. Open ended questions don't have right or wrong answers, and give you the greatest opportunity to sell yourself with quality answers with good practical examples. Focus on the 16 key areas to answer open ended questions.

6. Most of the interviewers start with your resume, and then get into more technical questions. Brush up on the fundamental technical questions. If you are confused about what to prepare, I have put together some books and 350+ blog posts to brush up prior to job interviews.
  • The book entitled Java/J2EE job interview companion  covers a wide range of  core Java and enterprise Java technologies shown in the above diagram.
  • The book entitled Core Java Career Essentials focuses in more detail with regards to some of the core Java must know topics like data types, language fundamentals, OO concepts and data structures.
  • This blog compliments the books with more coverage on multi-threading, sought after frameworks like JSF, Spring, Hibernate, etc, and handy tools like Selenium, JMeter, and lots more.   

7. Answer the following question -- Q. Why are you better than the other developers? [Hint: Sell yourself as a well rounded professional and not just as a techie, e.g. ability to look at the big picture, ability see things from both technical and business perspective,  SAR based answers to technical key areas, etc.]

8. Steps 4 and 5 can give you the much needed confidence in the interviews. It is natural to be nervous, but think of each interview as a free training session where you get to assess your strengths and weaknesses.

9. Interviews are not just technical contests, and it is an opportunity for both parties to assess each other. With some preparation and know-how, you can stand-out from the pack. Right "Attitude" is equally important. No body knows everything. If anyone things he/she does, others would not want to work with a such person. So, if you don't know, say you don't know. Your soft skills like communication skills, interpersonal skills, ability to work as a team and personal traits like positive attitude, honesty, passion, etc  will be under scrutiny in your job interviews as you will need to work as a team to get things done at your next job. It can be easier to work with your computer than working with people with different personalities. So, don't feel too discouraged by not performing too well in the technical questions, and maintain your composure throughout the interview. Your soft skills and right attitude could win you the next job.


10. Books and blog posts can only guide and help you learn from others' experience. But for real success, you need to pro-actively apply what you learn by experiencing it yourself. There is not substitute for hands-on experience


11. Hope this site helps you open more doors as it has helped many others. I will endeavor to add more resources to this blog. Stay tuned by subscribing to this blog.

Labels:

Yammer metrics to monitor RESTful web services with a Servlet filter

This extends Yammer metrics to monitor RESTful web services and report them via its admin web to demonstrate how a Servlet  filter can be added to gather metrics for
  • response codes for 200, 201, 404, etc to get a count.
  • active requests count
  • requests timing.

Step 1:  Decorate the "HttpServletResponseWrapper" class. HttpServletResponseWrapper is one particular implementation of HttpServletResponse which gives you a convenient way to wrap an existing response with some logic of your own without having to write a whole new implementation of the interface.



package com.mytutorial;

import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class StatusServletResponse extends HttpServletResponseWrapper {
 private int httpStatus;

 public StatusServletResponse(HttpServletResponse response) {
  super(response);
 }

 @Override
 public void sendError(int sc) throws IOException {
  this.httpStatus = sc;
  super.sendError(sc);
 }

 @Override
 public void sendError(int sc, String msg) throws IOException {
  this.httpStatus = sc;
  super.sendError(sc, msg);
 }

 @Override
 public void setStatus(int sc) {
  this.httpStatus = sc;
  super.setStatus(sc);
 }

 public int getStatus() {
  return this.httpStatus;
 }
}



Step 2: Add a Servlet filter to capture the above metrics. This where Yammer metrics is used.

package com.mytutorial;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.TimerContext;

public class MetricsFilter implements Filter {

 private static final String NAME_PREFIX = "responseCodes.";
 private static final int OK = 200;
 private static final int CREATED = 201;
 private static final int NO_CONTENT = 204;
 private static final int BAD_REQUEST = 400;
 private static final int NOT_FOUND = 404;
 private static final int SERVER_ERROR = 500;

 private ConcurrentMap<Integer, Meter> metersByStatusCode;
 private Counter activeRequests;
 private Timer requestTimer;
 
 public MetricsFilter() {

    }
    

 public void init(FilterConfig filterConfig) throws ServletException {
  MetricsRegistry metricsRegistry = getMetricsRegistry(filterConfig);
  Map<Integer, String> meterNamesByStatusCode = getMeterNamesByStatesCode();

  this.metersByStatusCode = new ConcurrentHashMap<Integer, Meter>(meterNamesByStatusCode.size());
  for (Entry<Integer, String> entry : meterNamesByStatusCode.entrySet()) {
   metersByStatusCode.put(entry.getKey(),
     metricsRegistry.newMeter(this.getClass(), entry.getValue(), "responses", TimeUnit.SECONDS));
  }

  this.activeRequests = metricsRegistry.newCounter(this.getClass(), "activeRequests");
  this.requestTimer = metricsRegistry.newTimer(this.getClass(), "requests", TimeUnit.MILLISECONDS,
    TimeUnit.SECONDS);
 }

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
   ServletException {
  final StatusServletResponse wrappedResponse = new StatusServletResponse(
    (HttpServletResponse) response);
  this.activeRequests.inc();
  final TimerContext context = this.requestTimer.time();
  try {
   chain.doFilter(request, wrappedResponse);
  } finally {
   context.stop();
   this.activeRequests.dec();
   markMeterForStatusCode(wrappedResponse.getStatus()); 
  }

 }
 
 
  private void markMeterForStatusCode(int status) {
         final Meter metric = metersByStatusCode.get(status);
         if (metric != null) {
             metric.mark();
         } 
     }


 public void destroy() {

 }

 protected MetricsRegistry getMetricsRegistry(FilterConfig config) {
  WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(config
    .getServletContext());
  return springContext.getBean(MetricsRegistry.class);
 }

 protected Map<Integer, String> getMeterNamesByStatesCode() {
  final Map<Integer, String> meterNamesByStatusCode = new HashMap<Integer, String>(6);
  meterNamesByStatusCode.put(OK, NAME_PREFIX + "ok");
  meterNamesByStatusCode.put(CREATED, NAME_PREFIX + "created");
  meterNamesByStatusCode.put(NO_CONTENT, NAME_PREFIX + "noContent");
  meterNamesByStatusCode.put(BAD_REQUEST, NAME_PREFIX + "badRequest");
  meterNamesByStatusCode.put(NOT_FOUND, NAME_PREFIX + "notFound");
  meterNamesByStatusCode.put(SERVER_ERROR, NAME_PREFIX + "serverError");
  return meterNamesByStatusCode;
 }
}

Step 3: Register this Servlet filter via web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>


 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:/com/mytutorial/applicationContext.xml</param-value>
 </context-param>

 <context-param>
  <param-name>resteasy.use.deployment.sensitive.factory</param-name>
  <param-value>false</param-value>
 </context-param>

 <context-param>
  <param-name>resteasy.servlet.mapping.prefix</param-name>
  <param-value>/rest</param-value>
 </context-param>


 <listener>
  <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
 </listener>
 <listener>
  <listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
 </listener>
  <listener>
   <listener-class>com.mytutorial.MetricsContextLoaderListener</listener-class>
  </listener>

 <filter>
  <description>
   Collects request metrics...</description>
  <display-name>MetricsFilter</display-name>
  <filter-name>MetricsFilter</filter-name>
  <filter-class>com.mytutorial.MetricsFilter</filter-class>
 </filter>

 <filter-mapping>
  <filter-name>MetricsFilter</filter-name>
  <servlet-name>resteasy-simple</servlet-name>
 </filter-mapping>

 <servlet>
  <servlet-name>resteasy-simple</servlet-name>
  <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
 </servlet>
 <servlet>
  <servlet-name>admin-servlet</servlet-name>
  <servlet-class>com.yammer.metrics.reporting.AdminServlet</servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>


 <servlet-mapping>
  <servlet-name>resteasy-simple</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
  <servlet-name>admin-servlet</servlet-name>
  <url-pattern>/admin/*</url-pattern>
 </servlet-mapping>
</web-app>


Step 4: Deploy the application to a web server.

Step 5: Invoke the RESTFul web service via :  http://localhost:8080/tutorial/rest/myapp/name/sam

Step 6: Invoke the Yammer metrics admin servlet via: http://localhost:8080/tutorial/admin and then click on "metrics".

Step 7: The metrics will be displayed as JSON data.Only a subset of JSON data is shown

"com.mytutorial.MetricsFilter" : {
    "activeRequests" : {
      "type" : "counter",
      "count" : 1
    },
    "requests" : {
      "type" : "timer",
      "duration" : {
        "unit" : "milliseconds",
        "min" : 3075.95668,
        "max" : 3075.95668,
        "mean" : 3075.95668,
        "std_dev" : 0.0,
        "median" : 3075.95668,
        "p75" : 3075.95668,
        "p95" : 3075.95668,
        "p98" : 3075.95668,
        "p99" : 3075.95668,
        "p999" : 3075.95668
      },
      "rate" : {
        "unit" : "seconds",
        "count" : 1,
        "mean" : 0.01570764346171342,
        "m1" : 0.015991117074135343,
        "m5" : 0.0033057092356765017,
        "m15" : 0.0011080303990206543
      }
    },
    "responseCodes.badRequest" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 0,
      "mean" : 0.0,
      "m1" : 0.0,
      "m5" : 0.0,
      "m15" : 0.0
    },
    "responseCodes.created" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 0,
      "mean" : 0.0,
      "m1" : 0.0,
      "m5" : 0.0,
      "m15" : 0.0
    },
    "responseCodes.noContent" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 0,
      "mean" : 0.0,
      "m1" : 0.0,
      "m5" : 0.0,
      "m15" : 0.0
    },
    "responseCodes.notFound" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 0,
      "mean" : 0.0,
      "m1" : 0.0,
      "m5" : 0.0,
      "m15" : 0.0
    },
    "responseCodes.ok" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 1,
      "mean" : 0.015706988535186733,
      "m1" : 0.015991117074135343,
      "m5" : 0.0033057092356765017,
      "m15" : 0.0011080303990206543
    },
    "responseCodes.serverError" : {
      "type" : "meter",
      "event_type" : "responses",
      "unit" : "seconds",
      "count" : 0,
      "mean" : 0.0,
      "m1" : 0.0,
      "m5" : 0.0,
      "m15" : 0.0
    }
  }
  
  

Labels: ,