Sunday, September 23, 2012
Chiếc Lá!
Đêm đông nảy lộc, duyên đành phận trao
Ngày qua tháng lại ngóng đào
Bao giờ hoa nở lá chào đón hoa
Đồi hoa nẩy mộng anh đào
Ong kia bướm nọ xúm vào xúm ra
Lặng ơi tiếng gió chiều tà
Hoa ơi có biết rằng ta ngóng nảng
...
Tiếng ai long lảnh chiều tàn
Nhạc khúc vui nhộn muôn ngàn hoa rơi
Hoa đi lá lại ... lả lơi
Tìm trong tiếng nhạc mảnh đời ngây ngây
Rồi đây Hạ đến ấm đầy
Ru ơi khúc Hạ nắng gầy nhẹ trao
Cỏi ta còn lắm lao đao
Hạ ơi đừng nhé kiếm đào mà đi !
Mến tặng ca sĩ Nhật Hạ,
T.A. Nguyen
September 23, 2012
Sunday, May 6, 2012
TinyWebServer.java an embedded sun httpserver implementation since JRE 1.6 less than 300 lines!
TinyWebServer.java is a Java implementation based on the embedded Sun httpserver which available since JRE 1.6. (in rt.jar) and is less than 300 lines of codes.
During my development work, I started out using the JETTY a small and lightweight web engine, but I need to attach a few jars to my projects. My automated build/deployment also need to pack these libraries as well since my test cases depend on it.
Since version 1.6 of the sun JRE, the rt.jar come with the embedded version (limited functionality) that work well, and just what I needed to set up my stub web services to ensure reliable test run. I also use this "TinyWebServer.java" for a small demo to my student as well. So, here are the codes for it. Next post I will share with you on how I use it in my JUnit test to set up and run my stub web services.
Remember, if you get errors in your eclipse project, simply remove the "System Library" and add it back, this will remove the security policy from the eclipse build.
package org.cnci.util; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URL; import java.security.KeyStore; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.xml.ws.Endpoint; import org.apache.log4j.Logger; import com.sun.net.httpserver.HttpContext; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; /** * TinyWebServer * * @author T.A. Nguyen * * Tiny Embedded (Stub) Web Server, intended for testing only. This implementation * will take advantage of the "sun" httpserver package that shipped with the JRE * since release 1.6. * * Provide an easy creation of a TinyWebServer that can be start up and host as * an embedded (secure) web server. Just pass in the URL as you intended to access * and it will attempt to host in that url, whether it is secure or not. This * will make it an ideal to configure and setup as a unit test stub server. * * By default, Eclipse will add restriction to com.sun.net.httpserver.* * package access, just need to remove the JRE library and add it back * then it will allow you to use it. * * Usage: * * TinyWebServer server = new TinyWebServer("https://*:8080"); * AddressBookSoap myBook = new AddressBookSoapImpl(); // your service implementation here * server.addSoapService("/ws/myBook", myBook); * PersonnelSoap myHR = new StubPersonnelSoapImpl(); // my other web service, local stub * server.addSoapService("/ws/myHR", myHR); * server.start(); * */ public class TinyWebServer { private static final String SSL_VERSION = "SSLv3"; private static Logger log = Logger.getLogger(TinyWebServer.class); private static final String KEYSTORE_FILENAME = "localhost.server.keystore.jks"; private static final String KEYSTORE_PASSWORD = "cnci.org"; private static final int RETRY_TIME = 5; private static final int SLEEP_TIME = 500; private static final String DEFAULT_PROTOCOL = "https"; private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 8080; private HttpServer server = null; private Map services = new HashMap(); private List endpoints = new ArrayList(); private String url; // same as the client access url private String protocol; // http/https private String host; // ip or host name private int port; /** * Simple, just pass the url as the client expected, * make sure that the host is of a local host name or ip * otherwise it will default to "localhost" * * https://*:8080 will listen to all local addresses on port 8080 via https (secure) protocol * https://localhost:8080 will listen to 127.0.0.1:8080 via https (secure) protocol * http://*:8080 will listen to all local addresses on port 8080 via http protocol * * @param host * @param port */ public TinyWebServer(String url) { this.url = url; } public TinyWebServer(String host, int port) { this.host = host; this.port = port; } public void addSoapService(String context, Object service) throws Exception { if (context == null || context.trim().length() <= 0) { throw new Exception("Context uri is a required parameter."); } if (service == null) { throw new Exception("Soap service handler is a required parameter."); } if (services.containsKey(context)) { throw new Exception("Context uri is already exist."); } services.put(context, service); createEndpoint(getServer().createContext(context), service); } public void addHttpService(String context, HttpHandler handler, Executor executor) throws Exception { if (context == null || context.trim().length() <= 0) { throw new Exception("Context uri is a required parameter."); } if (handler == null) { throw new Exception("HttpHandler service handler is a required parameter."); } if (services.containsKey(context)) { throw new Exception("Context uri is already exist."); } services.put(context, handler); server.createContext(context, handler); if (executor == null) executor = Executors.newCachedThreadPool(); // Executors.newFixedThreadPool(5) server.setExecutor(executor); // null for Default executor } public void start() throws Exception { HttpServer s = getServer(); Exception e = null; boolean started = false; int count = 0; while (!started && count 0) { port = u.getPort(); } if (u.getProtocol() != null) { protocol = u.getProtocol(); } } catch (Exception e) { // ignore } } if (protocol == null) protocol = DEFAULT_PROTOCOL; if (host == null) host = DEFAULT_HOST; if (port <= 0) port = DEFAULT_PORT; } private boolean isSsl() { return DEFAULT_PROTOCOL.equalsIgnoreCase(getProtocol()); } private String getHost() { if (host == null && url != null) validateUrl(); return host; } private int getPort() { if (host == null && url != null) validateUrl(); return port; } private String getProtocol() { if (host == null && url != null) validateUrl(); return protocol; } }
Saturday, April 28, 2012
DailyFileAppender.java a Solution to a log4j Daily Appender with a Retention!
I use the Log4j DailyRollingFileAppender over many years and many projects but always end up controlling the retention via a Cron jobs. That's work well, but what if we can control the retention in the log4j configuration file? Wouldn't that be great? Well, that came up and I wanted to share my solution with you:
package org.cnci.util; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.log4j.FileAppender; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.spi.LoggingEvent; /** * Provide AS IS, no warranty! * * Special log appender for log4j. Adds the current date (ie. year-mon) to the * end of the file name, so that rolling on to the next log is simply a case of * starting a new one - no renaming of old logs. * * ONLY support: yyyy (yearly) | yyyy-MM (monthly) | yyyy-MM-dd (daily) | * yyyy-ww (weekly) yyyy-MM-dd-a (midnight, midday) | yyyy-MM-dd-HH (hourly) | * yyyy-MM-dd-HH-mm (every minute) * * An example log4j.properties (one log per day, retains 30 days of logs) * * log4j.rootCategory=INFO, DAILY * log4j.appender.DAILY=org.cnci.util.DailyFileAppender * log4j.appender.DAILY.File=log/MyApplication.log * log4j.appender.DAILY.DatePattern=yyyy-MM-dd log4j.appender.DAILY.MaxLogs=30 * log4j.appender.DAILY.layout=org.apache.log4j.PatternLayout * log4j.appender.DAILY.layout.ConversionPattern=%d %-5p %c @ %m%n * * @author T.A. Nguyen **/ public class DailyFileAppender extends FileAppender { private static final String DEFAULT_DATE_PATTERN = "'.'yyyy-MM-dd"; private static final String[] SUPPORTED_DATE_PATTERNS = { "yyyy" /** (yearly) **/, "yyyy-MM" /** (monthly) **/, "yyyy-MM-dd" /** (daily) **/, "yyyy-ww" /** (weekly) **/, "yyyy-MM-dd-a" /** (midnight, midday) **/, "yyyy-MM-dd-HH" /** (hourly) **/, "yyyy-MM-dd-HH-mm" /** (every minute) **/ }; /** * Used internally and contains the name of the date derived from current * system date. **/ private Date now = new Date(); private String datePattern; private SimpleDateFormat formater; private int maxLogs = 0; private String scheduledFileName; /** * Default constructor. This is required as the appender class is dynamically * loaded. **/ public DailyFileAppender() { super(); } /** * computing for the scheduledFileName and set the now **/ @Override public void activateOptions() { super.activateOptions(); if (datePattern != null && fileName != null) { now.setTime(System.currentTimeMillis()); formater = new SimpleDateFormat(datePattern); File file = new File(fileName); scheduledFileName = fileName + formater.format(new Date(file.lastModified())); } else { LogLog.error("Either File or DatePattern options are not set for appender [" + name + "]."); } } public String getDatePattern() { return this.datePattern; } public void setDatePattern(String datePattern) { // make sure the the pattern conform to what we allow. this.datePattern = checkPattern(datePattern); } public int getMaxLogs() { return maxLogs; } public void setMaxLogs(int maxLogs) { this.maxLogs = maxLogs; } @Override protected void subAppend(LoggingEvent logEvent) { now.setTime(System.currentTimeMillis()); try { rollOver(); } catch (IOException IOEx) { LogLog.error("rollOver() failed!", IOEx); } super.subAppend(logEvent); } @Override public void setFile(String file) { try { // Fix the STUPID file path... Windows will use \ and *NIX will use // /, // so make sure we store the correct file path. File f = new File(file); file = f.getAbsolutePath(); } catch (Exception e) { // ignore! } super.setFile(file); } void rollOver() throws IOException { /** Compute filename, but only if datePattern is specified **/ if (datePattern == null) { // LogLog.error("Missing DatePattern option in rollOver()."); errorHandler.error("Missing DatePattern option in rollOver()."); return; } String datedFilename = fileName + formater.format(now); LogLog.debug(fileName + " ==> " + scheduledFileName + " ==> " + datedFilename); // It is too early to roll over because we are still within the // bounds of the current interval. Rollover will occur once the // next interval is reached. if (scheduledFileName.equals(datedFilename)) { return; } // close current file, and rename it to datedFilename this.closeFile(); File file = new File(fileName); File target = new File(fileName + formater.format(new Date(file.lastModified()))); if (target.exists()) { target.delete(); } boolean result = file.renameTo(target); if (result) { // LogLog.debug(fileName + " -> " + scheduledFileName); scheduledFileName = datedFilename; } else { LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFileName + "]."); } try { // This will also close the file. This is OK since multiple // close operations are safe. this.setFile(fileName, true, this.bufferedIO, this.bufferSize); } catch (IOException e) { // LogLog.error("setFile(" + fileName + ", true) call failed."); errorHandler.error("setFile(" + fileName + ", true) call failed."); } cleanupOldFiles(); } /** * The helper function to validate the DatePattern. * * @param pattern * The DatePattern to be validated. * @return The validated date pattern or default DEFAULT_DATE_PATTERN **/ private String checkPattern(String pattern) { if (isSupportedPattern(pattern)) { try { this.formater = new SimpleDateFormat(pattern); return pattern; } catch (Exception e) { LogLog.error("Invalid DatePattern " + pattern, e); } } return DEFAULT_DATE_PATTERN; } private boolean isSupportedPattern(String pattern) { if (pattern != null) { // give 'em that last chance to conform to our supported pattern. // Convert " " and ":" into "-" so that file operation will work // correctly! pattern = pattern.replaceAll(" |:", "-"); for (String p : SUPPORTED_DATE_PATTERNS) { if (pattern.contains(p)) return true; } } return false; } /** * Now delete those old files if needed. * * @param pstrName * The name of the new folder based on current system date. * @throws IOException **/ static private boolean deletingFiles = false; private static synchronized void setDeletingFiles(boolean isDelete) { deletingFiles = isDelete; } private static synchronized boolean isDeletingFiles() { return deletingFiles; } boolean isExtendedLogFile(String name) { if (name == null) return false; File logMaster = new File(fileName); String master = logMaster.getName(); return name.contains(master); } void cleanupOldFiles() { // If we need to delete log files if (maxLogs > 0 && !isDeletingFiles()) { // tell other thread, that we will do the clean up setDeletingFiles(true); try { // Array to hold the logs we are going to keep File[] logsToKeep = new File[maxLogs]; // Get a 'master' file handle, and the parent directory from it File logMaster = new File(fileName); File logDir = logMaster.getParentFile(); if (logDir.isDirectory()) { File[] logFiles = getLogFiles(logDir); // more files than expected, time to clean up! if (logFiles.length > maxLogs) for (File curLog : logFiles) { removeOldFile(logsToKeep, curLog); } } } catch (Exception e) { // ignore // LogLog.error("While deleting old files. " + e.getMessage(), e); } finally { // we are done with clean up. setDeletingFiles(false); } } } private void removeOldFile(File[] logsToKeep, File curLog) { String name = curLog.getAbsolutePath(); // Check that the file is indeed one we want // (contains the master file name) if (name.indexOf(fileName) >= 0) { // Iterate through the array of logs we are // keeping for (int i = 0; curLog != null && i < logsToKeep.length; i++) { // Have we exhausted the 'to keep' array? if (logsToKeep[i] == null) { // Empty space, retain this log file logsToKeep[i] = curLog; curLog = null; } // If the 'kept' file is older than the // current one else if (logsToKeep[i].getName().compareTo(curLog.getName()) < 0) { // Replace tested entry with current // file File temp = logsToKeep[i]; logsToKeep[i] = curLog; curLog = temp; } } // If we have a 'current' entry at this point, // it's a log we don't want if (curLog != null) { curLog.delete(); } } } private File[] getLogFiles(File logDir) { // Iterate all the files in that directory FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return isExtendedLogFile(name); } }; File[] logFiles = logDir.listFiles(filter); // only get the // extended // files return logFiles; } }
And here is the sample log4j.properties
#------------------------------------------------------------------------------ # Provide AS IS No Warranty! # Author: T.A. Nguyen # # Default log4j.properties file. This should be in the class path # # Possible Log Levels: # FATAL, ERROR, WARN, INFO, DEBUG # # Production Setting: #log4j.rootCategory=ERROR, DAILY # Development and Build Setting: log4j.rootCategory=DEBUG, DAILY, CONSOLE #------------------------------------------------------------------------------ # # The following properties configure the console (stdout) appender. # #------------------------------------------------------------------------------ log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender #log4j.appender.CONSOLE.Target=System.out log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n #------------------------------------------------------------------------------ # # The following properties configure the Daily File appender. # #------------------------------------------------------------------------------ log4j.appender.DAILY = org.cnci.util.DailyFileAppender log4j.appender.DAILY.File = log/MyApplication.log log4j.appender.DAILY.MaxLogs = 30 log4j.appender.DAILY.Append = true log4j.appender.DAILY.DatePattern = '.'yyy-MM-dd log4j.appender.DAILY.layout = org.apache.log4j.PatternLayout log4j.appender.DAILY.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n # Extracting all MyApplication logging into it own file #log4j.logger.org.cnci=DEBUG, DAILY #log4j.additivity.org.cnci=false
Monday, April 9, 2012
Double dereference for Ant Property
Just random note on solving the double dereference that ANT property will not allow.
And the output looks like this:myApp : ${org.cnci.firstapp} myCompany : ${firstCompany} myPackage : ${firstPackage} myClass : ${firstClass} myApp : ${org.cnci.secondapp} myCompany : ${secondCompany} myPackage : ${secondPackage} myClass : ${secondClass}
NOT-WORKING-double-dereferences: [echo] [echo] myApp : SimpleIRC [echo] myCompany : cnci [echo] myPackage : org.cnci.firstapp [echo] myClass : ${org.${firstCompany}.firstapp} [echo] WORKING-double-dereferences: [echo] [echo] myApp : AdvanceIRC [echo] myCompany : cnci [echo] myPackage : org.cnci.secondapp [echo] myClass : AdvanceIRC [echo]
Simple trick to convert Date format with timezone in Java!
I am sure most of you got frustrated from the fact that SimpleDateFormat can not handle ISO8601 format. Here is my little trick to solve this nuisance.
Create a list of Know format you know that you will use for your application and apply SimpleDateFormat to the list. Now, in your formatDate() method, simply try all your known format and trap the Exception, then if still did not have a date, just use javax.xml.bind.DatatypeConverter.parseDateTime(date) to try it and see if that work.
Here is my DateUtils.java
package org.cnci.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; public abstract class DateUtils { // XML formater: this only for converting date into XML string private static final SimpleDateFormat xmlFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); // define all known date formats private static final SimpleDateFormat[] allFormats = new SimpleDateFormat[] { //new SimpleDateFormat("MM/dd/yyyy hh:mm:ss.SSS a"), new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"), // XSD new SimpleDateFormat("MM/dd/yyyy HH:mm:ssZ"), // Oracle new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy"), new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"), // rfc822 new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US), new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"), // XSD new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"), new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS"), new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"), // Oracle new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"), new SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z"), new SimpleDateFormat("yyyy.MM.dd GGG hh:mm aaa"), new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"), new SimpleDateFormat("M/d/yyyy hh:mm:ss"), new SimpleDateFormat("M/d/yyyy hh:mm a"), new SimpleDateFormat("M/d/yy hh:mm:ss"), new SimpleDateFormat("M/d/yy hh:mm a"), new SimpleDateFormat("M/d/yy HH:mm"), new SimpleDateFormat("M/d/yyyy HH:mm"), new SimpleDateFormat("M-d-yy HH:mm"), new SimpleDateFormat("M-d-yyyy HH:mm"), new SimpleDateFormat("M/d/yy"), new SimpleDateFormat("M/d/yyyy"), new SimpleDateFormat("M-d-yy"), new SimpleDateFormat("M-d-yyyy"), new SimpleDateFormat("MMMM d, yyyyy"), new SimpleDateFormat("MMM d, yyyyy") }; //----------------------------------------------------------------------- public static Date parseDate(final String date) throws ParseException { if (isEmptyString(date)) throw new ParseException("Error: date is null or empty!", 0); // iterate over the array and parse Date myDate = null; if (date.endsWith("Z")) { myDate = parseXMLDate(date); } if (myDate == null) { for (DateFormat df : allFormats) { try { myDate = df.parse(date); return myDate; } catch (Exception e) { // do nothing because we want to try the next // format if current one fails } } if (myDate == null) { myDate = parseXMLDate(date); } // nothing returned so couldn't parse if (myDate == null) throw new ParseException("Error: unable to parse date: " + date, 0); } return myDate; } //Use the JAXB data type conversion, since it already know the xml valid date format private static java.util.Date parseXMLDate(final String date) { Calendar cal = javax.xml.bind.DatatypeConverter.parseDateTime(date); return cal.getTime(); } //convert to the string representation of the datetime format //NOTES: this should be the same as formatDate() but keep // and preferred to use this one instead. Just in case the JAXB // document DatetypeConverter change in the future. public static String formatXMLDate(final java.util.Date date) { Calendar cal = Calendar.getInstance(); cal.setTime(date); return javax.xml.bind.DatatypeConverter.printDateTime(cal); } // should yield the same results as formatXMLDate() method above. public static String formatDate(final java.util.Date date) { return xmlFormater.format(date); } public static boolean isEmptyString(String str) { if (str == null) return true; return "".equals(str.trim()); } }
Saturday, December 17, 2011
Tiển Mẹ
Đườ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

