Friday 25 October 2013

Java XMLEncoder issue with java.sql.Timestamp in JDK 5 and 6

Objective

To highlight XMLEncoder class issue with java.sql.Timestamp class in JDK 5 and 6.

Environment

Java 5 and 6

Issue

From Java 5 we have XMLEncoder and XMLDecoder class to deal with XML encoding and decoding; but if you have observed the java.sql.Timestamp class is not taken care correctly by these classes, the following example is to show the same:

Program

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.sql.Timestamp;
import java.util.Date;

/**
 * @author mchopker
 *
 */
public class TestXMLEnocdingIssue {

    public static void main(String[] args) {
        try {
            XMLEncoder enc = new XMLEncoder(new BufferedOutputStream(
                    new FileOutputStream("Test.xml")));
            Person person = new Person("Mahesh", new Timestamp(
                    new Date().getTime()));
            enc.writeObject(person);
            enc.close();
            System.out.println("Encoding completed....");
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            XMLDecoder dec = new XMLDecoder(new BufferedInputStream(
                    new FileInputStream("Test.xml")));
            Object result = dec.readObject();
            dec.close();
            System.out.println(result);
            System.out.println("Decoding completed....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

---
import java.io.Serializable;
import java.sql.Timestamp;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private Timestamp time;
    public Timestamp getTime() {
        return time;
    }
    public void setTime(Timestamp time) {
        this.time = time;
    }
    public Person() {
    }
    public Person(String string, Timestamp time) {
        this.name = string;
        this.time = time;
    }
    public String getName() {
        return name;
    }
    public void setName(String string) {
        this.name = string;
    }
}

Console Output

java.lang.InstantiationException: java.sql.Timestamp
Continuing ...
java.lang.RuntimeException: failed to evaluate: <unbound>=Class.new();
Continuing ...

Encoding completed....
Person@6bdd46f7
Decoding completed....

XML Output

<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0_15" class="java.beans.XMLDecoder">
 <object class="Person">
  <void property="name">
   <string>Mahesh</string>
  </void>
 </object>
</java>

Observation

You can notice the exception shown as red color text, this is thrown by Java itself and the resulting XML not containing the timestamp attribute; the reason java.sql.Timestamp class is not taken care by Java 5/6 and is fixed now in Java 7. 
If you still happens to use Java 5/6 for some reason then you might have to write custom persistence delegate to deal with this issue as mentioned here:

Thank You!


Saturday 19 October 2013

Java SNMP Trap Sender and Receiver using SNMP4j Open Source library

Objective

To write a SNMP Trap Receiver (for v1, v2c and v3) and Sender in Java using SNMP4j Open Source library.

Environment

Eclipse IDE
Java 7
SNMP4j library (snmp4j-2.2.2.jar, can be downloaded from Net)

Development

Testing

Thank You!

JBoss EAP 6 JBoss Monitoring Java Client

Objective

To demonstrate a Java Client which can fetch JMS statistics of a JBoss EAP 6 running applications.

Environment

  • Eclipse IDE
  • Java 7
  • jboss-cli-client.jar (can be downloaded from Net)

Development

  • Using Eclipse IDE create a Java Project and a class with following main method:

    public static void main(String[] args) {
            String host;
            int port;
            String username;
            String password;
            String[] jmsHostNodes;
            String[] operations;
            String[] queues;
            CLI cli = null;

            try {

                File f = new File("jmsmonitoring.properties");
                if (f.exists()) {
                    Properties jmsMonProperties = new Properties();
                    FileInputStream in = new FileInputStream(f);
                    jmsMonProperties.load(in);

                    // fetch properties
                    host = jmsMonProperties.getProperty("DomainHost");
                    port = Integer.parseInt(jmsMonProperties
                            .getProperty("DomainPort"));
                    username = jmsMonProperties.getProperty("Username");
                    password = jmsMonProperties.getProperty("Password");
                    jmsHostNodes = jmsMonProperties.getProperty("JmsHostNodes")
                            .split(",");
                    operations = jmsMonProperties.getProperty("Operations").split(
                            ",");
                    queues = jmsMonProperties.getProperty("Queues").split(",");
                } else {
                    System.out.println("Input Proerty file missing");
                    return;
                }

                // Get CLI instance
                cli = CLI.newInstance();
                // connect to domain controller
                cli.connect(host, port, username, password.toCharArray());

                // iterate through JMS Nodes and Queues to fetch the needed data
                for (String jmsNode : jmsHostNodes) {
                    StringBuffer cmdString = new StringBuffer("/");
                    cmdString.append(jmsNode);
                    System.out.println("########### JMS Node: " + jmsNode
                            + " #########");
                    for (String queue : queues) {
                        System.out.println("Queue: " + queue);
                        StringBuffer cmdString2 = new StringBuffer(
                                cmdString.toString())
                                .append("/subsystem=messaging/hornetq-server=default/jms-queue=")
                                .append(queue).append("/:read-attribute(name=");
                        for (String operation : operations) {
                            // execute the command
                            System.out.println("command: " + cmdString2.toString()
                                    + operation + ")");
                            Result result = cli.cmd(cmdString2.toString()
                                    + operation + ")");
                            ModelNode response = result.getResponse();
                            String returnValue = response.get("result").asString();
                            System.out.println(operation + " : " + returnValue);
                        }
                    }
                }
            } catch (Exception e) {
                // print exception trace
                e.printStackTrace();
            } finally {
                // disconnect the CLI instance
                if (cli != null)
                    cli.disconnect();
            }
        }
  • The program takes the input parameters like JMS Node, Server and Queue names from a property file. You can create a property file with name "jmsmonitoring.properties" and following contents: 
DomainHost=10.133.2.68
#check the port jboss.management.native.port in domain.xml
DomainPort=10099
Username=admin
Password=admin01
JmsHostNodes=host=host122/server=salcore-jms-node-122,host=host127/server=salcore-jms-node-127
Operations=messages-added,message-count
Queues=alarms,Inventory,sender

  • The program is very generic in the sense that you can add queue names and operation names in the property file and program will be able to fetch and display them. For sample I added just two operations messages-added and message-count only.

Testing

  • Just run the program and you will see the given queues added messages and total count properties in the console.
  • The source code can also be found here: https://github.com/mchopker/myprojects/tree/master/JBossJMSMonitoring

    Thank You!

A simple WebService using JDK

Objective

To develop a Soap WS in standalone Java (no web/app server).

Environment

  • JDK 1.7
  • Eclipse IDE

Development

  • Create a Eclipse Java Project and add a class definition as given below:
package test;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;

@WebService(name = "Alarm", serviceName = "AlarmingService", portName = "AlarmPort")
public class AlarmingListenerImpl {

    @WebMethod
    public String sendAlarm(@WebParam(name = "alarm") String alarm) {
        if (alarm != null) {
            System.out.println("Message received:" + alarm);
        }
        return "success";
    }

    public static void main(String[] args) {
        // Start Alarm Listener
        Endpoint endPoint = Endpoint.create(new AlarmingListenerImpl());
        endPoint.publish("http://localhost:9001/alarmingService");
    }
}
  • Start the class (main method is written in the same class); this will be running and listening on port 9001 for any WS requests.

Testing

  • You need any WS client to test the WS you just created.
  • I used Eclipse Java EE Perspective's "Web Service Explorer" (Run -> Launch the Web Service Exporer):
  • You can also try JDK provided tool wsimport to generate WS Client classes and write a Java client.
Thanks You!

Sunday 13 October 2013

Open LDAP Setup in Windows and a Java Spring LDAP Client Development

Objective

To setup an OpenLDAP server in Windows OS and write a LDAP client with standalone Java and Spring libraries.

Environment

  • Windows OS (I have Windows 7)
  • Open LDAP Windows Installer (downloaded from http://userbooster.de/en/download/openldap-for-windows.aspx)
  • LDAP Browser (I have  LDAP browser 2.8.1 by Jarek Gawor)
  • Eclipse IDE

Development

 LDAP Setup

  • Run the downloaded OpenLDAP windows installer; I selected the LDIF backend engine during LDAP setup.
  • You will see a windows service for OpenLDAP server installed.
  • Check if the LDAP is installed properly and service is running by connecting through the LDAP browser you have, for reference I have used following settings to connect to LDAP.
 
  • Now you need some sample data on the LDAP server, so the following entries can be put into a LDIF extension file and added:
dn: ou=groups, dc=maxcrc,dc=com
ou: groups
description: Container for group entries
objectClass: top
objectClass: organizationalUnit

dn: cn=itpeople,ou=groups, dc=maxcrc,dc=com
description: IT security group
objectClass: groupOfNames
member: cn=Sheri Smith,ou=people,dc=maxcrc,dc=com
cn: itpeople

dn: cn=salespeople,ou=groups, dc=maxcrc,dc=com
description: Human Resources group
objectClass: groupOfNames
member: cn=John Smith,ou=people,dc=maxcrc,dc=com
cn: salespeople

dn: ou=People, dc=maxcrc,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit
description: Container for user entries

dn: cn=John Smith,ou=People, dc=maxcrc,dc=com
sn: Smith
userPassword:: alNtaXRI
ou: Sales
carLicense: HISCAR 124
mail: j.smith@maxcrc.com
mail: jsmith@maxcrc.com
mail: john.smith@maxcrc.com
objectClass: inetOrgPerson
uid: jsmith
homePhone: 555-111-2223
cn: John Smith
cn: John J Smith

dn: cn=Sheri Smith,ou=People, dc=maxcrc,dc=com
sn: smith
userPassword:: c1NtaXRI
ou: IT
carLicense: HERCAR 125
mail: s.smith@maxcrc.com
mail: ssmith@maxcrc.com
mail: sheri.smith@maxcrc.com
objectClass: inetOrgPerson
uid: ssmith
homePhone: 555-111-2225
cn: Sheri Smith

  •  To add the above data (copy paste it into some ldif file for example data.ldif) you need to use the LDAP browser import capability or run the following commnad (command available in installed openldap directory):
    • slapadd.exe -l data.ldif
  • You can verify the imported data through LDAP browser.

Java Client

package com.ldap.purjavaclient;

import java.util.Properties;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

public class LDAPPurJavaClient {

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            InitialDirContext context = getDirCtx("ldap://localhost",
                    "cn=Manager,dc=maxcrc,dc=com", "secret");
            try {
                if (args != null && args.length > 0) {
                    String input = args[0];
                    switch (input) {
                    case "1":
                        createPerson(context, "Mahesh Chopker", "Mahesh",
                                "mchopker", "Sales");
                        break;
                    case "2":
                        modifyPerson(context, "Mahesh Chopker", "Chopker",
                                "mchopker", "Sales");
                        break;
                    case "3":
                        removePerson(context, "Mahesh Chopker");
                        break;
                    default:
                        searchPerson(context);
                    }
                }
            } finally {
                context.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void searchPerson(InitialDirContext context) {
        Attributes attrs = new BasicAttributes();
        Attribute attr = new BasicAttribute("objectClass");
        attr.add("inetOrgPerson");
        attrs.put(attr);
        SearchControls ctls = new SearchControls();
        String[] ret = { "sn", "uid" };
        String filter = "(objectclass=*)";
        ctls.setReturningAttributes(ret);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        NamingEnumeration<SearchResult> enu = null;
        try {
            enu = context.search("cn=" + "Mahesh Chopker"
                    + ",ou=people,dc=maxcrc,dc=com", filter, ctls);
            while (enu != null && enu.hasMore()) {
                SearchResult sr = (SearchResult) enu.next();
                System.out.println(sr.getNameInNamespace());
                Attributes rattr = sr.getAttributes();
                System.out.println(rattr.get("sn"));
                System.out.println(rattr.get("uid"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (enu != null) {
                try {
                    enu.close();
                } catch (Exception e1) {
                    // igonore this
                }
            }
        }
    }

    private static void createPerson(InitialDirContext context, String name,
            String sn, String uid, String ou) throws NamingException {
        Attributes attrs = new BasicAttributes();
        String dn = "cn=" + name + ",ou=people,dc=maxcrc,dc=com";
        Attribute attr = new BasicAttribute("objectClass");
        attr.add("inetOrgPerson");
        attrs.put(attr);
        attrs.put("sn", sn);
        attrs.put("uid", uid);
        attrs.put("ou", ou);
        SearchResult sr = new SearchResult(dn, null, attrs);
        context.createSubcontext(sr.getName(), attrs);
    }

    private static void modifyPerson(InitialDirContext context, String name,
            String sn, String uid, String ou) throws NamingException {
        Attributes attrs = new BasicAttributes();
        String dn = "cn=" + name + ",ou=people,dc=maxcrc,dc=com";
        Attribute attr = new BasicAttribute("objectClass");
        attr.add("inetOrgPerson");
        attrs.put(attr);
        attrs.put("sn", sn);
        attrs.put("uid", uid);
        attrs.put("ou", ou);
        SearchResult sr = new SearchResult(dn, null, attrs);
        context.modifyAttributes(sr.getName(), DirContext.REPLACE_ATTRIBUTE,
                attrs);
    }

    private static void removePerson(InitialDirContext context, String name)
            throws NamingException {
        context.destroySubcontext(name);
    }

    private static InitialDirContext getDirCtx(String url, String userBindDn,
            String userBindPw) throws Exception {
        InitialDirContext ctx = null;
        Properties env = new Properties();
        env.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.setProperty(javax.naming.Context.PROVIDER_URL, url);
        env.setProperty(javax.naming.Context.SECURITY_PRINCIPAL, userBindDn);
        env.setProperty(javax.naming.Context.SECURITY_CREDENTIALS, userBindPw);
        try {
            ctx = new InitialDirContext(env);
        } catch (NamingException ex) {
            throw new Exception("LdapManagerBean::createConnection failed", ex);
        }
        return ctx;
    }
}

Java Spring Client without DI

package com.ldap.springclient;

import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;

import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;

public class LDAPSpringClient {

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {

            LdapContextSource ldapContextSource = new LdapContextSource();
            ldapContextSource.setUrl("ldap://localhost");
            ldapContextSource.setBase("dc=maxcrc,dc=com");
            ldapContextSource.setUserDn("cn=Manager,dc=maxcrc,dc=com");
            ldapContextSource.setPassword("secret");

            // this line needed since we are not using DI and manually
            // initizlizing context
            ldapContextSource.afterPropertiesSet();

            LdapTemplate ldapTemplate = new LdapTemplate();
            ldapTemplate.setContextSource(ldapContextSource);

            searchPerson(ldapTemplate);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void searchPerson(LdapTemplate ldapTemplate) {
        System.out.println(ldapTemplate.search("cn=Mahesh Chopker,ou=People",
                "(objectclass=*)", new AttributesMapper() {
                    public Object mapFromAttributes(Attributes attrs)
                            throws NamingException {
                        Attributes attributes = new BasicAttributes();
                        attributes.put(attrs.get("cn"));
                        attributes.put(attrs.get("uid"));
                        return attributes;
                    }
                }));
    }
}

Java Spring Client with DI

package com.ldap.springclient;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;

public class LDAPSpringDIClient {

    private LdapTemplate ldapTemplate;

    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            ApplicationContext context = new ClassPathXmlApplicationContext(
                    "context.xml");
            LDAPSpringDIClient ldapClientInstance = (LDAPSpringDIClient) context
                    .getBean("ldapClient");

            if (args != null && args.length > 0) {
                String input = args[0];
                switch (input) {
                case "1":
                    ldapClientInstance.createPerson("Mahesh K Chopker",
                            "Mahesh", "mchopker", "Sales");
                    break;
                case "2":
                    ldapClientInstance.modifyPerson("Mahesh K Chopker",
                            "Chopker", "mchopker2", "Sales");
                    break;
                case "3":
                    ldapClientInstance.removePerson("Mahesh K Chopker");
                    break;
                default:
                    ldapClientInstance.searchPerson();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void createPerson(String name, String sn, String uid, String ou)
            throws NamingException {
        Attributes attrs = new BasicAttributes();
        String dn = "cn=" + name + ",ou=people";
        Attribute attr = new BasicAttribute("objectClass");
        attr.add("inetOrgPerson");
        attrs.put(attr);
        attrs.put("sn", sn);
        attrs.put("uid", uid);
        attrs.put("ou", ou);
        ldapTemplate.bind(dn, null, attrs);
    }

    private void modifyPerson(String name, String sn, String uid, String ou)
            throws NamingException {
        Attributes attrs = new BasicAttributes();
        String dn = "cn=" + name + ",ou=people";
        Attribute attr = new BasicAttribute("objectClass");
        attr.add("inetOrgPerson");
        attrs.put(attr);
        attrs.put("sn", sn);
        attrs.put("uid", uid);
        attrs.put("ou", ou);
        ldapTemplate.rebind(dn, null, attrs);
    }

    private void removePerson(String name) throws NamingException {
        ldapTemplate.unbind(name);
    }

    private void searchPerson() {
        System.out.println(ldapTemplate.search("cn=Mahesh K Chopker,ou=People",
                "(objectclass=*)", new AttributesMapper() {
                    public Object mapFromAttributes(Attributes attrs)
                            throws NamingException {
                        Attributes attributes = new BasicAttributes();
                        attributes.put(attrs.get("cn"));
                        attributes.put(attrs.get("uid"));
                        return attributes;
                    }
                }));
    }
}
 

Testing

  • The test clients have options to add/modify/delete and search LDAP which can be triggered based on the command line input value 1,2,3 and 4.
  • The source code and LDIF data file can be downloaded from following git repository location:
    • https://github.com/mchopker/myprojects/tree/master/LDAPExample

 Thank You!

Sunday 6 October 2013

How to identify a High CPU thread in a Java application

Objective

To understand the basics of how a high cpu thread can be identified in a Java application.

Notes

  1. There are many good articles in net which tell step by step instructions to identify a high cpu thread in a Java application running on different OS. In this article I just try to summarize the key concepts instead of walk you through the steps/examples.
  2. The Linux OS have one to one mapping between Java (version 1.2 onwards) threads and OS threads, and the easiest way to identify a high cpu thread is:
    1. Run command: top -H
  3. The output of above command will be PID (thread id) sorted in descending order of CPU usage.
  4. Once you get the PID of high cpu thread (probably you need to monitor the high CPU thread for some time to see if it is keeping the CPU usage high for some longer time), take a thread dump of your Java application (via jstack or any other profiling tool lie VisualVM).
  5. The PID you got has to be converted into Hexa number before you search that into thread dump to identify which Java thread in your application it is and with the stack trace in dump you will be able to identify what kind of task the thread is doing.
Thanks You!

Saturday 5 October 2013

Java Keytool command usage

Objective

In a Client-Server Model when Server is exposing resources (like WebService, Servlets, etc) on HTTPS, the Java client program needs to authenticate the Server certificate to access the resource. I tried to explain the usage of few basic Keytool commands which are handy in such situations.

Environment

Java 7 (JDK or JRE)

Command Usage

  • First you need to obtain the Server certificate, you can do via hitting the resource URL in a Web Browser. Your Web Browser will ask to authenticate the Server and trust the certificate, once you do that, you will see a small icon on Address URL (either on left of right side of the URL), you can click the icon and it will provide you options to export the certificate.
  • Export the certificate in a .pem or .crt format and copy it to directory: <JAVA/JRE_HOME>/jre/lib/security and switch to this directory.
  • Command to import certificate into Java's truststore
    • keytool -import -file <PEM or CRT file you copied> -keystore cacerts
      • The cacerts truststore password is changeit 
Now the Java Client program should be able to access the server resource over https. Following are few additional commands which you might find useful:
  • Command to see available certificates in Java's truststore
    • keytool -list -keystore cacerts
  • Command to delete an existing certificate from Java's truststore
    • keytool -delete -alias <certificate alais name> -keystore cacerts
  • Command to change the alias name of an existing certificate from Java's truststore (this command is useful when you have more than one certificates but with same alias)
    • keytool -changealias -alias <existing-alias-name> -destalias <new-alias-name> -keystore cacerts
  • You can also export a certificate stored in a keystore via following command
    • keytool -export -file <exporeted-cert-filename.crt> -alias <alias-name-of-cert-which-to-be-exported> -keystore cacerts
Please note that all above commands are valid for any keystore (not only Java's own cacerts trustore); in that case you just put your keystore name instead of cacerts. Generally this is required when your application is maintaining its own keystore.