Initialize static map in Java interface

Sometimes, we need define a map constant in interface, but as interface can't define initializer or static initializer block, how are we going to put values into the map and initialize it? 

One way is to define an anonymous class that inherits from Hashmap, and initialize the map in the initialize block in the new anonymous class.

interface IAmInterface
{
    public static final Map MAP = new HashMap()
    {
        private static final long serialVersionUID = 1L;
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

Use Custom logging Formatter/Handler in Eclipse RCP

In my latest Eclipse RCP project, we use java.util.logging and want to add a custom Formatter in logging properties.

It works on IBM JDK 6.0, but doesn't work on Sun JDK, or any IBM previous JDK releases.Compared the source code of LogManager and Handler classes in Sun JDK or IBM old JDK releases, I figured out that this is because in IBM JDK 6.0, when to new a Formatter (Hanlder etc) instance, it use context ClassLoader (the ClassLoader of the method that called the caller) to load Formatter class:
Thread.currentThread().getContextClassLoader() But in old releases, it just use system classloader to load formatter class: ClassLoader.getSystemClassLoader().loadClass(val)
Of course, it can't find our custom Formatter class in system classloader, so it throws ClassNotFoundException.
The simple (not elegant way) is that when the application starts, configure jdk logging in AbstractUIPlugin subclass:

public class Activator extends AbstractUIPlugin  
{  
  public static final String PLUGIN_ID = "org.codeexample.myrcp"; //$NON-NLS-1$  
  public Activator(){}  
  public void start(BundleContext context) throws Exception  
  {  
   LogConfigurer.configureLogging();  
   final Logger logger = Logger.getLogger(Activator.class.getName());  
   // test logger  
   logger.warning("hello logger: " + new Date());  
   logger.throwing(Activator.class.getName(), "start", new RuntimeException("thrown exception"));  
   super.start(context);  
  }  
  public void stop(BundleContext context) throws Exception  
  {  
   super.stop(context);  
  }  
 }  
public class LogConfigurer { 
     public static String DATE_FOMAT = "yyyy-MM-dd hh:mm:ss.SSS"; 
     public static String LOG_DIRECTORY = Platform.getLocation() 
               .toPortableString() + IConstants.FILE_SEPARATOR + "logs"; 
     public static String LOG_PREFIX = "main"; 
     public static boolean configured = false; 
     public synchronized static void configureLogging() { 
          if (!configured) { 
               configured = true; 
               try { 
                    LogManager.getLogManager().readConfiguration( 
                              LogConfigurer.class 
                                        .getResourceAsStream("logging.properties")); 
                    File file = new File(LOG_DIRECTORY); 
                    if (!file.exists()) { 
                         file.mkdir(); 
                    } 
                    FileHandler fileHandler = new FileHandler(LOG_DIRECTORY 
                              + IConstants.FILE_SEPARATOR + LOG_PREFIX + ".log", true); 
                    fileHandler.setFormatter(new MySimpleFormatter()); 
                    fileHandler.setEncoding("UTF-8"); 
                    fileHandler.setLevel(Level.FINER); 
                    ConsoleHandler consoleHandler = new ConsoleHandler(); 
                    consoleHandler.setFormatter(new MySimpleFormatter()); 
                    consoleHandler.setEncoding("UTF-8"); 
                    consoleHandler.setLevel(Level.ALL); 
                    Logger parentLogger = Logger 
                              .getLogger("com.ibm.storage.ess.cli.wrapper"); 
                    parentLogger.addHandler(fileHandler); 
                    parentLogger.addHandler(consoleHandler); 
                    parentLogger.setLevel(Level.ALL); 
               } catch (Exception e) { 
                    Activator.logException(e); 
               } 
          } 
     } 
} 
/** 
 * The code is similar as JDK SimpleFormatter, just made a little change to 
 * record all message(including exception stack) in one line. 
 */ 
public class MySimpleFormatter extends Formatter { 
     Date dat = new Date(); 
     private final static String format = "{0,date," + LogConfigurer.DATE_FOMAT 
               + "}"; 
     private MessageFormat formatter; 
     private Object args[] = new Object[1]; 
     public static final String LINE_SEPARATOR = System 
               .getProperty("line.separator"); 
     private String separator = " ------ "; 
     public synchronized String format(LogRecord record) { 
          StringBuffer sb = new StringBuffer(); 
          // Minimize memory allocations here. 
          dat.setTime(record.getMillis()); 
          args[0] = dat; 
          StringBuffer text = new StringBuffer(); 
          if (formatter == null) { 
               formatter = new MessageFormat(format); 
          } 
          formatter.format(args, text, null); 
          sb.append(text); 
          sb.append(" "); 
          if (record.getSourceClassName() != null) { 
               sb.append(record.getSourceClassName()); 
          } else { 
               sb.append(record.getLoggerName()); 
          } 
          if (record.getSourceMethodName() != null) { 
               sb.append(" "); 
               sb.append(record.getSourceMethodName()); 
          } 
          sb.append(separator); 
          String message = formatMessage(record); 
          sb.append(record.getLevel().getLocalizedName()); 
          sb.append(": "); 
          sb.append(message); 
          if (record.getThrown() != null) { 
               sb.append(separator); 
               try { 
                    StringWriter sw = new StringWriter(); 
                    PrintWriter pw = new PrintWriter(sw); 
                    record.getThrown().printStackTrace(pw); 
                    pw.close(); 
                    String str = sw.toString(); 
                    String[] strs = str.split(IConstants.LINE_SEPARATOR); 
                    for (int i = 0; i < strs.length; i++) { 
                         sb.append(strs[i]); 
                         if (i != strs.length - 1) { 
                              sb.append(separator); 
                         } 
                    } 
               } catch (Exception ex) { 
               } 
          } 
          sb.append("\n"); 
          return sb.toString(); 
     } 
} 

java.util.logging Usage

The key elements in java.util.logging are Logger, Handler, Filter, Formatter.

Logger
The main entity on which applications make logging calls.
Each logger keeps track of a log level that it is interested in, and discards log requests that are below this level.

Loggers keep track of their parent loggers in the logging namespace. A logger's parent is its nearest extant ancestor in the logging namespace. The root Logger (named "") has no parent. Anonymous loggers are all given the root logger as their parent. Loggers may inherit various attributes from their parents in the logger namespace. In particular, a logger may inherit: Logging level, Handlers, Resource bundle names.

Handler
Exports LogRecord objects to a variety of destinations. J2SE provides the following Handlers:
StreamHandler, FileHandler, SocketHandler
ConsoleHandler: A simple handler for writing formatted records to System.err
MemoryHandler: A handler that buffers log records in memory.

Additional Handlers may be developed and attached to a logger.

Filter
Provides fine-grained control over what gets logged, beyond the control provided by log levels.

Formatter
Provides support for formatting LogRecord objects. J2SE already includes two standard Formatters: SimpleFormatter and XMLFormatter


Configuration Options

java.util.logging.FileHandler.[level|filter|formatter|encoding|limit|count|pattern|append]
special components in pattern:
"%t" the system temporary directory
"%h" the value of the "user.home" system property
"%g" the generation number to distinguish rotated logs
"%u" a unique number to resolve conflicts

Configuration Example
# Specify the handlers to create in the root logger
handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler
# Set the default logging level for the root logger
.level = ALL

java.util.logging.ConsoleHandler.level = FINER
# java.util.logging.ConsoleHandler.filter (defaults to no Filter).
java.util.logging.ConsoleHandler.formatter =java.util.logging.SimpleFormatter
java.util.logging.ConsoleHandler.encoding = UTF-8

java.util.logging.FileHandler.level = INFO
java.util.logging.FileHandler.formatter = org.codeexample.logging.MyFormatter
java.util.logging.FileHandler.encoding = UTF-8
#1 Mb
java.util.logging.FileHandler.limit = 1000000
java.util.logging.FileHandler.count = 3

org.codeexample.level = INFO
org.codeexample.handler = org.codeexample.logging.MyHandler

# config option can be used to specify a class whose constructor is responsible for setting the initial configuration.
# config = org.codeexample.logging.LogConfiguer

Preventing a Logger from Forwarding Log Records to Its Parent
# Stop/Start forwarding log records to ancestor handlers
logger.setUseParentHandlers(false|true);

Customization - see the documentation of LogManager class
Custom logging properties file can be loaded by
System property "java.util.logging.config.file" can be specified tp load custom logging properties file.
System property "java.util.logging.config.class" can be used to specify a class whose constructor is responsible for setting the initial configuration.
"java.util.logging.manager" can be used to sepcify your LogManager implementation.

Logging Example:
public class LoggingConfig  
 {  
   /**  
    * This example demonstrate how to add a handler and filter  
    */  
   public void setFilter()  
 {  
   try  
     {  
       FileHandler handler = new FileHandler("");  
       // Set the filter  
       handler.setFilter(new Filter()  
       {  
         public boolean isLoggable(LogRecord record)  
         {  
           // return true if the record should be logged, false otherwise.  
           // for example, only log if the method name starts with audit.  
           return (record.getSourceMethodName().startsWith("audit"));  
         }  
       });  
       // Add the handler to a logger  
       Logger logger = Logger.getLogger(LoggingConfig.class.getName());  
       logger.addHandler(handler);  
     }  
     catch (IOException e)  
     {  
       e.printStackTrace();  
     }  
   }  
   public void useMemeoryHandler()  
   {  
     // Create a memory handler with a memory of 100 records  
     // and dumps the records into the file my.log when a  
     // SEVERE message is logged  
     FileHandler fhandler;  
     try  
     {  
       fhandler = new FileHandler("my.log", true); // set append mode to true  
       int numRec = 100;  
       MemoryHandler mhandler = new MemoryHandler(fhandler, numRec, Level.SEVERE);  
       // Add to the desired logger  
       Logger logger = Logger.getLogger("com.mycompany");  
       logger.addHandler(mhandler);  
     }  
     catch (IOException e)  
     {  
       e.printStackTrace();  
     }  
   }  
   public static void main(String[] args)  
   {  
     Logger logger = Logger.getLogger(LoggingConfig.class.getName());  
     logger.setLevel(Level.SEVERE);  
   }  
 }  
Resources:

Working with Java Date and Calendar API


Working with Java Date, Calendar API

The following code tries to demonstrate main usage of Java Date, and Calendar API.

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DateCalendarTest {
    public static void testSimpleDateFormat() throws ParseException {
       // If no timezone is set, it would use the default timezone.
       DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm");
       // String to Date
       Date date = df.parse("2011/04/16 26:65");
       df.setLenient(true); // allow string like 26:65 etc, default is true
       // Date to String
       String sre = df.format(new Date());
       // this would use the default format of the user's computer
       df = DateFormat.getDateInstance();
       // It also provides some alternative styles for dates.
       df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
       df = DateFormat.getDateInstance(DateFormat.LONG);
       df = DateFormat.getTimeInstance();
    }
    public static void testCalendar() {
       // this would create a calendar with current time and time zone.
       Calendar calendar = Calendar.getInstance();
       Date date = calendar.getTime();
       // Be aware that months start with January equal to 0, month of the
       // following time is May.
       calendar = new GregorianCalendar(2011, 04, 16);
       // A better way to create that date would be:
       calendar = new GregorianCalendar(2011, Calendar.APRIL, 16, 23, 31, 00);
       calendar.add(GregorianCalendar.DATE, 90);
       calendar.add(GregorianCalendar.MONTH, 1);
       // calculate difference between times
       System.err.println(calendar.getTime());
    }
    public static void compareDate(Calendar calendar1, Calendar calendar2) {
       long days, hours, minutes, seconds;
       long diff = calendar1.getTimeInMillis() - calendar2.getTimeInMillis();
       long millisADay = 24 * 60 * 60 * 1000;
       days = diff / millisADay;
       diff = diff - (days * millisADay);
       hours = diff / 3600;
       diff = diff - (hours * 3600);
       minutes = diff / 60;
       diff = diff - (minutes * 60);
       seconds = diff;
       System.out.println(days + " days(s) " + hours + " hour(s) " + minutes
               + " minute(s) " + seconds + " second(s)");
    }
    public static void testTimeZone() {
       // displays the ID of the current default time zone
       System.out.println(TimeZone.getDefault().getID()); // Asia/Shanghai
       // You can change system property - user.timezone to alter the default time zone.
    }
    public static void testSqlTime() {
       // convert a java.sql.timestamp to a java.util.Date
       java.sql.Timestamp t = new java.sql.Timestamp(new Date().getTime());
       // 1 Milli = 1x10^6 Nanos
       Date d = new Date(t.getTime() + (t.getNanos() / 1000000));
       DateFormat df = new SimpleDateFormat("MM/dd/yy hh:mm:ss.SSS a");
       System.out.println(df.format(d)); // Right! At least we have the millis
    }
    /**
     * This is a program to demonstrate how to group the days between startDate
     * and endDate by week
     */
    public static void runWeeklyReport(Date startDate, Date endDate) {    Calendar startCalendar = Calendar.getInstance();
       startCalendar.setTime(startDate);
       Calendar endCalendar = Calendar.getInstance();
       endCalendar.setTime(endDate);
       while (startCalendar.before(endCalendar)) {
           Date weekStartDate = startCalendar.getTime();
           int dayOfWeek = startCalendar.get(Calendar.DAY_OF_WEEK);
           if (dayOfWeek != Calendar.SUNDAY) {
              startCalendar.add(Calendar.DAY_OF_WEEK, 7 - dayOfWeek + 1);
              startCalendar.set(Calendar.HOUR, 23);
              startCalendar.set(Calendar.MINUTE, 59);
              startCalendar.set(Calendar.SECOND, 59);
           }
           if (startCalendar.before(endCalendar)) {
              Date weekEndDate = startCalendar.getTime();
              subrutine(weekStartDate, weekEndDate);
           } else {
              break;
           }
           // Go to next week
           startCalendar.add(Calendar.SECOND, 1);
       }
    }
private static void subrutine(Date weekStartDate, Date weekEndDate) {
       System.err.println(weekStartDate + ":" + weekEndDate);
    }
}
Caveat
1. Months start with January equal to 0:
new GregorianCalendar(2011, 04, 16); the month would be set to May, not April, better to use the defined constants: Calendar.JANUARY.
2. The first day of week is SUNDAY, Calendar.SUNDAY = 1.

java.sql.Date, java.sql.Time, java.sql.Timestamp are subclasses of java.util.Date.

So don't use instance variables or static instance variables to store a SimpleDateFormat - in this way, you have to synchronize its access.
Instead, the simple and safe way is to create a new SimpleDateFormat object each time - modern jvms don't have any problem allocating short lived objects.

Validation
By default SimpleDateFormat is lenient, it is tolerant to some formatting errors. It will try to figure out the date by applying some heuristics to the given text.
So the following date would be interpreted as 2011/06/10 03:05:00.
Date date = df.parse("2011/04/100 26:65:00");

If you want to strictly validate user input, set the SimpleDateFormat non-lenient by: sdf.setLenient(false);

Many methods in java.util.Date are deprecated, but it doesn't mean java.util.Date is dead. In stead, we should use java.util.Date, Calendar together, use Date objects to hold, store or communicate a date-time value. Use a Calendar object to manipulate a date-time value. Of course don't use the deprecated methods of java.util.Date.

What then is the role of java.util.Date?
A java.util.Date is a value object, and is a variation of the Memento or Token pattern.
It simply holds state - a long integer value of milliseconds since 1970-01-01 00:00:00.00 GMT. This is a very compact way to represent a date and time value, thus fits the idea of the Memento pattern very well.

You should use a java.util.Date as a value object to hold timestamps when you need to move such values between different parts of your software.

What then is the role of Calendar?
GregorianCalendar is many tens of times larger than the much simplier java.util.Date as it holds many additional information as part of its extensive internal state.

So don't use Calendar as a data transfer object.
Calendar is a variation of a strategy pattern, There are various implementations of calendar available including GregorianCalendar , JapaneseImperialCalendar. ChineseCalendar, HebrewCalendar are available from the IBM ICU4J Project.

Resources

Explicitly Managing Maven Dependencies

If we add a library in pom.xml, Maven would automatically include its dependencies. This is cool. But if two libraries depend on a same library, except use different version, problems may happen.

The best way is to declare the library version explicitly.

Here is an example:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
      <modelVersion>4.0.0</modelVersion>  
      <groupId>com.codeexample</groupId>  
      <artifactId>mvn-example-exclusion</artifactId>  
      <version>1.0-SNAPSHOT</version>  
      <packaging>jar</packaging>  
      <name>mvn-example-exclusion</name>  
      <properties>  
           <slf4j.version>1.6.1</slf4j.version>  
      </properties>  
      <dependencies>  
           <dependency>  
                <groupId>log4j</groupId>  
                <artifactId>log4j</artifactId>  
                <version>1.2.16</version>  
           </dependency>  
           <dependency>  
                <groupId>org.slf4j</groupId>  
                <artifactId>slf4j-log4j12</artifactId>  
                <version>${slf4j.version}</version>  
           </dependency>  
           <dependency>  
                <groupId>net.sf.dozer</groupId>  
                <artifactId>dozer</artifactId>  
                <version>5.3.2</version>  
           </dependency>  
      </dependencies>  
 </project>  
public class App {
    public static void main(String[] args) {
       org.slf4j.LoggerFactory.getLogger(App.class).debug("Hello slf4j");
    }
}

When run the previous code which just use slf4j logger to print a debug message, it would report the following error:
SLF4J: The requested version 1.6 by your slf4j binding is not compatible with [1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10]

This means we use incorrect version of slf4j-api library. But why and who imports the incorrect slf4j-api jar?

To find the culprit, I run “mvn eclipse:eclipse –X”.
[DEBUG] com.codeexample:mvn-example-exclusion:jar:1.0-SNAPSHOT (selected for null)
[DEBUG]   log4j:log4j:jar:1.2.16:compile (selected for compile)
[DEBUG]   net.sf.dozer:dozer:jar:5.3.2:compile (selected for compile)
[DEBUG]     commons-beanutils:commons-beanutils:jar:1.8.3:compile (selected for compile)
[DEBUG]       commons-logging:commons-logging:jar:1.1.1:compile (selected for compile)
[DEBUG]     commons-lang:commons-lang:jar:2.5:compile (selected for compile)
[DEBUG]     org.slf4j:slf4j-api:jar:1.5.10:compile (selected for compile)
[DEBUG]   org.slf4j:slf4j-log4j12:jar:1.6.1:compile (selected for compile)
[DEBUG]     org.slf4j:slf4j-api:jar:1.6.1:compile (removed - nearer found: 1.5.10)

From the output, we can see slf4j-log4j12-1.6.1 and net.sf.dozer-5.3.2 both depend on slf4j-api, but slf4j-log4j12-1.6.1 needs slf4j-api-1.6.1, net.sf.dozer-5.3.2 depends on slf4j-api-1.5.10.

For somewhat reason, - maybe net.sf.dozer-5.3.2 declares slf4j-api version explicitly-, maven uses slf4j-api-1.5.10.
To fix, we can explicitly declare the version of slf4j-api.
 <dependency>  
      <groupId>org.slf4j</groupId>  
      <artifactId>slf4j-api</artifactId>  
      <version>${slf4j.version}</version>  
 </dependency>  

Or we can exclude slf4j-api dependency from net.sf.dozer.
 <dependency>  
      <groupId>net.sf.dozer</groupId>  
      <artifactId>dozer</artifactId>  
      <version>5.3.2</version>  
      <exclusions>  
           <exclusion>  
                <groupId>org.slf4j</groupId>  
                <artifactId>slf4j-api</artifactId>  
           </exclusion>  
      </exclusions>  
 </dependency>  

I prefer the former solution, as it is clearer and simpler to understand.

Testing Java RESTful Web Service

Developing and Testing Java RESTful Web Service - Part 2

There are 2 ways to run our web service, run it in an embedded Jetty server or use CXF JAXRSServerFactoryBean, the former one is preferred as it can do much more such as load default data before test, and clean data after test.
1.     Start an embedded Jetty server
public class JettyUtils {
      public static final int PORT = 8080;
      public static final String CONTEXT = "/rs-example";
      public static final String BASE_URL = "http://localhost:" + PORT + "/" + CONTEXT;
      public static Server buildTestServer(int port, String contextPath) {
           Server server = new Server(port);
           WebAppContext webContext = new WebAppContext("src/main/webapp/",
                   contextPath);
           webContext.setClassLoader(Thread.currentThread()
                   .getContextClassLoader());
           server.setHandler(webContext);
           server.setStopAtShutdown(true);
           // we also copy web.xml to src/test/resources
           ((WebAppContext) server.getHandler())
                   .setDescriptor("src/test/resources/web.xml");
           return server;
      }
      public static void main(String[] args) throws Exception {
           Server server = buildTestServer(PORT, CONTEXT);
           server.start();
           System.out.println("Hit Enter in console to stop server");
           if (System.in.read() != 0) {
                 server.stop();
                 System.out.println("Server stopped");
           }
      }
}
During development, we can run this program to start Jetty server, and use Firefox Plugin poster to test our web service manually.
After install the Firefox plugin, go to Tools->Poster (or click ctrl+alt+P) to open the poster window. In the window, you can send all kinds (Get, Post, Delete and etc) of requests.

2.     Run Functional Test in the embedded Jetty server
package org.codeexample.functional.rs;
@Ignore public class BaseFunctionalTestCase {
    protected static final String BASE_URL = JettyUtils.BASE_URL;
    private static Server server;
    @BeforeClass public static void start() throws Exception {
       server = JettyUtils
               .buildTestServer(JettyUtils.PORT, JettyUtils.CONTEXT);
       server.start();
    }
    @AfterClass public static void stop() throws Exception {
       server.stop();
    }
}
Other tasks can be done in the start and stop methods.
package org.codeexample.functional.rs;
public class FriendWebServiceClientTest extends BaseFunctionalTestCase {
    private static FriendWebServiceClient friendWebServiceClient;
    @BeforeClass public static void setUpClient() throws Exception {
       friendWebServiceClient = new FriendWebServiceCxfClient();
       friendWebServiceClient.setBaseUrl(BASE_URL);
    }
    @Test public void addFirend() {
       PersonDTO Steve = new PersonDTO("Steve", "Baker",
               "fakeSteve@gmail.com", Person.GENDER_MALE);
       Long friendId = friendWebServiceClient.addFriend(1L, Steve);
       System.out.println(friendId);
       // Remove the added friend
       friendWebServiceClient.removeFriend(1L, friendId);
    }
    @Test public void getFriends() {
       List persons = friendWebServiceClient.getFriends(1L);
       Assert.assertEquals(2, persons.size());
       System.err.println(persons);
    }
    @Test public void getFriend() {
       PersonDTO friend = friendWebServiceClient.getFriend(1L, 2L);
       Assert.assertNotNull(friend);
       Assert.assertEquals("Paul", friend.getFirstName());
       Assert.assertEquals("Graham", friend.getFamilyName());
       System.out.println(friend);
    }
}

3.     Develop Web Service Client
There can be many ways to access RESTful web service: use 1. Apache CXF WebClient, 2. Apache HttpClient, 3. HttpURLConnection, Apache CXF WebClient is the simplest way, Apache HttpClient and HttpURLConnection can be used in other situations, such as in old JDK or J2SE environment.
package org.codeexample.rs.client;
public interface FriendWebServiceClient {
    public abstract void setBaseUrl(String baseUrl);
    public abstract List getFriends(Long userId);
    public abstract PersonDTO getFriend(Long userId, Long friendId);
    public abstract Long addFriend(Long userId, PersonDTO friendDTO);
    public abstract void removeFriend(Long userId, Long friendId);
}
We can choose one that fits our requirements best.
3.1             Use Apache CXF WebClient to Access Web Service
WebClient uses the HTTP-centric approach to communicate with the RESFTful service.
package org.codeexample.rs.client;
public class FriendWebServiceCxfClient implements FriendWebServiceClient {
    private String baseUrl;
    @Required public void setBaseUrl(String baseUrl)
{this.baseUrl = baseUrl;}
    public List getFriends(Long userId) {
       List personDTO = new ArrayList();
       Collection collection = (Collection) WebClient
               .create(baseUrl).path("friends/" + userId)
               .accept(MediaType.APPLICATION_XML)
               .getCollection(PersonDTO.class);
       personDTO.addAll(collection);
       return personDTO;
    }
    public PersonDTO getFriend(Long userId, Long friendId) {
       PersonDTO personDTO = WebClient.create(baseUrl)
               .path("friends/" + userId + "/" + friendId)
            .accept(MediaType.APPLICATION_XML).get(PersonDTO.class);
       return personDTO;
    }
    public Long addFriend(Long userId, PersonDTO friendDTO) {
       Long firendId = WebClient.create(baseUrl).path("friends/" + userId)
               .accept(MediaType.APPLICATION_XML).post(friendDTO, Long.class);
       return firendId;
    }
    public void removeFriend(Long userId, Long friendId) {
       WebClient.create(baseUrl).path("friends/" + userId + "/" + friendId)
               .accept(MediaType.APPLICATION_XML).delete();
    }
}



3.2             Use Apache Http Client to access Web Service
 package org.codeexample.rs.client;  
 public class FriendWebServiceApacheHttpClient implements FriendWebServiceClient {  
      private String baseUrl;  
      @Required public void setBaseUrl(String baseUrl)   
 { this.baseUrl = baseUrl;     }  
      public List<PersonDTO> getFriends(Long userId) {  
           DefaultHttpClient httpClient = new DefaultHttpClient();  
           List<PersonDTO> friends = null;  
           HttpGet httpGet = new HttpGet(baseUrl + "/friends/" + userId);  
           httpGet.addHeader("accept", "application/xml");  
           ResponseHandler<List<PersonDTO>> responseHandler = new ResponseHandler<List<PersonDTO>>() {  
                public List<PersonDTO> handleResponse(HttpResponse response)  
                    throws ClientProtocolException, IOException {  
                     HttpEntity entity = response.getEntity();  
                     if (entity != null) {  
                          JAXBContext context;  
                          try {  
                               context = JAXBContext.newInstance(PersonDTO.class);  
                               String str = EntityUtils.toString(entity);  
                               StringReader sr = new StringReader(str);  
                               // TODO this would cause UnmarshalException, fix it.  
                               // javax.xml.bind.UnmarshalException: unexpected element  
                               // (uri:"com.codeexample", local:"persons"). Expected  
                               // elements are <{com.codeexample}person>  
                               return (List<PersonDTO>) context.createUnmarshaller()  
                                   .unmarshal(sr);  
                          } catch (JAXBException e) {  
                               e.printStackTrace();  
                               return null;  
                          }  
                     } else {  
                          return new ArrayList<PersonDTO>();  
                     }  
                }  
           };  
           try {  
                friends = httpClient.execute(httpGet, responseHandler);  
           } catch (ClientProtocolException e) {  
                e.printStackTrace();  
           } catch (IOException e) {  
                e.printStackTrace();  
           } finally {  
                httpClient.getConnectionManager().shutdown();  
           }  
           return friends;  
      }  
      public PersonDTO getFriend(Long userId, Long friendId) {  
           DefaultHttpClient httpClient = new DefaultHttpClient();  
           PersonDTO friend = null;  
           HttpGet httpGet = new HttpGet(baseUrl + "/friends/" + userId + "/"  
               + friendId);  
           httpGet.addHeader("accept", "application/xml");  
           ResponseHandler<PersonDTO> responseHandler = new ResponseHandler<PersonDTO>() {  
                public PersonDTO handleResponse(HttpResponse response)  
                    throws ClientProtocolException, IOException {  
                     HttpEntity entity = response.getEntity();  
                     if (entity != null) {  
                          JAXBContext context;  
                          try {  
                               context = JAXBContext.newInstance(PersonDTO.class);  
                               String str = EntityUtils.toString(entity);  
                               StringReader sr = new StringReader(str);  
                               return (PersonDTO) context.createUnmarshaller()  
                                   .unmarshal(sr);  
                          } catch (JAXBException e) {  
                               e.printStackTrace();  
                               return null;  
                          }  
                     } else {  
                          return null;  
                     }  
                }  
           };  
           try {  
                friend = httpClient.execute(httpGet, responseHandler);  
           } catch (ClientProtocolException e) {  
                e.printStackTrace();  
           } catch (IOException e) {  
                e.printStackTrace();  
           } finally {  
                httpClient.getConnectionManager().shutdown();  
           }  
           return friend;  
      }  
      public Long addFriend(Long userId, PersonDTO friendDTO) {  
           Long friendId = null;  
           DefaultHttpClient httpClient = new DefaultHttpClient();  
           HttpPost httpPost = new HttpPost(baseUrl + "/friends/" + userId);  
           httpPost.addHeader("accept", "application/xml");  
           try {  
                StringWriter sw = new StringWriter();  
                JAXBContext context = JAXBContext.newInstance(PersonDTO.class);  
                context.createMarshaller().marshal(friendDTO, sw);  
                System.err.println(sw.toString());  
                StringEntity entity = new StringEntity(sw.toString());  
                entity.setContentType("application/xml");  
                httpPost.setEntity(entity);  
                ResponseHandler responseHandler = new BasicResponseHandler();  
                friendId = (Long) httpClient.execute(httpPost, responseHandler);  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
           return friendId;  
      }  
      public void removeFriend(Long userId, Long friendId) {  
           DefaultHttpClient httpClient = new DefaultHttpClient();  
           HttpDelete httpPost = new HttpDelete(baseUrl + "/friends/" + userId  
               + "/" + friendId);  
           httpPost.addHeader("accept", "application/xml");  
           try {  
                httpClient.execute(httpPost);  
           } catch (Exception e) {  
                e.printStackTrace();  
           }  
      }  
 }  
3.3             Use Apache Http Client to access Web Service
 package org.codeexample.rs.client;  
 public class FriendWebServiceHttpClient implements FriendWebServiceClient {  
      private String baseUrl;  
      @Required public void setBaseUrl(String baseUrl) {  
           this.baseUrl = baseUrl;  
      }  
      @Override public List<PersonDTO> getFriends(Long userId) {  
           List<PersonDTO> friends = null;  
           try {  
                URL url = new URL(baseUrl + "/friends/" + userId);  
                HttpURLConnection connection = (HttpURLConnection) url  
                    .openConnection();  
                connection.setRequestMethod("GET");  
                connection.setRequestProperty("Accept", "application/xml");  
                if (connection.getResponseCode() != 200) { throw new RuntimeException(  
                    "Operation failed:” + connection.getResponseCode()); }  
                JAXBContext context = JAXBContext.newInstance(PersonDTO.class);  
                friends = (List<PersonDTO>) context.createUnmarshaller().unmarshal(  
                    connection.getInputStream());  
           } catch (MalformedURLException e) {  
                e.printStackTrace();  
           } catch (IOException e) {  
                e.printStackTrace();  
           } catch (JAXBException e) {  
                e.printStackTrace();  
           }  
           return friends;  
      }  
      @Override public PersonDTO getFriend(Long userId, Long friendId) {  
           PersonDTO friend = null;  
           try {  
                URL url = new URL(baseUrl + "/friends/" + userId + "/" + friendId);  
                HttpURLConnection connection = (HttpURLConnection) url  
                    .openConnection();  
                connection.setRequestMethod("GET");  
                connection.setRequestProperty("Accept", "application/xml");  
                if (connection.getResponseCode() != 200) { throw new RuntimeException(  
                    "Operation failed:” + connection.getResponseCode()); }  
                JAXBContext context = JAXBContext.newInstance(PersonDTO.class);  
                friend = (PersonDTO) context.createUnmarshaller().unmarshal(  
                    connection.getInputStream());  
           } catch (MalformedURLException e) {  
                e.printStackTrace();  
           } catch (IOException e) {  
                e.printStackTrace();  
           } catch (JAXBException e) {  
                e.printStackTrace();  
           }  
           return friend;  
      }  
      @Override public Long addFriend(Long userId, PersonDTO friendDTO) {  
           Long friendId = null;  
           HttpURLConnection connection = null;  
           try {  
                URL url = new URL(baseUrl + "/friends/" + userId);  
                connection = (HttpURLConnection) url.openConnection();  
                connection.setDoOutput(true);  
                connection.setInstanceFollowRedirects(false);  
                connection.setRequestMethod("POST");  
                connection.setRequestProperty("Content-Type", "application/xml");  
                connection.setRequestProperty("accept", "application/xml");  
                StringWriter sw = new StringWriter();  
                JAXBContext context = JAXBContext.newInstance(PersonDTO.class);  
                context.createMarshaller().marshal(friendDTO, sw);  
                System.err.println(sw.toString());  
                OutputStream os = connection.getOutputStream();  
                os.write(sw.toString().getBytes());  
                os.flush();  
                BufferedReader reader = new BufferedReader(new InputStreamReader(  
                    connection.getInputStream()));  
                friendId = new Long(reader.readLine());  
                // this would cause org.xml.sax.SAXParseException: Content is not  
                // allowed in prolog.]  
                // context = JAXBContext.newInstance(PersonDTO.class);  
                // friendId = (Long) context.createUnmarshaller().unmarshal(  
                // connection.getInputStream());  
           } catch (IOException e) {  
                e.printStackTrace();  
           } catch (JAXBException e) {  
                e.printStackTrace();  
           } finally {  
                connection.disconnect();  
           }  
           return friendId;  
      }  
      @Override public void removeFriend(Long userId, Long friendId) {  
           try {  
                URL url = new URL(baseUrl + "/friends/" + userId + "/" + friendId);  
                HttpURLConnection connection = (HttpURLConnection) url  
                    .openConnection();  
                connection.setRequestMethod("DELETE");  
                System.err.println(connection.getResponseCode());  
           } catch (MalformedURLException e) {  
                e.printStackTrace();  
           } catch (IOException e) {  
                e.printStackTrace();  
           }  
      }  
 }  


To test different web service client, just replace FriendWebServiceClient with corresponding client implementation class.

Labels

Java (159) Lucene-Solr (110) Interview (61) All (58) J2SE (53) Algorithm (45) Soft Skills (37) Eclipse (33) Code Example (31) Linux (24) JavaScript (23) Spring (22) Windows (22) Web Development (20) Nutch2 (18) Tools (18) Bugs (17) Debug (16) Defects (14) Text Mining (14) J2EE (13) Network (13) Troubleshooting (13) PowerShell (11) Chrome (9) Design (9) How to (9) Learning code (9) Performance (9) Problem Solving (9) UIMA (9) html (9) Http Client (8) Maven (8) Security (8) bat (8) blogger (8) Big Data (7) Continuous Integration (7) Google (7) Guava (7) JSON (7) ANT (6) Coding Skills (6) Database (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) Dynamic Languages (5) IDE (5) Lesson Learned (5) Programmer Skills (5) System Design (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) Miscs (4) OpenNLP (4) Project Managment (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Bit Operation (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Firefox (2) Google Drive (2) Gson (2) How to Interview (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Python (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts