Skip to content

Kx Connect (Java) appendix

Java Dependencies

// Place these .jar files or equivalent under the lib folder.

// Used for ConnectSample.java
commons-codec-1.9.jar
commons-logging-1.2.jar
httpclient-4.5.1.jar
httpcore-4.4.3.jar
json-simple-1.1.1.jar
spring-security-core-4.0.1.RELEASE.jar


// Used for ConnectSampleWithCSV.java
commons-codec-1.9.jar
commons-logging-1.2.jar
httpclient-4.5.1.jar
httpcore-4.4.3.jar
spring-security-core-4.0.1.RELEASE.jar
org.apache.commons.io.jar
java-json.jar

ConnectSample.java

// Declare the package
package com.fd.connect;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;


public class ConnectSample {

    private HttpClient client;  

    /* NOTES
     * The following sample implementation uses various 3rd party libraries for simplicity and convenience.
     * Equivalents which provide the same functionality may be used. 
     * The imported jar files for this sample are noted above in the imports section.
     *  
     * The following sample throws all exceptions rather than handling them, in order to make the sample code easily readable. 
     * Full implementations should handle all exceptions appropriately   
     */ 


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

        String baseURL = "http://localhost:8080/connect";
        String userName = "Administrator";
        String password = "password";
        String method = "getMem";

        if(args.length > 0) {
            if (args.length == 4){
                baseURL = "http://" + args[0] + "/connect";
                userName = args[1];
                password = args[2];
                method = args[3];
            } else{
                System.err.printf("Expected parameters host:port username password method. Received %d arguments\n", args.length);
                System.err.printf("Continuing using default parameters");
            }
        }   

        ConnectSample connectSample = new ConnectSample();      

        connectSample.makeRestRequests(baseURL, userName, password, method);        
    }


    public ConnectSample() {    
        client = HttpClientBuilder.create().build();
    }


    private void makeRestRequests(String baseURL, String userName, String password, String method) throws Exception {

        // Login - the login request differs from all other calls as it does not require the payload to be encoded
        // A Session ID is required to encode all subsequent requests after login , including logout

        log("Making request to login to URL : " + baseURL + " for user " + userName);

        String group = "auth";
        String analytic = "login";
        String requestType = analytic + "Req";
        String requestId = makeRequestGUID();

        String sessionId = null;

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("username", userName);
        parameters.put("password", password);


        String loginResponseJSON = callRestAnalytic(baseURL, userName, sessionId, group, analytic, requestType, parameters, requestId);

        // Extract sessionId from successful login
        sessionId = parseSessionIdFromJSON(loginResponseJSON);

        if(sessionId != null){
            log("Logged in with session ID : " + sessionId);        

            // Call the .monPublic.getMem analytic with empty parameters
            parameters = new HashMap<String, Object>();
            parameters.put("start", "");    
            parameters.put("end", "");
            JSONArray symsArray = new JSONArray();
            parameters.put("syms", symsArray);

            group = "monPublic";
            // analytic = "getMem";
            requestType = method + "Req";

            log("\nMaking request to analytic : " + method);
            String responseJSON = callRestAnalytic(baseURL, userName, sessionId, group, method, requestType, parameters, requestId);

            // Logging response in a readable format
            parseJsonResponse(responseJSON, method);

        }else{
            System.err.printf("Failed login\n");
            System.exit(0);
        }
    }   


    private void parseJsonResponse(String json, String analytic) {

        JSONParser parser = new JSONParser();
        JSONObject jsonObject = null;
        try {
            jsonObject = (JSONObject )parser.parse(json);
        } catch (ParseException e) {            
            e.printStackTrace();
        }

        JSONArray messageArray = (JSONArray)jsonObject.get("msg");

        for(int i=0; i<messageArray.size(); i++) {
            JSONObject messagePart = (JSONObject)messageArray.get(i);
            String error = (String)messagePart.get("exceptionMessage");
            if(error!=null) {
                log("Extracted error from invalid request : " + error);
            }else{
                log("Got response JSON for analytic " + analytic + " : " + json);
            }
        }
    }


    private String callRestAnalytic(String baseURL, String userName, String sessionId, String group, String method, String requestType, Map<String, Object> parameterMap, String requestId) throws Exception {

        URI path = new URI(baseURL + "/api/" + group + "/" + method);

        String dateStr = formatCurrentDate();

        String requestJSON = buildRequestJSON(requestId, dateStr, requestType, parameterMap);
        log("RequestJSON is : " + requestJSON);   

        StringEntity data = new StringEntity(requestJSON, ContentType.APPLICATION_JSON);   

        HttpPost post = new HttpPost(path);
        post.setEntity(data);

        // sessionId available for login call
        if(sessionId != null) {

            // MD5 encode the payload
            String contentEncoded = contentToMD5(requestJSON);  

            String stringToSign = constructRESTSignature("POST", path.getPath(), sessionId, contentEncoded,
                    "application/json", dateStr, userName);
            String authorization = getPublicKey(userName, sessionId) + ":" + calculateHMAC(stringToSign, sessionId);

            log("Authorization is : " + authorization);

            // Add authorization to the request header
            post.addHeader(new BasicHeader("Content-Type","application/json"));
            post.addHeader(new BasicHeader("Authorization", authorization));
        }

        String responseJSON = doHttpPost(post);

        return responseJSON;        
    }



    @SuppressWarnings("unchecked")
    private String buildRequestJSON(String requestId, String dateStr, String requestType, Map<String, Object> parameterMap) {

        JSONObject payload = new JSONObject();

        // All messages require these mandatory details
        payload.put("type", requestType);       
        payload.put("id", requestId);
        payload.put("date", dateStr);


        JSONArray messageArray = new JSONArray();
        messageArray.add(parameterMap);     
        payload.put("msg", messageArray);

        return payload.toJSONString();
    }



    private String getPublicKey(String userName, String sessionID) {
        return userName + sessionID.substring(sessionID.length() - 5, sessionID.length());
    }


    private String makeRequestGUID() {
        return UUID.randomUUID().toString();
    }


    private String formatCurrentDate() {
        SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
        Date now = new Date();
        return formatter.format(now);
    }


    private String doHttpPost(HttpPost post) throws Exception {
        HttpResponse response = client.execute(post);
        return  extractPayloadFromHttpResponse(response);
    }


    private String extractPayloadFromHttpResponse(HttpResponse response) throws Exception {

        InputStream content = response.getEntity().getContent();
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(content));

        char[] charBuffer = new char[128];
        int bytesRead = -1;
        while ((bytesRead = reader.read(charBuffer)) > 0) {
            stringBuilder.append(charBuffer, 0, bytesRead);
        }
        String responseText = stringBuilder.toString();     

        return responseText;
    }



    private String parseSessionIdFromJSON(String json) {
        log(json);
        JSONParser parser = new JSONParser();
        JSONObject jsonObject = null;
        try {
            jsonObject = (JSONObject )parser.parse(json);
        } catch (ParseException e) {            
            e.printStackTrace();
        }

        JSONArray messageArray = (JSONArray)jsonObject.get("msg");
        JSONObject sessionId = (JSONObject)messageArray.get(0);
        return (String)sessionId.get("sessionId");
    }




    private String contentToMD5(String content) throws Exception {
        Md5PasswordEncoder encoder =  new Md5PasswordEncoder();
        return encoder.encodePassword(content, null);
    }



    private String constructRESTSignature(String httpMethod, String requestURI, String sessionId,
            String contentMd5, String contentType, String timestamp, String username) {

        // Calculate content to sign        
        StringBuffer toSign = new StringBuffer();

        toSign.append(httpMethod).append("\n")
               .append(requestURI).append("\n")
               .append(username).append("\n")
               .append(contentMd5).append("\n")
               .append(contentType).append("\n")
               .append(timestamp);

        if (sessionId != null && sessionId.length() > 0) {
            toSign.append("\n").append(sessionId);
        }
        return toSign.toString();
    }



    private String calculateHMAC(String data, String secretKey) throws Exception {

        String result;

        log("Data for signing : \n" + data + "\n");
        log("Secret key is: " + secretKey);

        // Get an hmac_sha1 key from the raw key bytes
        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");

        // Get an hmac_sha1 Mac instance and initialize with the signing key
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);

        // Compute the hmac on input data bytes
        byte[] rawHmac = mac.doFinal(data.getBytes());

        log("HMAC is : " + new String(rawHmac, "UTF8"));

        // Base64-encode the hmac
        result = new String(Base64.encodeBase64(rawHmac));

        log("HMAC after encoding is : " + result);

        return result;
    }


    private void log(String logMessage) {
        System.out.println(logMessage);
    }
}

ConnectSampleWithCSV.java


// Declare the package
package com.fd.connect;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;

// For CSV Export
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.json.CDL;


public class ConnectSample {

    private HttpClient client;  

    /* NOTES
     * The following sample implementation uses various 3rd party libraries for simplicity and convenience.
     * Equivalents which provide the same functionality may be used. 
     * The imported jar files for this sample are noted above in the imports section.
     *  
     * The following sample throws all exceptions rather than handling them, in order to make the sample code easily readable. 
     * Full implementations should handle all exceptions appropriately   
     */ 


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

        String baseURL = "http://localhost:8080/connect";
        String userName = "Administrator";
        String password = "password";
        String method = "getMem";

        if(args.length > 0) {
            if (args.length == 4){
                baseURL = "http://" + args[0] + "/connect";
                userName = args[1];
                password = args[2];
                method = args[3];
            } else{
                System.err.printf("Expected parameters host:port username password method. Received %d arguments\n", args.length);
                System.err.printf("Continuing using default parameters");
            }
        } 

        ConnectSampleWithCSV connectSample = new ConnectSampleWithCSV();        

        connectSample.makeRestRequests(baseURL, userName, password, method);        
    }


    public ConnectSampleWithCSV() { 
        client = HttpClientBuilder.create().build();
    }


    private void makeRestRequests(String baseURL, String userName, String password, String method) throws Exception {

        // Login - the login request differs from all other calls as it does not require the payload to be encoded
        // A Session ID is required to encode all subsequent requests after login , including logout

        log("Making request to login to URL : " + baseURL + " for user " + userName);

        String group = "auth";
        String analytic = "login";
        String requestType = analytic + "Req";
        String requestId = makeRequestGUID();

        String sessionId = null;

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("username", userName);
        parameters.put("password", password);


        String loginResponseJSON = callRestAnalytic(baseURL, userName, sessionId, group, analytic, requestType, parameters, requestId);

        // Extract sessionId from successful login
        sessionId = parseSessionIdFromJSON(loginResponseJSON);

        if(sessionId != null){
            log("Logged in with session ID : " + sessionId);        

            // Call the .monPublic.getMem analytic with empty parameters
            parameters = new HashMap<String, Object>();
            parameters.put("start", "");    
            parameters.put("end", "");
            JSONArray symsArray = new JSONArray();
            parameters.put("syms", symsArray);

            group = "monPublic";
            requestType = method + "Req";

            log("\nMaking request to analytic : " + method);
            String responseJSON = callRestAnalytic(baseURL, userName, sessionId, group, method, requestType, parameters, requestId);

            // Logging response in a readable format
            parseJsonResponse(responseJSON, method);

        }else{
            System.err.printf("Failed login\n");
            System.exit(0);
        }
    }   


    private void parseJsonResponse(String json, String analytic) {

        try {
            JSONObject jsonObject = new JSONObject(json);
            JSONArray messageArray = jsonObject.getJSONArray("msg");

            String error = null;
            for(int i=0; i<messageArray.length(); i++) {
                JSONObject messagePart = messageArray.getJSONObject(i);

                if(messagePart.has("exceptionMessage")) {
                    error = messagePart.getString("exceptionMessage");
                }
            }

            if(error!=null) {
                log("Extracted error from invalid request : " + error);
            }else{
                log("Got response JSON for analytic " + analytic + " : " + json);
                // CSV Export
                exportToCSV(messageArray, analytic);
            }

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

    private void exportToCSV(JSONArray data, String analytic) {

        try {
            Date today = new Date();
            String date= new SimpleDateFormat("yyyyMMdd").format(today);
            File filename = new File("/tmp/" + analytic + "_" + date + ".csv");

            String csv = CDL.toString(data);
            FileUtils.writeStringToFile(filename, csv);

            log("Finished writing CSV");

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


    private String callRestAnalytic(String baseURL, String userName, String sessionId, String group, String method, String requestType, Map<String, Object> parameterMap, String requestId) throws Exception {

        URI path = new URI(baseURL + "/api/" + group + "/" + method);

        String dateStr = formatCurrentDate();

        String requestJSON = buildRequestJSON(requestId, dateStr, requestType, parameterMap);
        log("RequestJSON is : " + requestJSON);   

        StringEntity data = new StringEntity(requestJSON, ContentType.APPLICATION_JSON);   

        HttpPost post = new HttpPost(path);
        post.setEntity(data);

        // sessionId available for login call
        if(sessionId != null) {

            // MD5 encode the payload
            String contentEncoded = contentToMD5(requestJSON);  

            String stringToSign = constructRESTSignature("POST", path.getPath(), sessionId, contentEncoded,
                    "application/json", dateStr, userName);
            String authorization = getPublicKey(userName, sessionId) + ":" + calculateHMAC(stringToSign, sessionId);

            log("Authorization is : " + authorization);

            // Add authorization to the request header
            post.addHeader(new BasicHeader("Content-Type","application/json"));
            post.addHeader(new BasicHeader("Authorization", authorization));
        }

        String responseJSON = doHttpPost(post);

        return responseJSON;        
    }



    @SuppressWarnings("unchecked")
    private String buildRequestJSON(String requestId, String dateStr, String requestType, Map<String, Object> parameterMap) {

        JSONObject payload = new JSONObject();

        try {       
            // All messages require these mandatory details
            payload.put("type", requestType);       
            payload.put("id", requestId);
            payload.put("date", dateStr);


            JSONArray messageArray = new JSONArray();
            messageArray.put(parameterMap);     
            payload.put("msg", messageArray);

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

        return payload.toString();
    }



    private String getPublicKey(String userName, String sessionID) {
        return userName + sessionID.substring(sessionID.length() - 5, sessionID.length());
    }


    private String makeRequestGUID() {
        return UUID.randomUUID().toString();
    }


    private String formatCurrentDate() {
        SimpleDateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
        Date now = new Date();
        return formatter.format(now);
    }


    private String doHttpPost(HttpPost post) throws Exception {
        HttpResponse response = client.execute(post);
        return  extractPayloadFromHttpResponse(response);
    }


    private String extractPayloadFromHttpResponse(HttpResponse response) throws Exception {

        InputStream content = response.getEntity().getContent();
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(content));

        char[] charBuffer = new char[128];
        int bytesRead = -1;
        while ((bytesRead = reader.read(charBuffer)) > 0) {
            stringBuilder.append(charBuffer, 0, bytesRead);
        }
        String responseText = stringBuilder.toString();     

        return responseText;
    }



    private String parseSessionIdFromJSON(String json) {

        String sessionId = null;

        try {
            JSONObject jsonObject = new JSONObject(json);
            JSONArray messageArray = jsonObject.getJSONArray("msg");
            JSONObject sessionIdObj = messageArray.getJSONObject(0);
            sessionId = sessionIdObj.getString("sessionId");

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

        return sessionId;
    }



    private String contentToMD5(String content) throws Exception {
        Md5PasswordEncoder encoder =  new Md5PasswordEncoder();
        return encoder.encodePassword(content, null);
    }



    private String constructRESTSignature(String httpMethod, String requestURI, String sessionId,
            String contentMd5, String contentType, String timestamp, String username) {

        // Calculate content to sign        
        StringBuffer toSign = new StringBuffer();

        toSign.append(httpMethod).append("\n")
               .append(requestURI).append("\n")
               .append(username).append("\n")
               .append(contentMd5).append("\n")
               .append(contentType).append("\n")
               .append(timestamp);

        if (sessionId != null && sessionId.length() > 0) {
            toSign.append("\n").append(sessionId);
        }
        return toSign.toString();
    }



    private String calculateHMAC(String data, String secretKey) throws Exception {

        String result;

        log("Data for signing : \n" + data + "\n");
        log("Secret key is: " + secretKey);

        // Get an hmac_sha1 key from the raw key bytes
        SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");

        // Get an hmac_sha1 Mac instance and initialize with the signing key
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);

        // Compute the hmac on input data bytes
        byte[] rawHmac = mac.doFinal(data.getBytes());

        log("HMAC is : " + new String(rawHmac, "UTF8"));

        // Base64-encode the hmac
        result = new String(Base64.encodeBase64(rawHmac));

        log("HMAC after encoding is : " + result);

        return result;
    }


    private void log(String logMessage) {
        System.out.println(logMessage);
    }

}