Installing and configuring vsftpd

To perform ftp file transaction it is required to install a ftp server and configure it appropriately. The vsftpd server is used in this scenario.

First install the vsftpd server – sudo apt-get install vsftpd

Configure the vsftpd server by editing the /etc/vsftpd.conf file

listen=YES

anonymous_enable=NO

local_enable=YES

write_enable=YES

anon_mkdir_write_enable=NO

dirmessage_enable=YES

use_localtime=YES

xferlog_enable=YES

secure_chroot_dir=/var/run/vsftpd/empty

pam_service_name=vsftpd

rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem

rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key

pasv_enable=YES

pasv_min_port=11810

pasv_max_port=11819

pasv_address=<public external ip address of the machined where vsftpd is installed>

 

Open the port ranges 20-21 and 11810-11819 of the machine

Finally restart the vsftp server – sudo service vsftpd restart.

 

FTP user accounts

I created  two user accounts to send and receive files with login disabled (This is not required)

sudo adduser ftpuser1 –disabled-login

sudo adduser ftpuser2 –disabled-login

 

Create the following directory structure

/home/ftpuser1/files/in      /home/ftpuser1/files/out

/home/ftpuser2/files/in      /home/ftpuser2/files/out

 

Change the change file owner to respective users

chown -R ftpuser1 /home/ftpuser1/files

chown -R ftpuser2 /home/ftpuser2/files

Configure Apache2 and NginX as Content based router

Content based routing is to send received request to particular destination depending on the request content. The configuration bellow will be using a custom HTTP header as the content to decide the destination

Apache2

Create a file named a2cbr in /etc/apache2/sites-available, and copy the following content to a file named a2cbr

<VirtualHost *:10000>
    ServerName localhost
    ProxyRequests Off
    RewriteEngine On

    <Directory>
        AllowOverride All
        <Limit GET HEAD POST PUT DELETE OPTIONS>
        Order Allow,Deny
        Allow from all
        </Limit>
    </Directory>

    RewriteCond %{HTTP:routing} xadmin
    RewriteRule ^/a2cbr(.*) http://localhost:9000/service/EchoService [P]
</VirtualHost>

NginX

Create a file named nginxcbr in /etc/nginx/sites-available, and copy the following content to a file named nginxcbr

server {
   listen *:15000;
   server_name localhost;

   location  / {
       if ($http_routing ~ 'xadmin') {
          rewrite ^/nginxcbr(.*) /service/EchoService;
           proxy_pass http://localhost:9000;
       }
    }
}
<pre>

JMX tips

I’m hoping to continuously update this post as I come across important facts while developing JMX applications. So here we go…..

  1.  Do not pass complex structures to the JMX connection. JMX finds it difficult to render complex objects.
    e.g: Last week I was trying to pass a hash map which stored several other hash maps as its values, using MXBeans (The purpose was to add custom statistic support to UlraESB). 

    HashMap<String, HashMap<String, Integer>> statisticMap  = new HashMap<String, HashMap<String, Integer>>();
    

    The  statisticMap  was directly passed to the JMX connection. However the beans were created and visible through  the jconsole, but data wasn’t rendered. Then I tried out the following

    List<HashMap<String, Integer>> statisticList  = new ArrayList<HashMap<String, Integer>>();
    

    The list was directly passed to the connection and it worked like magic. Data were properly rendered. The only difference between these is that first design is “Maps in a Map” and the second is “Maps in a List”. I cannot explain why this happens… Please share your thoughts on this

  2. The objects which are passes to the JMX connection should be Serializable. If a static object is passed then the JMX connection would not be able to expose the data of that static object, since  static objects are not serializable. (Serialization can be only applied to objects and static variables does not belong to individual instances, they are class variables)

How TO KILL an ANT

Well this is nothing related my tech attempts, but I found this story really funny and thought of re-posting it on my blog . Also reminded me of my school days, where we  invented new formulas and theories just to get 15 marks 😀  The moral of this story is nothing but the absolute truth.

So here we go “How to kILL an ant” 😉
Q: How to Kill an Ant?
Asked in exam for 15 marks..
Student’s Answer:
Mix Chilli Powder with Sugar & keep it outside the Ant’s Hole.
After eating, Ant will search for some water near a water tank.
Push ant in to it. Now ant will go to dry itself near fire.
When it reaches fire, put a bomb into the fire.
Then admit wounded ant in ICU.
Remove oxygen mask from its mouth and kill the ant.

MORAL: Don’t play with students they can do anything for 15 marks.

Writing sample configurations for Apache ServiceMix

ServiceMix is an open source ESB by Apache Software Foundtion. This post will provide a brief description on creating new sample configurations and running them with ServiceMix

I have created sample configurations for five different scenarios.

The Direct Proxy

This is a basic scenario where a client issues a POST request and the ESB sends request to a specified service without any changes to the original message.
This cofiguration needs a http-consumer and a http-provider.

Steps to create the project

  • Create a folder and name it “direct-proxy”
  • Add a pom.xml file to the folder with following configuration
<?xml version="1.0" encoding="utf-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.apache.servicemix.http.proxy</groupId>
        <artifactId>parent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>pom</packaging>
        <name>SMX-HTTP :: DirectProxy</name>
        <url>http://servicemix.apache.org</url>
     </project>
  • cd direct-proxy
  • Create a comsumer service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-http-consumer-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=proxy-consumer-su
  • Create a provider service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-http-provider-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=proxy-provider-su
  • Create the Service Assembly
mvn archetype:create -DarchetypeArtifactId=servicemix-service-assembly -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=proxy-sa

After executing the above commands you will find three folders created in the ‘direct-proxy’ folder.

  • Navigate to the ‘proxy-sa’ folder and add the created service units as dependencies to the pom.xml file.

e.g ;

<dependencies>
    <dependency>
         <groupId>org.apache.servicemix.http.proxy</groupId>
         <artifactId>proxy-provider-su</artifactId>
         <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.apache.servicemix.http.proxy</groupId>
        <artifactId>proxy-consumer-su</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
  • Then navigate to ‘proxy-consumer-su’ and ‘proxy-provider-su’ and check if the generated pom.xml files have specified the componentName. If not specify the component name as follows.
<properties>
    <componentName>servicemix-http</componentName>
</properties>

Now you are ready to deploy the configuration.

  • Do the necessary changes to the xbean files in ‘proxy-consumer-su’ and ‘proxy-provider-su’.

{source}

  • Start servicemix –
    1. cd <servicemix-home>/bin
    2. ./servicemix
  • Build the project
    1. cd direct-proxy
    2. mvn clean install

The project should build without any errors

  • Navigate to direct-proxy/proxy-sa/target. Copy the zip file and paste it to <servicemix-home>/deploy folder.

The Content Based Router

The content based router will evaluate the POST request issued by the client. If the evaluation passes the request is sent to a specified service.
This configuration evaluates the request using xpath. Apache Camel is used to define routes and to perform the xpath evaluation.

Steps to create the project

  •  Create a folder and name it as “router”
  • Add a pom.xml file to the folder with following configuration
<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.servicemix.cbr.camel</groupId>
    <artifactId>parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>SMX-CAMEL :: CBRProxy</name>
    <url>http://servicemix.apache.org</url>
</project>
  •  cd router
  • create the ServiceMix Camel service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-camel-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=cbr-camel-su
  • Create a comsumer service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-http-consumer-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=cbr-consumer-su
  • Create a provider service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-http-provider-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=cbr-provider-su
  • Create the Service assembly
mvn archetype:create -DarchetypeArtifactId=servicemix-service-assembly -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=cbr-sa

There will be four folders created inside the ‘router’ folder

  •  Navigate to the ‘cbr-sa’ folder and add the created service units as dependencies to the pom.xml file.(refer the direct proxy)
  • Then navigate to ‘cbr-consumer-su’ and ‘cbr-provider-su’ and check if the generated pom.xml files have specified the componentName. If not, specify the  component name as follows.
<properties>
    <componentName>servicemix-http</componentName>
</properties>

Also check the pom generated in ‘cbr-camel-su’. If the componenName is not specified, if not specify it as follows

<properties>
    <componentName>servicemix-camel</componentName>
</properties>

To get this configuration working I had to replace the following of the pom.xml in cbr-camel-su

<dependency>
    <groupId>org.apache.servicemix</groupId>
    <artifactId>servicemix-camel</artifactId>
    <scope>provided</scope>
</dependency>

with

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-core</artifactId>
    <version>2.8.0</version>
</dependency>

Before this replacement I could not deploy the project due to “Error creating bean with name ‘camel’: Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.apache.servicemix.cbr.camel.MyRouteBuilder.from”

Now you are ready to deploy the configuration.

  • Do the necessary changes to the xbean files, camel contex and MyRouteBuilder.

{source}

  • Start servicemix – cd <servicemix-home>/bin

./servicemix

  • Build the project

cd router
mvn clean install
The project should build without any errors

  • Navigate to direct-proxy/proxy-sa/target. Copy the zip file and paste it to <servicemix-home>/deploy folder.

XSLT Transformation

This configuration transforms POST request using a XSLT stylesheet in the ESB, then sends the transformed request to an echo service. The echo service echo backs the received request. The echoed back transformed request is again transformed back to the original state, using another XSLT style sheet and sent back to the client. The Camel pipeline pattern is used for this.

  • Create a folder and name it as “xslt”
  •  add a pom.xml file to the folder with following configuration
 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.adroitlogic.com</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>SMX-CAMEL :: XSLT</name>
<url>http://servicemix.apache.org</url>
</project>

  • cd xslt
  • create the camel service unit
mvn archetype:create -DarchetypeArtifactId=servicemix-camel-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-camel-su
  • create transform-back-xslt-su
mvn archetype:create -DarchetypeArtifactId=servicemix-saxon-xslt-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-back-xslt-su
  • creat transform-xslt-su
mvn archetype:create -DarchetypeArtifactId=servicemix-saxon-xslt-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-xslt-su
  • create transform-xslt-comsumer-su
mvn archetype:create -DarchetypeArtifactId=servicemix-http-consumer-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-xslt-consumer-su
  • create transform-xslt-provider-su
mvn archetype:create -DarchetypeArtifactId=servicemix-http-provider-service-unit -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-xslt-provider-su
  • create the SA
mvn archetype:create -DarchetypeArtifactId=servicemix-service-assembly -DarchetypeGroupId=org.apache.servicemix.tooling -DartifactId=transform-sa

There will be four folders vreated inside the ‘router’ folder

  • Navigate to the ‘cbr-sa’ folder and add the created service units as dependencies to the pom.xml file.

        org.apache.servicemix.xslt.transform
        transform-back-xslt-su
        1.0-SNAPSHOT

        org.apache.servicemix.xslt.transform
        transform-camel-su
        1.0-SNAPSHOT

        org.apache.servicemix.xslt.transform
        transform-xslt-consumer-su
        1.0-SNAPSHOT

        org.apache.servicemix.xslt.transform
        transform-xslt-provider-su
        1.0-SNAPSHOT

        org.apache.servicemix.xslt.transform
        transform-xslt-su
        1.0-SNAPSHOT

  •  Then navigate to ‘transform-xslt-consumer-su’ and ‘transform-xslt-provider-su’ and check if the generated pom.xml files have specified the componentName. If not, specify the component name as follows.
servicemix-http

Check the pom generated in ‘transform-camel-su’. If the componenName is not specified, if not specify it as follows


     servicemix-camel

Check the pom generated in ‘transform-back-xslt-su’ and ‘transform-xslt-su’. If the componenName is not specified, if not specify it as follows


     servicemix-saxon

  • Do the necessary changes to the xbean files, camel contex and MyRouteBuilder.
  • Start servicemix – cd <servicemix-home>/bin

./servicemix

  • Build the project

cd router
mvn clean install
The project should build without any errors

  •  Navigate to direct-proxy/proxy-sa/target. Copy the zip file and paste it to <servicemix-home>/deploy folder.

So this is how you create, build and deploy samples in ServiceMix. However I think the overhead is too much. It is needed to build a project each time a new sample is deployed, unlike in most other ESBs where deployin a new sample is just a matter of creating a sample configuration file and you are up and running 🙂

Capture Packets from Wireshark and see the content of requests or responses

First start Wireshark. If you are working on Ubuntu it is important to run Wireshark as the root. (I think same applies for other linux platforms as well)

sudo wireshark

Then capture options should be specified. Press the Capture Options button on the welcome page or the second button from the left in the tool bar.

Then specify the interface and the port number. I’m capturing packets within localhost, so the interface would be “lo” and the port which I’m receiving the request is 8192

Once you specify these options you are ready to capture packets. Just click on the start button.

When the request is sent, wireshark will display a list of captured packet.

Right click on one of the packets and select “Follow TCP Stream”.  The pop up window will show the request received and response sent

A Simple File Uploader Servlet

The HTML form

<form action="FileUpload?artifact=endpoints&type=items" enctype="multipart/form-data" method="post">
    <input type="file"/>
    <button type="submit">Upload</button>
</form>

 

The …./WEB-INF/web.xml

<web-app>
        .
        .
        .
  <servlet>
     <servlet-name>FileUpload</servlet-name>
     <servlet-class>org.adroitlogic.ultraesb.zabbix.FileUpload</servlet-class>
  </servlet>

  <servlet-mapping>
     <servlet-name>FileUpload</servlet-name>
     <url-pattern>FileUpload</url-pattern>
  </servlet-mapping>
        .
        .
        .
</web-app>

The form action value should be similar to the url-pattern of servlet-mapping in web.xml, i.e FileUpload. The rest of the string in form action specifies the url parameters. The question mark indicates the start of url parameters. Two url parameters are defined in this form action, artifact and type. The value of artifact is endpoints and the value of type is items.

The values of these url parameters can be retrieved in the servlet by request.getParameter(“urlParameter”) method. (servlet lines 24,25)

 

The Servlet

The third party jars used : Apache Commons File Upload and Apache Commons IO

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.List;

public class FileUpload extends HttpServlet {

    private static final Logger logger = 
        LoggerFactory.getLogger(FileUpload.class);

    protected void doPost(HttpServletRequest request, HttpServletResponse 
        response) {

        StringBuilder basePath = new StringBuilder();
        basePath.append("conf").append(File.separator).append("zabbix").
            append(File.separator).append(request.getParameter("artifact")).
            append(File.separator).append(request.getParameter("type")).
            append(File.separator);

        FileItemFactory fileItemFactory = new DiskFileItemFactory();
        ServletFileUpload uploadHandler = new 
            ServletFileUpload(fileItemFactory);
        
        String destination = "the destination url that you want to send the 
            redirect to";
        response.setContentType("text/plain");

        try {
            List<FileItem> items = (List<FileItem>) 
                uploadHandler.parseRequest(request);
            for (FileItem item : items) {

                if (item.isFormField()) {

                    String fileName = item.getString();

                    int separatorIndex = fileName.lastIndexOf(File.separator);
                    if (separatorIndex > 0) {
                        fileName = fileName.substring(separatorIndex + 1);
                    }
                    basePath.append(fileName);
                    File file = new File( basePath.toString());
                    item.write(file);
                }                                       
           }
            response.sendRedirect(response.encodeURL(destination));
        } catch (Exception e) {
             logger.warn("Fault in uploading file", e);
        }
    }
}

Zabbix Monitoring via API

A few weeks ago I was asked to implement a feature for the Uconsole, to monitor “UltraESB” artifacts exposed to JMX connection. In today’s post I’ll be explaining the approach that I followed to achieve this task.

You can get an idea how the API works here.

The “item” is the basic element that should be configured to start Zabbix Monitoring. A graph is created with relevance to an item. Each item is uniquely identified by the name given and most importantly by its key. It describes the object to be monitored by the item.

This post will mainly focus on how to work with the zabbix api and how to interact with it. To get a further understanding, refer this link. It also describes how to specify the Zabbix item key for a JMX artifact.

Creating an item via the GUI is pretty easy, but the GUI is not very helpful always. Specially when several number of items and graphs need to be created at one shot. My requirement was the latter one.

Other third party JARs used

Apache Http Client
Jackson Java JSON-processor

The Design

If you went through the zabbix api it is made clear that first a user should get an authentication token to  register  items, graphs and triggers. So first we’ll get authorized.

public String authenticate(String username, String password, String url) {
    this.password = password;
    this.userName = username;
    zabbixApiUrl = url;

    StringBuilder uiConnectMessage = new StringBuilder();
    StringBuilder sb = new StringBuilder();
    sb.append("{\"jsonrpc\":\"2.0\"").
    append(",\"params\":{").
    append("\"user\":\"").append(username).
    append("\",\"password\":\"").append(password).
    append("\"},").
    append("\"method\":\"user.authenticate\",").
    append("\"id\":\"2\"}");

    try {
        HttpResponse response = postAndGet(sb.toString());
        HttpEntity entity = response.getEntity();

        HashMap untyped = mapper.readValue(EntityUtils.toString(entity), HashMap.class);
        auth = untyped.get("result");

        if (auth == null) {
            throw new IllegalArgumentException("Authorization failed to : " + url + ", using username : "               + username);
        }
        uiConnectMessage.append("Successfully connected to the server\n");

    } catch (IOException e) {
        uiConnectMessage.append("Could not connect to the Zabbix Server at : ").
        append(url).append(" Exception : ").append(e.getMessage()).append("\n");
    }
    return uiConnectMessage.toString();
}
    private HttpResponse postAndGet(String request) throws IOException {
        HttpClient client = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(zabbixApiUrl);
        httpPost.setEntity(new StringEntity(request));
        httpPost.addHeader("Content-Type", "application/json-rpc");
        return client.execute(httpPost);
    }

The above method will authenticate you to the zabbix server when the zabbix api url, user name and the password is given.
The received authentication token is stored in the “auth” varible which is declared globally for later uses.  Bellow is a screenshot of the web management console of the UltraESB which invokes the above methods


If you are successfully authenticated to the server, a list of hosts and a list of applications defined for each hosts are  presented. It is important to choose a host and a relevant application to register items. Defining an application is optional, but if you don’t latest data for an item will not be shown on the Zabbix GUI.

These two methods are used to retrieve hosts and applications defined to each hosts. When a host is selected from the drop down menu, the application drop down will show only the applications defined for the selected host.

    private Map<String, String> hostMap = new HashMap<String, String>();
    private Map<String, Map<String, String>> appMap = new HashMap<String, Map<String, String>>();

    private void getHosts() throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("{\"jsonrpc\":\"2.0\",");
        sb.append("\"method\":\"host.get\",");
        sb.append("\"params\":{");
        sb.append("\"output\":\"extend\"},");
        sb.append(" \"auth\":\"").append(auth).append("\",");
        sb.append("  \"id\":2}");

        HttpResponse response = postAndGet(sb.toString());
        HttpEntity entity = response.getEntity();

        JsonNode rootNode = mapper.readValue(EntityUtils.toString(entity), JsonNode.class);
        JsonNode resultNode = rootNode.path("result");
        Iterator hostList = resultNode.getElements();
        while (hostList.hasNext()) {
            JsonNode element = hostList.next();
            hostMap.put(element.findValue("hostid").toString().replaceAll("(\\[|\\]|\")", ""), element.findValue("host")
                .toString().replaceAll("(\\[|\\]|\")", ""));
        }
    }

    private void getApps() throws IOException {
        StringBuilder sb = new StringBuilder();
        sb.append("{\"jsonrpc\":\"2.0\",");
        sb.append("\"method\":\"application.get\",");
        sb.append("\"params\":{");
        sb.append("\"output\":\"extend\"},");
        sb.append(" \"auth\":\"").append(auth).append("\",");
        sb.append("  \"id\":2}");

        HttpResponse response = postAndGet(sb.toString());
        HttpEntity entity = response.getEntity();

        JsonNode rootNode = mapper.readValue(EntityUtils.toString(entity), JsonNode.class);
        JsonNode resultNode = rootNode.path("result");
        Iterator appList = resultNode.getElements();
        while (appList.hasNext()) {
            JsonNode element = appList.next();
            JsonNode hosts = element.path("hosts");
            Iterator hostList = hosts.getElements();
            while (hostList.hasNext()) {
                JsonNode hostElement = hostList.next();
                String hostID = hostElement.findValue("hostid").toString().replaceAll("(\\[|\\]|\")", "");
                if (hostMap.containsKey(hostID)) {
                    if (appMap.containsKey(hostID)) {
                        appMap.get(hostElement.findValue("hostid").toString().replaceAll("(\\[|\\]|\")", "")).put(element.
                            findValue("name").toString().replaceAll("(\\[|\\]|\")", ""), element.findValue("applicationid").
                            toString().replaceAll("(\\[|\\]|\")", ""));

                    } else {
                        Map apps = new HashMap();
                        apps.put(element.findValue("name").toString().replaceAll("(\\[|\\]|\")", ""), element.
                            findValue("applicationid").toString().replaceAll("(\\[|\\]|\")", ""));
                        appMap.put(hostID, apps);
                    }
                }
            }
        }
    }

Finally when the user chooses the host and the application, the available artifacts are displayed. The user have the will of choosing which artifact is to be monitored.

Several items, graphs and triggers can be created for one type of artifact. For an example, some of the attributes exposed by the JMX connection of an Endpoint will be active address count, failed sending messages, ready address count etc. This program facilitates registration of many attributes for particular endpoint (not only endpoints other artifacts as well).

So how is this done?
I have defined a directory structure as shown in the image bellow

If a user needs to monitor a particular attribute, all what he have to do is to drop in json template file to the relevant folder, and the program will create items or graphs or triggers by reading the template files.
Following is a sample json file stored in the items directory of endpoints

NOTE : If a user checks an artifact and clicks the register button, the program will create items, graphs and triggers for all the json templates saved in the relevant artifact directory. (the Uconsole also provides with the facility of disabling a particular template).

{
"jsonrpc":"2.0",
"method":"item.create",
"params":{
      "description": "$filename$-$id$",
      "key_": "jmx[org.adroitlogic.ultraesb.detail:Type=Endpoints,Name=$id$][Details.activeAddressCount]",
      "hostid": "$hostid$",
      "value_type": 3 ,
      "data_type" : 0,
      "status": 0,
      "applications": ["$app$"]
},
"auth":"$auth$",
"id":2
}

The following method will list all the availble json files in specified artifact directory and loops through each file for manipulation.

     /**
     * Registers configuration elements at the zabbix server.
     *
     * @param category    - the type of the items
     * @param items       - name of the item to be registered
     * @param delExisting - the parameter to determine if the user need to delete the existing items
     * @return String - A String containing information about all unsuccessful operations
     */
public void registerConfigurationElements(String category, String[] items, boolean delExisting) {

    deleteExisting = delExisting;
    final String basePath = "conf" + File.separator + "zabbix" + File.separator;

    // Finding the path to Item templates
    File dirItemPath = new File(basePath + category + File.separator + "items");
    if (!dirItemPath.exists()) {
        throw new IllegalArgumentException("Item template directory for type : " + category + " does not exists");
    }

    File dirItems = new File(dirItemPath.getAbsolutePath());
    for (File file : dirItems.listFiles()) {
        int index = file.getName().lastIndexOf(".");
        String extension = file.getName().substring(index + 1);
        if ("json".equals(extension)) {
            processFile(file.getAbsolutePath(), items,
                file.getName().replaceAll(".json", ""), true);
        }
    }

    //find the path to Trigger templates
    File dirTriggerPath = new File(basePath + category + File.separator + "triggers");
    if (!dirTriggerPath.exists()) {
        throw new IllegalArgumentException("Trigger templates for type: " + category + " does not exists");
    }

    File dirTriggers = new File(dirTriggerPath.getAbsolutePath());
    for (File file : dirTriggers.listFiles()) {
        int index = file.getName().lastIndexOf(".");
        String extension = file.getName().substring(index + 1);
        if ("json".equals(extension)) {
            processFile(file.getAbsolutePath(), items,
                file.getName().replaceAll(".json", ""), false);
        }
     }

    // Finding the path to Graph templates
    File dirGraphPath = new File(basePath + category + File.separator + "graphs");
    if (!dirGraphPath.exists()) {
        throw new IllegalArgumentException("Graph templates directory for type : " + category + " does not exists");
    }

    File dirGraphs = new File(dirGraphPath.getAbsolutePath());
    for (File file : dirGraphs.listFiles()) {
        int index = file.getName().lastIndexOf(".");
        String extension = file.getName().substring(index + 1);
        if ("json".equals(extension)) {
            createGraph(file.getAbsolutePath(), items,
                file.getName().replaceAll(".json", ""));
        }
    }
}

If you carefully went through the json template above json template you would have probably noticed some variables wrapped in dollar ($) sign. These variable are replaced by the program.

Bellow is the createGraph method which is used for creating graphs at the zabbix server, but the method for creating items and triggers is not given as functionality is very much the same. Also I have omitted code lines which re-authenticate a if the zabbix session times out, solely for the purpose of maintaining the simplicity of the post.

    private void createGraph(String path, String items[], String fileName) {

        for (String item : items) {
            String request = readFile(path);
            request = request.replace("$filename$", fileName);
            request = request.replace("$id$", item);
            request = request.replace("$auth$", auth);

            try {
                HashMap requestNode = mapper.readValue(request, HashMap.class);
                ArrayList params = (ArrayList) requestNode.get("params");
                HashMap graphItems = (HashMap) params.get(0);
                ArrayList graphItemsList = (ArrayList) graphItems.get("gitems");
                HashMap itemId = (HashMap) graphItemsList.get(0);
                String itemIdList = (String) itemId.get("itemid");
                itemId.remove("itemid");
                itemId.put("itemid", map.get(itemIdList));
                String reqMssg = mapper.writeValueAsString(requestNode);
                postAndGet(reqMssg);

            } catch (EOFException e) {
                logger.warn("Empty json file at : {} - Exception : {}", path, e);
                uiRegisterElemMessage.append("Empty json file at ").append(path).append(" ").append(e).append("\n");

            } catch (IOException e) {
                logger.warn("Could not create graph for item : {} at path : {}", item, path);
                uiRegisterElemMessage.append("Could not create graph for item ").append(item).append(" at path ").
                    append(path).append("\n");
            }
        }
    }

As you can see working with Zabbix api is not difficult. The tedious part is sending requests to the server and processing received responses.

Running the Zabbix Server

The Zabbix server should be invoked by executing this command “/etc/init.d/zabbix-server start” before you start monitoring . You can check whether the Zabbix server is running from the front end GUI.

How ever if the GUI says “NO” have a look at the zabbix server log file at /var/log/zabbix-server/zabbix_server.log. If the log message some what similar to “Access denied for user ‘zabbix’@’localhost’ (using password: YES)”, there is a conflict between the zabbix database password and the passwords saved in following files.

/etc/zabbix/dbconfig.php

/etc/zabbix/zabbix_server.conf

Make sure that all three passwords are the same. The password for the zabbix database can reset by typing “set password for ‘zabbix’@’localhost’ = PASSWORD(‘yourPassword’); ” on the mysql prompt.

Log4J Custom Memory Appender

Hi all,

This post will describe how to write a custom appenders for Log4J, and about the Memory Appender, which I implemented.

Log4j is a project of Apache Software Foundation, which provides Java based logging Utility. It is quite efficient do logging with log4j, than having common System.out.println s all around your code.

Ok, lets get back to our custom appender.

Writing a custom appender for log4j is pretty easy. All what you have to do is to extend the “AppenderSkeleton” class in Log4j.


package org.adroitlogic.ultraesb.core.helper.memoryappender;

import org.adroitlogic.ultraesb.Util;
import org.adroitlogic.ultraesb.jmx.JMXConstants;
import org.adroitlogic.ultraesb.jmx.core.LogManagementMXBean;
import org.adroitlogic.ultraesb.jmx.view.LogEntry;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;

import java.util.List;

/**
* InMemoryAppender appends log messages to a LogList, and implements methods in the MXBean interface
*/

public class InMemoryAppender extends AppenderSkeleton implements LogManagementMXBean {

private volatile long id = 0;
private final LogList logList = new LogList();

public synchronized void close() {
    if (this.closed) {
    return;
}
    this.closed = true;
}

public boolean requiresLayout() {
    return true;
}

protected boolean checkEntryConditions() {
    if (this.closed) {
        LogLog.warn("Not allowed to write to a closed appender.");
        return false;
    }

    if (this.layout == null) {
        errorHandler.error("No layout set for the appender named [" + name + "].");
        return false;
    }
    return true;
}

public void setSize(int size) {
    logList.setSize(size);
}

public void append(LoggingEvent event) {
    if (!checkEntryConditions()) {
       return;
    }
    subAppend(event);
}

protected void subAppend(LoggingEvent event) {
    int index = event.getLoggerName().lastIndexOf('.');
    String loggerName;

    if (index > -1) {
        loggerName = event.getLoggerName().substring(index + 1);
    } else {
        loggerName = event.getLoggerName();
    }

    LogEntry log = new LogEntry();
    log.setId(id++);
    log.setHost(event.getProperty("host"));
    log.setIp(event.getProperty("ip"));
    log.setLoggerName(loggerName);
    log.setMessage((String) event.getMessage());
    log.setThreadName(event.getThreadName());
    log.setTimeStamp(event.getTimeStamp());
    log.setLogLevel(event.getLevel().toString());
    logList.insert(log);
}

}

The method void close(), boolean requiresLayout() and void append(LoggingEvent event) are extended from  AppendercSkeleton class. Out of these, most important method is void append(LoggingEvent event) method. Each time a log message occurs, it is passed to this method as a LoggingEvent. Once  the LoggingEvent  is captured, you are almost done with writing the custom appender.

Then the LogginEvent is passed to  “subAppend(LogginEvent event)”. In there,  the “LoggingEvent” can be processed according to one’s requirements.

My requirement was to retrieve the tail end of UltraESB log messages and to display them on the web management console.

The following class defines the data structures I used and its insertion and retrieval methods.


package org.adroitlogic.ultraesb.core.helper.memoryappender;

import org.adroitlogic.ultraesb.jmx.view.LogEntry;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

public class LogList {

/**
* Specifies the number of log messages appended. Is incremented each time, a log is added to the linked list
*/
private int usedSize = 0;
/**
* Size of the hash map
*/
private int capacity;
private Map map;
private LinkedList linkedList = new LinkedList();
private static final DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss");

public LogList() {
}

public void setSize(int hashMapSize) {
    this.capacity = hashMapSize;
    map = new HashMap(hashMapSize);
}

/**
* Inserts log messages to the linked list and the hash map.
* If the number of log messages exceed the hashMapSize, new logs will be added after removing, the oldest log from the linked list and the hash map.
* @param log the log entries
*/
public synchronized void insert(LogEntry log) {
    if (usedSize > capacity) {
        map.remove(linkedList.removeFirst().getId());
        map.put(log.getId(), log);
        linkedList.add(log);
    } else {
        linkedList.add(log);
        map.put(log.getId(), log);
        usedSize++;
    }
}

public List formatFields(List list) {
int i = 0;
LogEntry entry;
String formattedString;

while (i < list.size()) {
    entry = list.get(i);
    if (entry.getFormattedTime() == null) {
        synchronized (dfm) {
        formattedString = dfm.format(entry.getTimeStamp());
        entry.setFormattedTime(formattedString);
        }
    }

    formattedString = entry.getMessage().replaceAll("( ){2}?", "  ");
    entry.setMessage(formattedString);
    i++;
}
return list;
}

/**
* When called returns the entire list of logs
* @return linkedList the list containing the logs
*/
public List getAll() {
    if (linkedList != null) {
        return formatFields(linkedList);
    } else {
    return Collections.emptyList();
    }
}

/**
* When called returns a sub list of the logs starting from the specified log id
* @param start starting id
* @return list the list containing the sub list
*/
public List getListFrom(long start) {
    if (map.get(start) != null) {
        List list = new ArrayList();
        long i = start;
        while (map.get(i) != null) {
            list.add(map.get(i));
            i++;
        }
        return formatFields(list);
    } else {
        return Collections.emptyList();
    }
}
}

I suppose that you guys noticed the “void setSize(int size)” method in “InMemoryappender” class. This is where I set the size of the LogList HashMap.
This method is directly called from the Log4j properties file and anyone can change the size of the hash map by editing this file. This is how it looks like.

log4j.rootCategory=ERROR, MEMORY_APPENDER

log4j.appender.MEMORY_APPENDER=org.adroitlogic.ultraesb.core.helper.memoryappender.InMemoryAppender
log4j.appender.MEMORY_APPENDER.Size=500
log4j.appender.MEMORY_APPENDER.layout=org.apache.log4j.PatternLayout

Now I’ll try to provide a more detailed view of the LogViewer. The “void insert(LogEntry log)” method is called by the subAppend method. Each log message is inserted to a LinkedList and a HashMap. The HashMap is used to retrieve a log message by its log id. An ESB can generate thousands of log messages, but users may be interested on reading the latest logs. So only the latest 500 (this number can be configured by the user as mentioned above) messages are kept in the memory and presented to the user.

JMX capabilities are used for the interactions between the console and the back end. Communication is done via JSON messages and “Apache Wink” is used to map Json to Java and vice versa.

When a user navigates to the LogViwer tab, the “List getAll()” method is invoked, displaying all existing log messages in the linked list. The console app checks for any latest log messages with an interval of 5 seconds. “List getListFrom(long start)” method is invoked for this purpose.

So that’s all about writing a custom appender for log4j. Hope it was useful…:)

Control PC games from your Android Phone

Have you ever wanted to control PC games with your phone? Well we can show you the way 🙂 Our third year group project, MotionMote is all about controlling PC games via an Android phone. Of course this can be extended for other mobile OS platforms as well.

To start off with, let’s look at the concept behind the MotionMote. Most of the motion based system, such as WiiMote and PlayStationMove are not very popular in  computer gaming. Instead joysticks, joy pads and steering wheels are heavily used for PC simulation games, but they do not support motion detection. The MotionMote binds the funtions of game controllers and motion detectors, to create a virtual environment for PC simulation wirelessly.

The client (phone) and the server (PC) should be connected through a socket (ip address and port). Wi-Fi technology is used for the connection. Data is transmitted to the server via the phone, and then the server retransmit  data to PPJoy for further processing.

PPJoy is a virtual joystick driver. The server  requires this to be installed on the PC. It acts as a bridge between the server and the gaming application.

All the executables can be downloaded fromhere.

Technologies used

  • Android SDK 2.1(Mobile App)
  • .Net Framework 3.5(Desktop Application)

References – DroidPad

Other team members

  • Pragaladan Sivakumar (team leader – it was his idea 🙂 )
  • Rajeeva Uthayasankar
  • Irham Iqbal
  • Kreshan Rajendran

Design a site like this with WordPress.com
Get started