Labels

Saturday, December 17, 2011

Tiển Mẹ

Ngàn hoa con tiển mẹ đi
Đường quê cát trắng râm ri biển gào
Mẹ đi mưa ướt hồn nào
Trời mưa thay tiếng rì rào cỏi tâm

Bồng lai ngây ngất hương trầm
Lời con khấn nguyện âm thầm mẹ hay
Đến miền cực lạc an thay
Vãng sanh cảnh thế duyên nay vẹn toàn

Tặng mẹ,
T.A. Nguyen
Sàigòn - 12/17/2011

Wednesday, May 11, 2011

RemoteService a simple client to test your Restful and SOAP services

Random thought...

As Java growth so does the Web Service spaces along with the complexity of the services it can handle. With Axis/Axis2 then come along CXF many of these implementations are wonderful in focusing on how to make it easier for the developer to create Webservices. I am all for it, and thankful for those who contribute to making it better. SoapUI a recent addition to the sleuth of tool that helps with the web-services space, great tool, a must check and use if you use web services day in day out.

As an educator, I often being asked about the basic building block behind these. One of my favorites is the simple HTTP/HTTPS implementation. Yes, it all started when the world wild web protocol started....
So, I find myself referring back to my basic HTTP/S GET/POST codes that had been developed a decade ago... and amazingly, it still works, not only that it also serves as a great example for my student to understand the building block behind it all.

Here is a simple class that I been using for a while but recently modified to add some JUnit tests and more convenient methods for SOAP and RESTful operations:

package org.cnci.service.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Scanner;

import javax.xml.bind.DatatypeConverter;

import org.junit.Test;

/**
 * RemoteSerivce: A simple client to test your RESTful and SOAP web service
 * along with other HTTP Protocols as well. Tested protocols:
 *
 * SOAP Web Service (is POST with SOAPAction and SOAP envelop wrapped data content)
 * RESTful Web Service:
 * * POST    http operation (also as RESTful POST  )
 * * GET     http operation (also as RESTful GET   )
 * * DELETE  http operation (also as RESTful DELETE)
 * * PUT     http operation (also as RESTful PUT   )
 * * HEAD    http operation - not tested
 * * OPTIONS http operation - not tested
 * * TRACE   http operation - not tested
 *
 * provide as is, no warranty.
 *
 * @author T.A. Nguyen ta{at}cnci{dot}org
 *
 * Article:
 *  http://tinyurl.com/taRemoteService
 **/
public class RemoteService {
   public enum Operation {
      GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE;
   }
    
   // SOAP :: Convenient method to support SOAP Action via HTTP POST Operation
   public static String SOAP(String url, String action, String data, String user, String pwd)
            throws RemoteServiceException {
      return remoteService(action, url, data, user, pwd, null);
   }
    
   // PUT :: Convenient method to support HTTP PUT Operation
   public static String PUT(String url, String data, String user, String password)
            throws RemoteServiceException {
      return remoteService(HTTP(Operation.PUT), url, data, user, password, null);
   }

   // DELETE :: Convenient method to support HTTP DELETE Operation
   public static String DELETE(String url, String data, String user, String password)
            throws RemoteServiceException {
      return remoteService(HTTP(Operation.DELETE), url, data, user, password, null);
   }

   // POST :: Convenient method to support HTTP POST Operation
   public static String POST(String url, String data, String user, String password)
            throws RemoteServiceException {
      return remoteService(HTTP(Operation.POST), url, data, user, password, null);
   }

   // GET :: Convenient method to support simple HTTP GET Operation
   public static String GET(String getUrl, String user, String password, String encoding)
            throws RemoteServiceException {
      return remoteService(HTTP(Operation.GET), getUrl, null, user, password, encoding);
   }

   // GET :: Convenient method to support full HTTP Get Operation (using default encoding...)
   public static String GET(String getUrl, String user, String password)
            throws RemoteServiceException {
      return remoteService(HTTP(Operation.GET), getUrl, null, user, password, null);
   }

   /**
    * Simple remote get/post client if data present, it will do a post, if user
    * present it will place it in the Authorization header
    * - ONLY BASIC Authorization Support at this time!
    *
    * @throws RemoteServiceException
    **/
   public static String remoteService(String operation, String getUrl, String data,
            String user, String password, String encoding)
          throws RemoteServiceException {
      String result = null;
      URLConnection uc = null;

      try {
         URL url = new URL(getUrl);
         uc = url.openConnection();
         uc.setRequestProperty("User-Agent", "TAzilla/4.0");

         /**
          * ----- encoding, we use "identity" for authentication -------
          * Accept-Encoding: compress, gzip
          * Accept-Encoding: Accept-Encoding: *
          * Accept-Encoding: compress;q=0.5, gzip;q=1.0
          * Accept-Encoding: * gzip;q=1.0, identity; q=0.5, *;q=0
          **/

         if (encoding == null)
            encoding = "identity";
         uc.setRequestProperty("Accept-Encoding", encoding);
         if (user != null) {
            String auth = user;
            if (password != null) {
               String encode = user + ":" + password;
               auth = "Basic " + encode(encode.getBytes());
            }
            uc.setRequestProperty("Authorization", auth);
         }
         if (data != null) {
            uc.setDoOutput(true);
            if (uc instanceof HttpURLConnection && operation != null) {
               if (isHttpOperation(operation)) {
                  // HTTP Operation is only support the HTTP(s) connections type
                  ((HttpURLConnection)uc).setRequestMethod(operation);
               } else {
                  uc.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
                  uc.setRequestProperty("Content-Length", "" + data.length());
                  // NOTES: if you have a SOAPAction of PUT/GET/DELETE/POST you are in trouble
                  uc.setRequestProperty("SOAPAction", operation);
               }
            }
            byte[] payload = data.getBytes();
             
            ((HttpURLConnection)uc).setFixedLengthStreamingMode(payload.length);
            uc.getOutputStream().write(payload);
            uc.getOutputStream().flush();
         }

         // Get the response
         result = readResponse(uc.getInputStream());

         if (result.indexOf("Exception: ") > 0) {
            throw new RemoteServiceException(result);
         }
      } catch (Exception e) {
         throw new RemoteServiceException(e.getMessage(), e);
      } finally {
         try {
            if (uc instanceof HttpURLConnection) ((HttpURLConnection)uc).disconnect();
         } catch (Exception e) {
            e.printStackTrace();
         }
      }

      return result;
   }
    
   // Rewrite for fast reading, not recommended for large data
   private static String readResponse(InputStream is) throws IOException {
      String value = null;
      Scanner s = null;
      try {
         s = new Scanner(is, "UTF-8").useDelimiter("\\A");
         value = s.hasNext() ? s.next() : "";
      } finally {
         if (s != null) {
            try {
               s.close();
            } catch (Exception e) {
               // nada!
            }
         }
      }
      return value;
  }
    
   private static boolean isHttpOperation(String operation) {
      if (operation == null) return false;
      for (Operation o : Operation.values()) {
         if (o.toString().equals(operation)) return true;
      }
      return false;
   }
    
   private static String HTTP(Operation op) {
      return ("" + op).toUpperCase();
   }
    
   // Base64 decoding -- not use here
   public static byte[] decode(String msg) {
      return DatatypeConverter.parseBase64Binary(msg);
   }

   // Base64 encoding
   public static String encode(byte[] b) {
      return DatatypeConverter.printBase64Binary(b);
   }

   // TEST SUITE!!! :D
   public static void main(String[] args) {
      RemoteService rs = new RemoteService();
      rs.testSOAPVerifyEmail();
      rs.testRESTVerifyEmail();
      rs.testPOSTVerifyEmail();
      rs.testSOAP12VerifyEmail();
   }
    
   private static final String TEST_EMAIL   = "mutantninja" + "@" + "gmail.com";
   private static final String TEST_LICENSE = "example";
   @Test
   // Simple example to use GET
   public void testRESTVerifyEmail() {
      String url = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx/"
                 + "VerifyEmail?email=" + TEST_EMAIL + "&LicenseKey=" + TEST_LICENSE;
      try {
         String result = GET(url, null, null);
         System.out.println(result);
      } catch (Exception e) {
         System.err.println("Unable to send SOAP request to " + url + "; error:" + e.getMessage());
         e.printStackTrace();
      }
   }
    
   @Test
   // Simple example to use POST
   public void testPOSTVerifyEmail() {
      String url = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx/VerifyEmail";
      try {
         String result = POST(url, "email=" + TEST_EMAIL + "&LicenseKey=" + TEST_LICENSE, null, null);
         System.out.println(result);
      } catch (Exception e) {
         System.err.println("Unable to send SOAP request to " + url + "; error:" + e.getMessage());
         e.printStackTrace();
      }
   }
    

   @Test
   // Example for sending SOAP1.2 envelop, the easy way is to use SOAPUI to get the envelop
   public void testSOAP12VerifyEmail() {
      String url = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";
      String soapEnv = "SOAP 1.2 ENVELOPE HERE";
      String soapAction = "http://ws.cdyne.com/VerifyEmail";
      try {
         String result = SOAP(url, soapAction, soapEnv, null, null);
         System.out.println(result);
      } catch (Exception e) {
         System.err.println("Unable to send SOAP request to " + url + "; error:" + e.getMessage());
         e.printStackTrace();
      }
   }
    
   @Test
   // Example for sending SOAP1.1 envelop, the easy way is to use SOAPUI to get the envelop
   public void testSOAPVerifyEmail() {
      String url = "http://ws.cdyne.com/emailverify/Emailvernotestemail.asmx";
      String soapEnv = "SOAP 1.1 ENVELOPE HERE";
      String soapAction = "http://ws.cdyne.com/VerifyEmail";
      try {
         String result = SOAP(url, soapAction, soapEnv, null, null);
         System.out.println(result);
      } catch (Exception e) {
         System.err.println("Unable to send SOAP request to " + url + "; error:" + e.getMessage());
         e.printStackTrace();
      }
   }
          
   /**
    * provide as is, no warranty.
    *
    * @author T.A. Nguyen ta{at}cnci{dot}org
    **/
   public static class RemoteServiceException extends Exception {
      private static final long serialVersionUID = 1L;

      public RemoteServiceException() {
      }

      public RemoteServiceException(String msg) {
         super(msg);
      }

      public RemoteServiceException(Throwable e) {
         super(e);
      }

      public RemoteServiceException(String msg, Throwable e) {
         super(msg, e);
      }
   }
}
And as many of the https protocol, there are invalid/expired certs that show up more often that us developers like to see. The problem is a valid certificate cost yearly fee, not sure why? And so to enable the security cost, most of the smaller (mom and pop) web sites and services just use the default or expired certs, this cause the problem to the Java space of end up enforcing to a failed certificate. Sun, provided a neat tool called InstallCert.java by Sun Micro System. Just download and follow these steps
  • javac InstallCert.java
  • java InstallCert yoursslserver:sslport
For a full solution refer to InstallCert on GitHub
SOAP 1.1 Example:


SOAP 1.2 Example: