Sunday 24 August 2014

Dynamic Content Mapping with Jersey

In this article you will learn how to do return the response dynamically based on header or client request, something called Dynamic Content Mapping,much like Spring's ContentNegotiatingViewResolver.
So the response could be in any format xml, json,text ,html, protobuf.Also you will learn to create
restfull serviceprovider with protocol buffer.
There is no default mediatype for protocolbuffer,so you have to create Custom MessageBodyWriter
and MessageBodyReader to Serialize and Deserialize protobuf format.

There are  many ways how you can achieve this feature,but i will show you two most commonly used approach in real world scenario.

1) We can use the javax.ws.rs.HeaderParam in the Service provider to the Content-Type requested by the client and send the response accordingly to that format.

@GET
@Path("/dynamic")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response service(@QueryParam("msg") String msg,@HeaderParam("Content-Type")String contentType) {
String output = "Welcome : " + msg;
 return Response.status(200).entity(output).header(HttpHeaders.CONTENT_TYPE,      
         "application/json".equals(contentType) ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML)      
          .build();
}

2) Another approach is write custom Resource Config for your application and map your application accordingly.You can also write your own Media Type and Map it to your custom resource configuration.In-order to write custom mdeia type you need to override the MessageBodyWriter and MessageBodyReader for your Produce and Consume annotation tag. Here's a simple example to demonstrate it:

Order.java- Model class

@XmlRootElement(name = "orders")
public class Order implements Serializable {

    private static final long serialVersionUID = -3631611117467960241L;

    @JsonProperty("id")
    private String orderId;

    @JsonProperty("invoice")
    private double invoice;

    @JsonProperty("number")

    private int itemNumber;

//getter setters .....


CustomResourceConfig.java

public class CustomResourceConfig extends PackagesResourceConfig {

 public CustomResourceConfig(String... packages) {    //this constructor needs to be here, do not delete it or else the com.sun.jersey.config.property.packages param can't be passed in.
       super(packages);
   }

   public CustomResourceConfig(Map<String, Object> props) { //this constructor needs to be here, do not delete it or else the com.sun.jersey.config.property.packages param can't be passed in.
       super(props);
   }

 @Override
 public Map<String, MediaType> getMediaTypeMappings() {
   Map<String, MediaType> map = new HashMap<String, MediaType>();
   map.put("xml", MediaType.APPLICATION_XML_TYPE);
   map.put("json", MediaType.APPLICATION_JSON_TYPE);
   map.put("html", MediaType.TEXT_HTML_TYPE);
   map.put("js", MediaType.valueOf("text/javascript"));
   map.put("js", MediaType.valueOf("application/x-protobuf"));
   return map;
 }

}

Now Map your custom Resource Config in application web.xml

<!-- Custom Resource Config for Dynamic Response Mapping-->
<init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.service.content.mapping.CustomResourceConfig</param-value>

        </init-param> 



HtmlWriterProvider.java

@Produces(MediaType.TEXT_HTML)
@Provider
public class HtmlWriterProvider implements MessageBodyWriter<Order> {
    public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2,
            MediaType arg3) {
        return true;
    }
    public long getSize(Order t, Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType) {
        return -1;
    }
    public void writeTo(Order order, Class<?> type, Type genericType,
            Annotation[] annotations, MediaType mediaType,
            MultivaluedMap<String, Object> httpHeaders,
            OutputStream entityStream) throws IOException,
            WebApplicationException {
        String result = "<html>\n" + "<body>\n" + "<table border=\"1\">\n"
                + "<tr>\n" + "<td>Id Order</td>\n" + "<td>"
                + order.getOrderId() + "</td>\n" + "</tr><tr>\n"
                + "<td>Number Of Item</td>\n" + "<td>" + order.getItemNumber()
                + "</td>\n" + "</tr><tr>\n" + "<td>Invoice</td>\n" + "<td>"
                + order.getInvoice() + "</td>\n" + "</tr>\n" + "</table>\n"
                + "</body>\n" + "</html>";
        entityStream.write(result.getBytes());
    }
}

Similarly you can have your other custom writer and reader.Refer to Protocol Buffer example using Jersey.

OrderService.java

@Path("/order-service")
public class OrderService {

    @GET
    @Path("/get-order")
    @Produces({
        MediaType.APPLICATION_XML,
        MediaType.APPLICATION_JSON,
        MediaType.TEXT_HTML,
        "text/javascript" })
    public Order getOrder() {

        Order order = new Order();
        order.setOrderId("cb123");
        order.setInvoice(120);
        order.setItemNumber(100);
        return order;
    }

}

Now after deploying the service,navigate to the following in the browser:

1) HTML http://localhost:8080/RestFileServer/service/order-service/get-order



2) Rest Client with Content-Type(application/xml)  http://localhost:8080/RestFileServer/service/order-service/get-order




This dynamic response make the service very scalable and  effective for different type of consumer expecting different response format.

Saturday 23 August 2014

Jersey (JAX-RS) with Protocol Buffer for high performance Rest API

In this tutorial you will learn how to develop a Restful service with Jersey/JAX-RS using protocol buffer (commonly known as protobuf)for high performance.One of the great things about the JAX-RS specification is that it is very extensible and adding new providers for different mime-types is very easy. One of the interesting binary protocols out there is Google Protocol Buffers. They are designed for
 high-performance systems and drastically reduce the amount of over-the-wire data and also the amount of CPU spent serializing and deserializing that data.

The first thing you will need to do to get started is to download and build Protocol Buffers.
The next step is to create a Protocol Buffer using their definition language.We will create a customer.proto that will contain the schema for custmer information.

customer.proto

option java_package = "com.service.protobuf.example";
option java_outer_classname = "CustomerProtos";

enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
}
message PhoneNumber {
    required string phoneNumber=1;
    optional PhoneType phoneType=3;

     
message Address {
    required string addressLine1=2;
    optional string zipCode=4;
    optional string country=5;
    optional string state=6;
}

message CreditCard {
    required string cardNumber=1;
    optional string expMonth=2;
    optional string expYear = 3;
    required Address billingAddress = 5;
    required string type=7;
}   

message Customer{
     required int32 id=1;
     required string lastName=2;
     required string firstName=3;
     required string email=4;
     repeated PhoneNumber phoneNumber=6;
     repeated Address addresses=8;
     repeated CreditCard creditCards=9;

}

A fairly simple data description but it does touch on a lot of the features of Protocol Buffers including embedded messages, enums, repeating entries and their type system. You need to generate the Java code fro protobuf language using the protocol buffer compiler(protoc) that you have to download as mentioned above.

Sample Command:
//protoc -I=D:\Study\Codebase\test --java_out=D:\Study\Codebase\test\code D:\Study\Codebase\test\customer.proto

Now lets define a simple service that we want to get to work using the extension SPI of JAX-RS. This service will have two methods, a GET method for returning a new instance of a Customer and a POST method that just reflects what is passed to it back to the caller unmodified.

CustomerService.java

@Path("/customer")
public class CustomerService {

    @POST
    @Consumes("application/x-protobuf")
    @Produces("application/x-protobuf")
public CustomerProtos.Customer process(CustomerProtos.Customer  person) {
        return person;    
        }       
     
 @GET
     @Produces("application/x-protobuf")
     public CustomerProtos.Customer getCust(){
        return  CustomerProtos.Customer.newBuilder()
            .setEmail("a@bdc.com")
            .setFirstName("Test")
            .setLastName("Customer")
            .setId(1)
            .addAddresses(CustomerProtos.Address.newBuilder()
                    .setAddressLine1("123 Main St")
                    .setCountry("US")
                    .setState("CA")
                    .setZipCode("12345"))
            .addPhoneNumber(CustomerProtos.PhoneNumber.newBuilder()
                    .setPhoneNumber("415 510 5100")
                    .setPhoneType(CustomerProtos.PhoneType.HOME))
            .addCreditCards(CustomerProtos.CreditCard.newBuilder()
                    .setCardNumber("54111111111111")
                    .setExpMonth("10")
                    .setExpYear("2010")
                    .setType("VISA")
                    .setBillingAddress(CustomerProtos.Address.newBuilder()
                        .setAddressLine1("456Main St")
                        .setCountry("US")
                        .setState("AZ")
                        .setZipCode("12345"))
            ).build();
     }

}

For each of these methods we’ve restricted them to either consuming or producing content of type application/x-protobuf
When JAX-RS sees a request that matches that type or a caller that accepts that type these will be valid endpoints to satisfy those requests. Out of the box, Jersey includes readers and writers for a variety of types including form data, XML and JSON. 
They also provide a way to register new mime-type readers and writers with a very simple set of annotations on classes that implement either MessageBodyReader  or MessageBodyWriter. The class that implements reading is very straight forward, first it calls you back to see if you can read something, then it calls you to actually read it passing you the stream of data. 

This class either needs to be under a package that is registered to be scanned when the application starts or it could be explicitly registered by extending Application. You’ll note that in order for us to instantiate a new Protocol Buffer builder we need to use reflection on the type that JAX-RS is expecting. The writer is a bit more complicated because in addition to the isWritable and writeTo methods you have to be able to return the size that you are going to write.

ProtoBufMimeProvider.java

@Provider
@Consumes("application/x-protobuf")
@Produces("application/x-protobuf")
 
public class ProtoBufMimeProvider implements MessageBodyWriter<Message>, MessageBodyReader<Message> {
    //MessageBodyWriter Implementation
    @Override
    public long getSize(Message message, Class<?> arg1, Type arg2, Annotation[] arg3,
            MediaType arg4) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            message.writeTo(baos);
        } catch (IOException e) {
            return -1;
        }
        return baos.size();
    }
 
    @Override
    public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2,
            MediaType arg3) {
        return Message.class.isAssignableFrom(arg0);
    }
 
    @Override
    public void writeTo(Message message, Class<?> arg1, Type arg2, Annotation[] arg3,
            MediaType arg4, MultivaluedMap<String, Object> arg5,
            OutputStream ostream) throws IOException, WebApplicationException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        message.writeTo(baos);
        ostream.write(baos.toByteArray());
    }
     
     
    //MessageBodyReader Implementation
    @Override
    public boolean isReadable(Class<?> arg0, Type arg1, Annotation[] arg2,
            MediaType arg3) {
        return Message.class.isAssignableFrom(arg0);
    }
    @Override
    public Message readFrom(Class<Message> arg0, Type arg1, Annotation[] arg2,
            MediaType arg3, MultivaluedMap<String, String> arg4,
            InputStream istream) throws IOException, WebApplicationException {
        try {
            Method builderMethod = arg0.getMethod("newBuilder");
            GeneratedMessage.Builder<?> builder = (GeneratedMessage.Builder<?>) builderMethod.invoke(arg0);
            return builder.mergeFrom(istream).build();
        } catch (Exception e) {
            throw new WebApplicationException(e);
        }
    }
}


Now deploy the service and navigate to the following( with CONTENT-TYPE  as application/x-protobuf) using Rest client like(Poster,Postman) or use you own jersey client given in the code:

http://localhost:8080/RestFileServer/service/customer



Jersey Client:

 private void protoTest() throws IOException{
  
 URL target = new URL ("http://localhost:8080/RestFileServer/service/customer");
 HttpURLConnection conn = (HttpURLConnection) target.openConnection ();
 conn.setDoOutput (true);
 conn.setDoInput (true);
 conn.setRequestMethod ("GET");
 conn.setRequestProperty ("Content-Type", "application/x-protobuf");
conn.setRequestProperty ("Accept", "application/x-protobuf");
 conn.connect ();
 // Check response code
 int code = conn.getResponseCode ();
// boolean success = (code>=200) && (code <300);
 //InputStream in = success? conn.getInputStream (): conn.getErrorStream ();
 InputStream in=conn.getInputStream ();
 int size = conn.getContentLength ();
 byte [] response = new byte [size];
 
 int curr = 0, read = 0;
 while (curr==read){
 read = in.read (response, curr, size - curr);
 if (read <= 0) break;
 curr+=read;
 }
 
 Customer c=Customer.parseFrom (response);
 
 System.out.println ("id:" + c.getId());
 System.out.println ("name:" + c.getFirstName());
 System.out.println ("email:" + c.getEmail());

 for (PhoneNumber pn:c.getPhoneNumberList()) {
 System.out.println ("number:" + pn.getPhoneNumber() + "type:" + pn.getPhoneType().name());

 }
}

Download Code

Conditional Get and Http Caching using Jersey

In this tutorial you will learn the conditional http feature using Jersey.The HTTP Protocol defines a caching mechanism, in which the proxy web-servers can cache pages, files, images etc. Since caching is in place, There is a method which the servers are asked to return the document, either the “cached” or “live” document.

This request of asking the server for a document considering a specific parameter is called a Conditional GET Request.

ETags or entity tags are useful HTTP headers which can help in building a super fast application by minimizing the server load on system. ETag is set to the response to the client so a client can use various control request headers such as If-Match and If-None-Match for conditional requests. javax.ws.rs.core.Response.ResponseBuilder#tag() and javax.ws.rs.core.EntityTag are useful classes to work on ETags.

On server side, a unchanged ETag (match done between ETag attached with HTTP request and ETag calculated for requested resource) simply means, resource is unchanged from last requested time, so simply sending a HTTP 304 header [Not Modified] will be enough so that client can use the local available copy of resource without worrying much.

To demonstrate the example, I have two REST APIs in Employee Service class.

a) GET http://localhost:8080/RestFileServer/service/employee-service/get-employee/1

This API will get the employee resource which will have a ETag attached to it. This ETag will be used by server to verify if employee details have been updated since last request or not.

b) PUT http://localhost:8080/RestFileServer/service/employee-service/update-employee/1

This API will update the employee resource and it  will force the server to return the new copy of resource again.

So Lets get started !

Employee.java

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "employee")
public class Employee implements Serializable {
@JsonProperty("id")
private int id;

@JsonProperty("firstName")
private String firstName;

@JsonProperty("lastName")
private String lastName;

@JsonProperty("last-modified")

private Date lastModified;

......//getter setters .........

Now we will create a dummy Employee database EmployeeData.

public class EmployeeData 
{
    public static HashMap<Integer, Employee> employees = new HashMap<Integer, Employee>();
    static
    {
        Employee emp = new Employee();
        emp.setId(1);
        emp.setFirstName("Kuntal");
        emp.setLastName("Ganguly");
        emp.setLastModified(new Date());
        employees.put(1, emp);
    }
     
    public static Employee getEmployeeById(Integer id)
    {
        return employees.get(id);
    }
     
    public static void updateEmployee(Integer id)
    {
        Employee emp = employees.get(id);
        emp.setLastName("Gangopadhay");
        emp.setLastModified(new Date());
    }
     
    public static Date getLastModifiedById(Integer id)
    {
        return employees.get(id).getLastModified();
    }

}

Finally the Employee Service.

@Path("/employee-service")
public class EmployeeService 
{
    @GET
    @Produces(MediaType.APPLICATION_JSON )
    @Path("/get-employee/{id}")
    public Response getEmployeeById(@PathParam("id") int id, @Context Request req) 
    {
        //Create cache control header
         CacheControl cc = new CacheControl();
         //Set max age to one day
         cc.setMaxAge(86400);
             
        Response.ResponseBuilder rb = null;
         
        //Calculate the ETag on last modified date of user resource  
        EntityTag etag = new EntityTag(EmployeeData.getLastModifiedById(id).hashCode()+"");
         
        //Verify if it matched with etag available in http request
        rb = req.evaluatePreconditions(etag);
         
        //If ETag matches the rb will be non-null; 
        //Use the rb to return the response without any further processing
        if (rb != null) 
        {
            return rb.cacheControl(cc).tag(etag).build();
        }
         
        //If rb is null then either it is first time request; or resource is modified
        //Get the updated representation and return with Etag attached to it
        rb = Response.ok(EmployeeData.getEmployeeById(id)).cacheControl(cc).tag(etag);
        return rb.build();
    }
     
    @PUT
    @Produces(MediaType.APPLICATION_JSON )
    @Path("/update-employee/{id}")
    public Response updateEmployeeById(@PathParam("id") int id) 
    {
        //Update the Employee resource
        EmployeeData.updateEmployee(id);
        return Response.status(200).build();
    }

}

After building and deploying this service in your server.You can navigate to the following and Validate:

1) First time request : GET http://localhost:8080/RestFileServer/service/employee-service/get-employee/1






2) Subsequent request(s) : GET http://localhost:8080/RestFileServer/service/employee-service/get-employee/1




3) Modify request : PUT http://localhost:8080/RestFileServer/service/employee-service/update-employee/1



4) Updated Resource : GET http://localhost:8080/RestFileServer/service/employee-service/get-employee/1




Hope you understand the http Caching and Conditional Get. :)
Download code

File Upload and Download Service using Jersey


In this tutorial you will learn how to create File Upload and Download restful Web service
service using jersey.
JAX-RS provide a meda type MULTIPART_FORM_DATA for consuming blob through html form.
UploadService will consume blob(pdf,image,etc) through form and save it to disk.

UploadFileService.java

@Path("/uploader")
public class UploadFileService {

@POST
@Path("/post-blob")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public Response uploadFile(
@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {

String uploadedFileLocation = System.getProperty("user.dir")+"/"+ fileDetail.getFileName();

writeToDisk(uploadedInputStream, uploadedFileLocation);

String output = "File uploaded to : " + uploadedFileLocation;

return Response.status(200).entity(output).build();

}

private void writeToDisk(InputStream uploadedInputStream,
String uploadedFileLocation) {

try {
OutputStream out = new FileOutputStream(new File(
uploadedFileLocation));
int read = 0;
byte[] bytes = new byte[1024];

out = new FileOutputStream(new File(uploadedFileLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {

e.printStackTrace();
}

}


}

The Download Service will download the file from the disk and return Streaming Output fo the clients of Download Service.

DownloadService.java

@Path("/downloader")
public class DownloadFileService {
@GET
@Path("/receive-blob")
@Produces(MediaType.APPLICATION_OCTET_STREAM)

public StreamingOutput getPDF(@QueryParam("fileName") final String fileName) throws Exception {
  
return new StreamingOutput() {
@Override
public void write(OutputStream output) throws IOException,WebApplicationException {
try{
if (null!=fileName) {
InputStream fileStream=new FileInputStream(new File(System.getProperty("user.dir")+"/"+fileName));
byte[] buffer = new byte[2048];
int length = 0;
while ((length = fileStream.read(buffer)) != -1) {
output.write(buffer, 0, length);
}

}
}catch(Exception e){

}finally{
  output.close();
   
}

}
   
   };
}


}

Client for download service

public class JerseyClientFileDownload {

//Enter the file you want to download
private static String FILE_NAME="FolderSyncGetChnages.jmx";
public static void main(String[] args) throws IOException {
JerseyClientFileDownload jc=new JerseyClientFileDownload();
 jc.getPDFStream(FILE_NAME);

 
}
 
 
 public InputStream getPDFStream(String fileName) throws IOException  {
 Client client = Client.create();
 
WebResource webResource = client.resource("http://localhost:8080/RestFileServer/service/downloader/receive-blob");
   ClientResponse response = webResource.queryParam("fileName",fileName).type("application/octet_stream").get(ClientResponse.class);
   System.out.println(response.getEntityInputStream());
 
   OutputStream os = new FileOutputStream(new File("/home/kuntal/practice"+fileName));
   byte[] buffer = new byte[2048];
int length = 0;
InputStream inputStream =response.getEntityInputStream();
while ((length = inputStream.read(buffer)) !=-1) {
os.write(buffer, 0, length);
}
   return response.getEntityInputStream();
}
 
 

}


The Html form to upload the file to UploadService.

Upload.html

<html>
<body>
<h1>File Upload with Jersey</h1>

<form action="service/uploader/post-blob" method="post" enctype="multipart/form-data">

  <p>
Select a file : <input type="file" name="file" size="45" />
  </p>

 <b></b> <input type="submit" value="Upload" /></b>
</form>

</body>

</html>


Now you have RestFileServer ready to upload and download file for you :) Hope it helps you!

Download Code

Saturday 9 August 2014

Set up and Configure Ganglia Custom Metrics through Python module

In this tutorial you will learn how to monitor the health of your application through Ganglia using Python script.You can use shell script as well,but i will be using python script in this tutorial assuming ganglia is setup properly on CentOS or RHEL.

You can also refer to this tutorial for pushing custom metric into Ganglia through Gmetric.

Suppose you have an application running,and you want to use ganglia to monitor its health (like memory Used, Memory Free, No of Threads) along with application specific data (Message in Queue, Cache build time,etc). Let us assume your application publish this status through a url:

http://localhost:4080/dummyApp/healthStatus

Output:
jvm-maxm-mb:
jvm-min-mb
jvm-free-mb
no-of-threads:
msg-in-queue:
max-resp-time:
min-resp-time:
avg-resp-time:
cache-build-avg:

1. Now create a Python script to parse the value from above health status output(custom_metrics.py)

# Metric Initialization
def metric_init(params):
global descriptors, SERVER_STATUS_URL
 
if "metric_group" not in params:
params["metric_group"] = "custom_metrics"

if "refresh_rate" not in params:
params["refresh_rate"] = 2

if "url" not in params:
params["url"] = "http://localhost:4080/dummyApp/healthStatus"

SERVER_STATUS_URL = params["url"]

metric_obj_template = {
'name': 'jvm-maxm-mb',
        'call_back': get_value,
        'time_max': 90,
        'value_type': 'uint',
        'units': '',
        'slope': 'both',
        'format': '%u',
        'description': 'Application Server Metrics',
        'groups': "dummy"
}
metrics = get_metrics()[0]['data']
descriptors = list();
for m in metrics:
descriptors.append(create_desc({
"name"       : m,
"value_type" : "float",
"units"      : "",
"call_back"   : get_value,                
"format"     : "%f",
"description": m,
'groups': "dummy"
}))
return descriptors

# Create Metric object from template
def create_desc(prop):
d = metric_obj_template.copy()
for k,v in prop.iteritems():
d[k] = v
return d

# Get value for a metric
def get_value(name):

metrics = get_metrics()[0]

try:
result = metrics['data'][name]
except StandardError:
result = 0

return result

# Read metrics from REST API
def get_metrics():
global METRICS_GLOBAL_STORE_OBJ, SERVER_STATUS_URL, descriptors

try:
req = urllib2.Request(SERVER_STATUS_URL + "")

# Read stats from REST API
res = urllib2.urlopen(req, None, 2)

metrics = {};
for line in res:
split_line = line.rstrip().split(":")
metric_name = split_line[0]
metrics[metric_name] = split_line[1]
except urllib2.URLError:
traceback.print_exc()

METRICS_GLOBAL_STORE_OBJ = {
'time': time.time(),
'data': metrics
    }

return [METRICS_GLOBAL_STORE_OBJ]

# Clean up method
def metric_cleanup():
'''Clean up the metric module.'''
pass

# Main function for debug
if __name__ == '__main__':
try:
params = {
            'url'  : 'http://localhost:4080/dummyApp/healthStatus',
        }
metric_init(params)
get_metrics()
for d in descriptors:
v = d['call_back'](d['name'])
print 'value for %s is %s' % (d['name'],  v)
except KeyboardInterrupt:
os._exit(1)


2. Next create a custom metric configuration file (custom_metrics.conf)

modules {
module{
name = "custom_metrics"
language = "python"
param url {
value = "http://localhost:4080/dummyApp/healthStatus"
}

param metric_group {
value = "dummy"
}
}
}


collection_group {
  collect_every  = 2
  time_threshold = 60

  metric {
    name  = "jvm-maxm-mb"
    title = "JVM Maximum in MB"
    value_threshold = 0
  }

  metric {
    name  = "jvm-totm-mb"
    title = "JVM Total Memory in MB"
    value_threshold = 0

  }
. .  .  .  .  .  . .  .   .<put your other custom metrics with name as coming from the health service url>
}

3. Copy Custom metrics python module "custom_metrics.py" to Ganglia python modules directory1.      (/usr/lib64/ganglia/python_modules)

cp custom_metrics.py /usr/lib64/ganglia/python_modules

Make sure the script is executable before copying to ganglia python module directory
chmod +x custom_metrics.py



1.      4. Copy Custom metrics configuration file to Ganglia nodes configuration directory ( /etc/ganglia//conf.d )

  cp custom_metrics.conf /etc/ganglia/conf.d


5. Add following module and configuration entry into Ganglia nodes gmond.conf 

  vi /etc/ganglia/gmond.conf


 Look for modules section

 Add below module ( Make sure path and params values are exists)

module {
  name = "python_module"
  path = "/usr/lib64/ganglia/modpython.so"
  params = "/usr/lib64/ganglia/python_modules/"
 }

Now restart ganglia:

/etc/init.d/gmond stop

/etc/init.d/gmond start

For Setting up ganglia please refer to the previous tutorials